From dea636af4d1902a081ee891f1b19ee2f8729d759 Mon Sep 17 00:00:00 2001 From: Pavel Březina Date: Tue, 29 Mar 2016 12:38:25 +0200 Subject: DP: Switch to new interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Sumit Bose Reviewed-by: Jakub Hrozek Reviewed-by: Lukáš Slebodník --- src/providers/ad/ad_access.c | 121 +- src/providers/ad/ad_access.h | 12 +- src/providers/ad/ad_autofs.c | 10 +- src/providers/ad/ad_common.h | 18 +- src/providers/ad/ad_id.c | 186 +- src/providers/ad/ad_id.h | 14 +- src/providers/ad/ad_init.c | 698 +++--- src/providers/ad/ad_subdomains.c | 1426 ++++++----- src/providers/ad/ad_subdomains.h | 9 +- src/providers/ad/ad_sudo.c | 14 +- src/providers/backend.h | 126 - src/providers/data_provider/dp_custom_data.h | 30 + src/providers/data_provider/dp_iface.c | 12 +- src/providers/data_provider/dp_iface.h | 28 + src/providers/data_provider/dp_target_auth.c | 302 +++ src/providers/data_provider/dp_target_autofs.c | 55 + src/providers/data_provider/dp_target_hostid.c | 63 + src/providers/data_provider/dp_target_id.c | 311 +++ src/providers/data_provider/dp_target_subdomains.c | 50 + src/providers/data_provider/dp_target_sudo.c | 199 ++ src/providers/data_provider_be.c | 2522 ++------------------ src/providers/data_provider_req.c | 29 +- src/providers/data_provider_req.h | 3 - src/providers/ipa/ipa_access.c | 951 ++++---- src/providers/ipa/ipa_access.h | 34 +- src/providers/ipa/ipa_auth.c | 339 ++- src/providers/ipa/ipa_auth.h | 14 +- src/providers/ipa/ipa_autofs.c | 23 +- src/providers/ipa/ipa_common.h | 14 +- src/providers/ipa/ipa_hbac_common.c | 12 +- src/providers/ipa/ipa_hostid.c | 177 +- src/providers/ipa/ipa_hostid.h | 11 +- src/providers/ipa/ipa_id.c | 273 +-- src/providers/ipa/ipa_id.h | 14 +- src/providers/ipa/ipa_init.c | 1032 ++++---- src/providers/ipa/ipa_selinux.c | 742 +++--- src/providers/ipa/ipa_selinux.h | 11 +- src/providers/ipa/ipa_subdomains.c | 1609 +++++++------ src/providers/ipa/ipa_subdomains.h | 10 +- src/providers/ipa/ipa_subdomains_ext_groups.c | 22 +- src/providers/ipa/ipa_subdomains_id.c | 19 +- src/providers/ipa/ipa_subdomains_server.c | 2 +- src/providers/ipa/ipa_sudo.c | 220 +- src/providers/krb5/krb5_auth.c | 172 +- src/providers/krb5/krb5_auth.h | 12 +- src/providers/krb5/krb5_common.h | 4 - src/providers/krb5/krb5_init.c | 265 +- src/providers/ldap/ldap_access.c | 122 +- src/providers/ldap/ldap_auth.c | 619 ++--- src/providers/ldap/ldap_common.c | 6 - src/providers/ldap/ldap_common.h | 62 +- src/providers/ldap/ldap_id.c | 300 +-- src/providers/ldap/ldap_init.c | 780 +++--- src/providers/ldap/sdap_access.h | 11 + src/providers/ldap/sdap_autofs.c | 217 +- src/providers/ldap/sdap_autofs.h | 8 +- src/providers/ldap/sdap_idmap.c | 2 +- src/providers/ldap/sdap_online_check.c | 249 ++ src/providers/ldap/sdap_sudo.c | 233 +- src/providers/ldap/sdap_sudo.h | 8 +- src/providers/proxy/proxy.h | 25 +- src/providers/proxy/proxy_auth.c | 278 +-- src/providers/proxy/proxy_client.c | 192 ++ src/providers/proxy/proxy_id.c | 201 +- src/providers/proxy/proxy_init.c | 675 ++---- src/providers/simple/simple_access.c | 269 ++- src/providers/simple/simple_access_check.c | 17 +- 67 files changed, 8132 insertions(+), 8362 deletions(-) create mode 100644 src/providers/data_provider/dp_target_auth.c create mode 100644 src/providers/data_provider/dp_target_autofs.c create mode 100644 src/providers/data_provider/dp_target_hostid.c create mode 100644 src/providers/data_provider/dp_target_id.c create mode 100644 src/providers/data_provider/dp_target_subdomains.c create mode 100644 src/providers/data_provider/dp_target_sudo.c create mode 100644 src/providers/ldap/sdap_online_check.c create mode 100644 src/providers/proxy/proxy_client.c (limited to 'src/providers') diff --git a/src/providers/ad/ad_access.c b/src/providers/ad/ad_access.c index bf03f8637..a64a5b053 100644 --- a/src/providers/ad/ad_access.c +++ b/src/providers/ad/ad_access.c @@ -457,73 +457,92 @@ ad_access_recv(struct tevent_req *req) return EOK; } -static void -ad_access_done(struct tevent_req *req); +struct ad_pam_access_handler_state { + struct pam_data *pd; +}; -void -ad_access_handler(struct be_req *breq) +static void ad_pam_access_handler_done(struct tevent_req *subreq); + +struct tevent_req * +ad_pam_access_handler_send(TALLOC_CTX *mem_ctx, + struct ad_access_ctx *access_ctx, + struct pam_data *pd, + struct dp_req_params *params) { + struct ad_pam_access_handler_state *state; + struct tevent_req *subreq; struct tevent_req *req; - struct be_ctx *be_ctx = be_req_get_be_ctx(breq); - struct ad_access_ctx *access_ctx = - talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data, - struct ad_access_ctx); - struct pam_data *pd = - talloc_get_type(be_req_get_data(breq), struct pam_data); - struct sss_domain_info *domain; - /* Handle subdomains */ - if (strcasecmp(pd->domain, be_ctx->domain->name) != 0) { - domain = find_domain_by_name(be_ctx->domain, pd->domain, true); - if (domain == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "find_domain_by_name failed.\n"); - be_req_terminate(breq, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL); - return; - } - } else { - domain = be_ctx->domain; + req = tevent_req_create(mem_ctx, &state, + struct ad_pam_access_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } - /* Verify access control: locked accounts, ldap policies, GPOs, etc */ - req = ad_access_send(breq, be_ctx->ev, be_ctx, domain, - access_ctx, pd); - if (!req) { - be_req_terminate(breq, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL); - return; + state->pd = pd; + + subreq = ad_access_send(state, params->ev, params->be_ctx, + params->domain, access_ctx, pd); + if (subreq == NULL) { + pd->pam_status = PAM_SYSTEM_ERR; + goto immediately; } - tevent_req_set_callback(req, ad_access_done, breq); + + tevent_req_set_callback(subreq, ad_pam_access_handler_done, req); + + return req; + +immediately: + /* 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 -ad_access_done(struct tevent_req *req) +static void ad_pam_access_handler_done(struct tevent_req *subreq) { + struct ad_pam_access_handler_state *state; + struct tevent_req *req; errno_t ret; - struct be_req *breq = - tevent_req_callback_data(req, struct be_req); - struct pam_data *pd = - talloc_get_type(be_req_get_data(breq), struct pam_data); - ret = ad_access_recv(req); - talloc_zfree(req); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_pam_access_handler_state); + + ret = ad_access_recv(subreq); + talloc_free(subreq); switch (ret) { case EOK: - pd->pam_status = PAM_SUCCESS; - be_req_terminate(breq, DP_ERR_OK, PAM_SUCCESS, NULL); - return; + state->pd->pam_status = PAM_SUCCESS; + break; case ERR_ACCESS_DENIED: - /* We got the proper denial */ - pd->pam_status = PAM_PERM_DENIED; - be_req_terminate(breq, DP_ERR_OK, PAM_PERM_DENIED, NULL); - return; + state->pd->pam_status = PAM_PERM_DENIED; + break; case ERR_ACCOUNT_EXPIRED: - pd->pam_status = PAM_ACCT_EXPIRED; - be_req_terminate(breq, DP_ERR_OK, PAM_ACCT_EXPIRED, NULL); - return; + state->pd->pam_status = PAM_ACCT_EXPIRED; + break; default: - /* Something went wrong */ - pd->pam_status = PAM_SYSTEM_ERR; - be_req_terminate(breq, DP_ERR_FATAL, - PAM_SYSTEM_ERR, sss_strerror(ret)); - return; + state->pd->pam_status = PAM_SYSTEM_ERR; + break; } + + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); +} + +errno_t +ad_pam_access_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data) +{ + struct ad_pam_access_handler_state *state = NULL; + + state = tevent_req_data(req, struct ad_pam_access_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_data = talloc_steal(mem_ctx, state->pd); + + return EOK; } diff --git a/src/providers/ad/ad_access.h b/src/providers/ad/ad_access.h index 34f5d2d1b..cc565a8e6 100644 --- a/src/providers/ad/ad_access.h +++ b/src/providers/ad/ad_access.h @@ -49,7 +49,15 @@ struct ad_access_ctx { enum gpo_map_type gpo_default_right; }; -void -ad_access_handler(struct be_req *breq); +struct tevent_req * +ad_pam_access_handler_send(TALLOC_CTX *mem_ctx, + struct ad_access_ctx *access_ctx, + struct pam_data *pd, + struct dp_req_params *params); + +errno_t +ad_pam_access_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data); #endif /* AD_ACCESS_H_ */ diff --git a/src/providers/ad/ad_autofs.c b/src/providers/ad/ad_autofs.c index 7d4ed34b5..c1d7219aa 100644 --- a/src/providers/ad/ad_autofs.c +++ b/src/providers/ad/ad_autofs.c @@ -22,16 +22,16 @@ #include "providers/ad/ad_common.h" #include "providers/ldap/sdap_autofs.h" -int ad_autofs_init(struct be_ctx *be_ctx, - struct ad_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data) +errno_t ad_autofs_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx, + struct dp_method *dp_methods) { int ret; DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing autofs AD back end\n"); - ret = sdap_autofs_init(be_ctx, id_ctx->sdap_id_ctx, ops, pvt_data); + ret = sdap_autofs_init(mem_ctx, be_ctx, id_ctx->sdap_id_ctx, dp_methods); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD autofs [%d]: %s\n", ret, sss_strerror(ret)); diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h index 37178d611..ce363c5a4 100644 --- a/src/providers/ad/ad_common.h +++ b/src/providers/ad/ad_common.h @@ -172,15 +172,15 @@ errno_t ad_dyndns_init(struct be_ctx *be_ctx, struct ad_options *ctx); void ad_dyndns_timer(void *pvt); -int ad_sudo_init(struct be_ctx *be_ctx, - struct ad_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data); - -int ad_autofs_init(struct be_ctx *be_ctx, - struct ad_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data); +errno_t ad_sudo_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx, + struct dp_method *dp_methods); + +errno_t ad_autofs_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx, + struct dp_method *dp_methods); errno_t ad_machine_account_password_renewal_init(struct be_ctx *be_ctx, struct ad_options *ad_opts); diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c index c464fa948..92ac4ab6a 100644 --- a/src/providers/ad/ad_id.c +++ b/src/providers/ad/ad_id.c @@ -51,7 +51,6 @@ disable_gc(struct ad_options *ad_options) } struct ad_handle_acct_info_state { - struct be_req *breq; struct be_acct_req *ar; struct sdap_id_ctx *ctx; struct sdap_id_conn_ctx **conn; @@ -69,7 +68,6 @@ static void ad_handle_acct_info_done(struct tevent_req *subreq); struct tevent_req * ad_handle_acct_info_send(TALLOC_CTX *mem_ctx, - struct be_req *breq, struct be_acct_req *ar, struct sdap_id_ctx *ctx, struct ad_options *ad_options, @@ -78,14 +76,13 @@ ad_handle_acct_info_send(TALLOC_CTX *mem_ctx, { struct tevent_req *req; struct ad_handle_acct_info_state *state; - struct be_ctx *be_ctx = be_req_get_be_ctx(breq); + struct be_ctx *be_ctx = ctx->be; errno_t ret; req = tevent_req_create(mem_ctx, &state, struct ad_handle_acct_info_state); if (req == NULL) { return NULL; } - state->breq = breq; state->ar = ar; state->ctx = ctx; state->sdom = sdom; @@ -276,7 +273,7 @@ ad_handle_acct_info_recv(struct tevent_req *req, } struct sdap_id_conn_ctx ** -get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx, +get_conn_list(TALLOC_CTX *mem_ctx, struct ad_id_ctx *ad_ctx, struct sss_domain_info *dom, struct be_acct_req *ar) { struct sdap_id_conn_ctx **clist; @@ -289,11 +286,11 @@ get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx, case BE_REQ_USER_AND_GROUP: /* get SID */ case BE_REQ_GROUP: /* group */ case BE_REQ_INITGROUPS: /* init groups for user */ - clist = ad_gc_conn_list(breq, ad_ctx, dom); + clist = ad_gc_conn_list(mem_ctx, ad_ctx, dom); break; default: /* Requests for other object should only contact LDAP by default */ - clist = ad_ldap_conn_list(breq, ad_ctx, dom); + clist = ad_ldap_conn_list(mem_ctx, ad_ctx, dom); break; } @@ -365,146 +362,136 @@ done: return shortcut; } -static void ad_account_info_complete(struct tevent_req *req); - -struct ad_account_info_state { - struct be_req *be_req; - struct sss_domain_info *dom; +struct ad_account_info_handler_state { + struct sss_domain_info *domain; + struct dp_reply_std reply; }; -void -ad_account_info_handler(struct be_req *be_req) +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 be_acct_req *data, + struct dp_req_params *params) { - struct ad_id_ctx *ad_ctx; - struct be_acct_req *ar; + struct ad_account_info_handler_state *state; + struct sdap_id_conn_ctx **clist; struct sdap_id_ctx *sdap_id_ctx; - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - struct tevent_req *req; - struct sss_domain_info *dom; + struct sss_domain_info *domain; struct sdap_domain *sdom; - struct sdap_id_conn_ctx **clist; + struct tevent_req *subreq; + struct tevent_req *req; + struct be_ctx *be_ctx; bool shortcut; errno_t ret; - struct ad_account_info_state *state; - ad_ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data, - struct ad_id_ctx); - ar = talloc_get_type(be_req_get_data(be_req), struct be_acct_req); - sdap_id_ctx = ad_ctx->sdap_id_ctx; + sdap_id_ctx = id_ctx->sdap_id_ctx; + be_ctx = params->be_ctx; - if (be_is_offline(be_ctx)) { - return be_req_terminate(be_req, DP_ERR_OFFLINE, EAGAIN, "Offline"); + 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(ar)) { + if (sdap_is_enum_request(data)) { DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n"); - return sdap_handler_done(be_req, DP_ERR_OK, EOK, "Success"); + ret = EOK; + goto immediately; } /* Try to shortcut if this is ID or SID search and it belongs to * other domain range than is in ar->domain. */ shortcut = ad_account_can_shortcut(be_ctx, sdap_id_ctx->opts->idmap_ctx, - ar->filter_type, ar->filter_value, - ar->domain); + data->filter_type, data->filter_value, + data->domain); if (shortcut) { DEBUG(SSSDBG_TRACE_FUNC, "This ID is from different domain\n"); - be_req_terminate(be_req, DP_ERR_OK, EOK, NULL); - return; + ret = EOK; + goto immediately; } - dom = be_ctx->domain; - if (strcasecmp(ar->domain, be_ctx->domain->name) != 0) { - /* Subdomain request, verify subdomain */ - dom = find_domain_by_name(be_ctx->domain, ar->domain, true); + domain = be_ctx->domain; + if (strcasecmp(data->domain, be_ctx->domain->name) != 0) { + /* Subdomain request, verify subdomain. */ + domain = find_domain_by_name(be_ctx->domain, data->domain, true); } - if (dom == NULL) { + if (domain == NULL) { ret = EINVAL; - goto fail; + goto immediately; } - /* Determine whether to connect to GC, LDAP or try both */ - clist = get_conn_list(be_req, ad_ctx, dom, ar); + /* Determine whether to connect to GC, LDAP or try both. */ + clist = get_conn_list(state, id_ctx, domain, data); if (clist == NULL) { ret = EIO; - goto fail; + goto immediately; } - sdom = sdap_domain_get(sdap_id_ctx->opts, dom); + sdom = sdap_domain_get(sdap_id_ctx->opts, domain); if (sdom == NULL) { ret = EIO; - goto fail; + goto immediately; } - state = talloc(be_req, struct ad_account_info_state); - if (state == NULL) { - ret = ENOMEM; - goto fail; - } - state->dom = sdom->dom; - state->be_req = be_req; + state->domain = sdom->dom; - req = ad_handle_acct_info_send(be_req, be_req, ar, sdap_id_ctx, - ad_ctx->ad_options, sdom, clist); - if (req == NULL) { + subreq = ad_handle_acct_info_send(state, data, sdap_id_ctx, + id_ctx->ad_options, sdom, clist); + if (subreq == NULL) { ret = ENOMEM; - goto fail; + goto immediately; } - tevent_req_set_callback(req, ad_account_info_complete, state); - return; -fail: - be_req_terminate(be_req, DP_ERR_FATAL, ret, NULL); + tevent_req_set_callback(subreq, ad_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 -ad_account_info_complete(struct tevent_req *req) +static void ad_account_info_handler_done(struct tevent_req *subreq) { - struct be_req *be_req; - errno_t ret; + struct ad_account_info_handler_state *state; + struct tevent_req *req; + const char *err_msg; int dp_error; - const char *error_text = "Internal error"; - const char *req_error_text; - struct ad_account_info_state *state; - - state = tevent_req_callback_data(req, struct ad_account_info_state); - be_req = state->be_req; - - ret = ad_handle_acct_info_recv(req, &dp_error, &req_error_text); - talloc_zfree(req); - if (ret == ERR_SUBDOM_INACTIVE) { - be_mark_dom_offline(state->dom, be_req_get_be_ctx(be_req)); - return be_req_terminate(be_req, DP_ERR_OFFLINE, EAGAIN, "Offline"); - } else if (dp_error == DP_ERR_OK) { - if (ret == EOK) { - error_text = NULL; - } else { - DEBUG(SSSDBG_FATAL_FAILURE, - "Bug: dp_error is OK on failed request\n"); - dp_error = DP_ERR_FATAL; - error_text = req_error_text; - } - } else if (dp_error == DP_ERR_OFFLINE) { - error_text = "Offline"; - } else if (dp_error == DP_ERR_FATAL && ret == ENOMEM) { - error_text = "Out of memory"; - } else { - error_text = req_error_text; - } + 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); + talloc_zfree(subreq); - return be_req_terminate(be_req, dp_error, ret, error_text); + /* TODO For backward compatibility we always return EOK to DP now. */ + dp_reply_std_set(&state->reply, dp_error, ret, err_msg); + tevent_req_done(req); } -void -ad_check_online(struct be_req *be_req) +errno_t ad_account_info_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data) { - struct ad_id_ctx *ad_ctx; - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); + struct ad_account_info_handler_state *state = NULL; + + state = tevent_req_data(req, struct ad_account_info_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); - ad_ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data, - struct ad_id_ctx); + *data = state->reply; - return sdap_do_online_check(be_req, ad_ctx->sdap_id_ctx); + return EOK; } struct ad_enumeration_state { @@ -1079,3 +1066,4 @@ ad_enumeration_recv(struct tevent_req *req) TEVENT_REQ_RETURN_ON_ERROR(req); return EOK; } + diff --git a/src/providers/ad/ad_id.h b/src/providers/ad/ad_id.h index 9eb0ac375..2e7f9005a 100644 --- a/src/providers/ad/ad_id.h +++ b/src/providers/ad/ad_id.h @@ -23,12 +23,18 @@ #ifndef AD_ID_H_ #define AD_ID_H_ -void -ad_account_info_handler(struct be_req *breq); +struct tevent_req * +ad_account_info_handler_send(TALLOC_CTX *mem_ctx, + struct ad_id_ctx *id_ctx, + struct be_acct_req *data, + struct dp_req_params *params); + +errno_t ad_account_info_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data); struct tevent_req * ad_handle_acct_info_send(TALLOC_CTX *mem_ctx, - struct be_req *breq, struct be_acct_req *ar, struct sdap_id_ctx *ctx, struct ad_options *ad_options, @@ -48,6 +54,4 @@ ad_enumeration_send(TALLOC_CTX *mem_ctx, errno_t ad_enumeration_recv(struct tevent_req *req); -void -ad_check_online(struct be_req *be_req); #endif /* AD_ID_H_ */ diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c index 905338135..3a2ec1c7d 100644 --- a/src/providers/ad/ad_init.c +++ b/src/providers/ad/ad_init.c @@ -42,30 +42,10 @@ #include "providers/ad/ad_subdomains.h" #include "providers/ad/ad_domain_info.h" -struct ad_options *ad_options = NULL; - -static void -ad_shutdown(struct be_req *req); - -struct bet_ops ad_id_ops = { - .handler = ad_account_info_handler, - .finalize = ad_shutdown, - .check_online = ad_check_online -}; - -struct bet_ops ad_auth_ops = { - .handler = krb5_pam_handler, - .finalize = NULL -}; - -struct bet_ops ad_chpass_ops = { - .handler = krb5_pam_handler, - .finalize = NULL -}; - -struct bet_ops ad_access_ops = { - .handler = ad_access_handler, - .finalize = NULL +struct ad_init_ctx { + struct ad_options *options; + struct ad_id_ctx *id_ctx; + struct krb5_ctx *auth_ctx; }; #define AD_COMPAT_ON "1" @@ -119,7 +99,7 @@ static int map_sasl2sssd_log_level(int sasl_level) return sssd_level; } -int ad_sasl_log(void *context, int level, const char *message) +static int ad_sasl_log(void *context, int level, const char *message) { int sssd_level; @@ -137,6 +117,7 @@ static const sasl_callback_t ad_sasl_callbacks[] = { { SASL_CB_LOG, (sss_sasl_gen_cb_fn)ad_sasl_log, NULL }, { SASL_CB_LIST_END, NULL, NULL } }; + /* This is quite a hack, we *try* to fool openldap libraries by initializing * sasl first so we can pass in the SASL_CB_GETOPT callback we need to set some * options. Should be removed as soon as openldap exposes a way to do that */ @@ -149,26 +130,25 @@ static void ad_sasl_initialize(void) (void)sasl_client_init(ad_sasl_callbacks); } -static errno_t -common_ad_init(struct be_ctx *bectx) +static errno_t ad_init_options(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ad_options **_ad_options) { - errno_t ret; + struct ad_options *ad_options; char *ad_servers = NULL; char *ad_backup_servers = NULL; char *ad_realm; + errno_t ret; ad_sasl_initialize(); /* Get AD-specific options */ - ret = ad_get_common_options(bectx, bectx->cdb, - bectx->conf_path, - bectx->domain, - &ad_options); + ret = ad_get_common_options(mem_ctx, be_ctx->cdb, be_ctx->conf_path, + be_ctx->domain, &ad_options); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Could not parse common options: [%s]\n", - strerror(ret)); - goto done; + DEBUG(SSSDBG_FATAL_FAILURE, "Could not parse common options " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; } ad_servers = dp_opt_get_string(ad_options->basic, AD_SERVER); @@ -176,180 +156,165 @@ common_ad_init(struct be_ctx *bectx) ad_realm = dp_opt_get_string(ad_options->basic, AD_KRB5_REALM); /* Set up the failover service */ - ret = ad_failover_init(ad_options, bectx, ad_servers, ad_backup_servers, ad_realm, - AD_SERVICE_NAME, AD_GC_SERVICE_NAME, + ret = ad_failover_init(ad_options, be_ctx, ad_servers, ad_backup_servers, + ad_realm, AD_SERVICE_NAME, AD_GC_SERVICE_NAME, dp_opt_get_string(ad_options->basic, AD_DOMAIN), &ad_options->service); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to init AD failover service: [%s]\n", - strerror(ret)); - goto done; + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init AD failover service: " + "[%d]: %s\n", ret, sss_strerror(ret)); + talloc_free(ad_options); + return ret; } - ret = EOK; -done: - return ret; + *_ad_options = ad_options; + + return EOK; } -int -sssm_ad_id_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +static errno_t ad_init_srv_plugin(struct be_ctx *be_ctx, + struct ad_options *ad_options) { - errno_t ret; - struct ad_id_ctx *ad_ctx; + struct ad_srv_plugin_ctx *srv_ctx; const char *hostname; const char *ad_domain; const char *ad_site_override; - struct ad_srv_plugin_ctx *srv_ctx; + bool sites_enabled; + errno_t ret; - if (!ad_options) { - ret = common_ad_init(bectx); + hostname = dp_opt_get_string(ad_options->basic, AD_HOSTNAME); + ad_domain = dp_opt_get_string(ad_options->basic, AD_DOMAIN); + ad_site_override = dp_opt_get_string(ad_options->basic, AD_SITE); + sites_enabled = dp_opt_get_bool(ad_options->basic, AD_ENABLE_DNS_SITES); + + + if (!sites_enabled) { + ret = be_fo_set_dns_srv_lookup_plugin(be_ctx, hostname); if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set SRV lookup plugin " + "[%d]: %s\n", ret, sss_strerror(ret)); return ret; } - } - if (ad_options->id_ctx) { - /* already initialized */ - *ops = &ad_id_ops; - *pvt_data = ad_options->id_ctx; return EOK; } - - ad_ctx = ad_id_ctx_init(ad_options, bectx); - if (ad_ctx == NULL) { + srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx->be_res, + default_host_dbs, ad_options->id, + hostname, ad_domain, + ad_site_override); + if (srv_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n"); return ENOMEM; } - ad_options->id_ctx = ad_ctx; - ret = ad_dyndns_init(ad_ctx->sdap_id_ctx->be, ad_options); - if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, - "Failure setting up automatic DNS update\n"); - /* Continue without DNS updates */ - } + be_fo_set_srv_lookup_plugin(be_ctx, ad_srv_plugin_send, + ad_srv_plugin_recv, srv_ctx, "AD"); - ret = sdap_setup_child(); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "setup_child failed [%d][%s].\n", - ret, strerror(ret)); - goto done; - } + return EOK; +} - /* Set up various SDAP options */ - ret = ad_get_id_options(ad_options, bectx->cdb, - bectx->conf_path, - &ad_ctx->sdap_id_ctx->opts); - if (ret != EOK) { - goto done; - } +static errno_t ad_init_sdap_access_ctx(struct ad_access_ctx *access_ctx) +{ + struct dp_option *options = access_ctx->ad_options; + struct sdap_id_ctx *sdap_id_ctx = access_ctx->ad_id_ctx->sdap_id_ctx; + struct sdap_access_ctx *sdap_access_ctx; + const char *filter; - ret = sdap_id_setup_tasks(bectx, - ad_ctx->sdap_id_ctx, - ad_ctx->sdap_id_ctx->opts->sdom, - ad_enumeration_send, - ad_enumeration_recv, - ad_ctx); - if (ret != EOK) { - goto done; + sdap_access_ctx = talloc_zero(access_ctx, struct sdap_access_ctx); + if (sdap_access_ctx == NULL) { + return ENOMEM; } - ad_ctx->sdap_id_ctx->opts->sdom->pvt = ad_ctx; + sdap_access_ctx->id_ctx = sdap_id_ctx; - /* Set up the ID mapping object */ - ret = sdap_idmap_init(ad_ctx->sdap_id_ctx, ad_ctx->sdap_id_ctx, - &ad_ctx->sdap_id_ctx->opts->idmap_ctx); - if (ret != EOK) goto done; - ret = setup_tls_config(ad_ctx->sdap_id_ctx->opts->basic); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "setup_tls_config failed [%s]\n", strerror(ret)); - goto done; - } - - /* setup SRV lookup plugin */ - hostname = dp_opt_get_string(ad_options->basic, AD_HOSTNAME); - if (dp_opt_get_bool(ad_options->basic, AD_ENABLE_DNS_SITES)) { - /* use AD plugin */ - ad_domain = dp_opt_get_string(ad_options->basic, AD_DOMAIN); - ad_site_override = dp_opt_get_string(ad_options->basic, AD_SITE); - - srv_ctx = ad_srv_plugin_ctx_init(bectx, bectx->be_res, - default_host_dbs, ad_options->id, - hostname, ad_domain, - ad_site_override); - if (srv_ctx == NULL) { - DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n"); - ret = ENOMEM; - goto done; + /* If ad_access_filter is set, the value of ldap_acess_order is + * expire, filter, otherwise only expire. + */ + sdap_access_ctx->access_rule[0] = LDAP_ACCESS_EXPIRE; + filter = dp_opt_get_cstring(options, AD_ACCESS_FILTER); + if (filter != NULL) { + /* The processing of the extended filter is performed during the access + * check itself. + */ + sdap_access_ctx->filter = talloc_strdup(sdap_access_ctx, filter); + if (sdap_access_ctx->filter == NULL) { + talloc_free(sdap_access_ctx); + return ENOMEM; } - be_fo_set_srv_lookup_plugin(bectx, ad_srv_plugin_send, - ad_srv_plugin_recv, srv_ctx, "AD"); + sdap_access_ctx->access_rule[1] = LDAP_ACCESS_FILTER; + sdap_access_ctx->access_rule[2] = LDAP_ACCESS_EMPTY; } else { - /* fall back to standard plugin */ - ret = be_fo_set_dns_srv_lookup_plugin(bectx, hostname); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set SRV lookup plugin " - "[%d]: %s\n", ret, strerror(ret)); - goto done; - } + sdap_access_ctx->access_rule[1] = LDAP_ACCESS_EMPTY; } - /* setup periodical refresh of expired records */ - ret = sdap_refresh_init(bectx->refresh_ctx, ad_ctx->sdap_id_ctx); - if (ret != EOK && ret != EEXIST) { - DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh " - "will not work [%d]: %s\n", ret, strerror(ret)); + access_ctx->sdap_access_ctx = sdap_access_ctx; + + return EOK; +} + +errno_t ad_gpo_parse_map_options(struct ad_access_ctx *access_ctx); + +static errno_t ad_init_gpo(struct ad_access_ctx *access_ctx) +{ + struct dp_option *options; + const char *gpo_access_control_mode; + int gpo_cache_timeout; + errno_t ret; + + options = access_ctx->ad_options; + + /* GPO access control mode */ + gpo_access_control_mode = dp_opt_get_string(options, AD_GPO_ACCESS_CONTROL); + if (gpo_access_control_mode == NULL) { + return EINVAL; + } else if (strcasecmp(gpo_access_control_mode, "disabled") == 0) { + access_ctx->gpo_access_control_mode = GPO_ACCESS_CONTROL_DISABLED; + } else if (strcasecmp(gpo_access_control_mode, "permissive") == 0) { + access_ctx->gpo_access_control_mode = GPO_ACCESS_CONTROL_PERMISSIVE; + } else if (strcasecmp(gpo_access_control_mode, "enforcing") == 0) { + access_ctx->gpo_access_control_mode = GPO_ACCESS_CONTROL_ENFORCING; + } else { + DEBUG(SSSDBG_FATAL_FAILURE, "Unrecognized GPO access control mode: " + "%s\n", gpo_access_control_mode); + return EINVAL; } - ret = ad_machine_account_password_renewal_init(bectx, ad_options); + /* GPO cache timeout */ + gpo_cache_timeout = dp_opt_get_int(options, AD_GPO_CACHE_TIMEOUT); + access_ctx->gpo_cache_timeout = gpo_cache_timeout; + + /* GPO logon maps */ + ret = sss_hash_create(access_ctx, 10, &access_ctx->gpo_map_options_table); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Cannot setup task for machine account " - "password renewal.\n"); - goto done; + DEBUG(SSSDBG_FATAL_FAILURE, "Could not create gpo_map_options " + "hash table [%d]: %s\n", ret, sss_strerror(ret)); + return ret; } - *ops = &ad_id_ops; - *pvt_data = ad_ctx; - - ret = EOK; -done: + ret = ad_gpo_parse_map_options(access_ctx); if (ret != EOK) { - talloc_zfree(ad_options->id_ctx); + DEBUG(SSSDBG_FATAL_FAILURE, "Could not parse gpo_map_options " + "(invalid config) [%d]: %s\n", ret, sss_strerror(ret)); + talloc_zfree(access_ctx->gpo_map_options_table); + return ret; } - return ret; + + return EOK; } -int -sssm_ad_auth_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +static errno_t ad_init_auth_ctx(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ad_options *ad_options, + struct krb5_ctx **_auth_ctx) { + struct krb5_ctx *krb5_auth_ctx; errno_t ret; - struct krb5_ctx *krb5_auth_ctx = NULL; - - if (!ad_options) { - ret = common_ad_init(bectx); - if (ret != EOK) { - return ret; - } - } - - if (ad_options->auth_ctx) { - /* Already initialized */ - *ops = &ad_auth_ops; - *pvt_data = ad_options->auth_ctx; - return EOK; - } - krb5_auth_ctx = talloc_zero(NULL, struct krb5_ctx); - if (!krb5_auth_ctx) { + krb5_auth_ctx = talloc_zero(mem_ctx, struct krb5_ctx); + if (krb5_auth_ctx == NULL) { ret = ENOMEM; goto done; } @@ -357,257 +322,324 @@ sssm_ad_auth_init(struct be_ctx *bectx, krb5_auth_ctx->config_type = K5C_GENERIC; krb5_auth_ctx->service = ad_options->service->krb5_service; - ret = ad_get_auth_options(krb5_auth_ctx, ad_options, bectx, + ret = ad_get_auth_options(krb5_auth_ctx, ad_options, be_ctx, &krb5_auth_ctx->opts); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Could not determine Kerberos options\n"); + DEBUG(SSSDBG_FATAL_FAILURE, "Could not determine Kerberos options\n"); goto done; } - ret = krb5_child_init(krb5_auth_ctx, bectx); + ret = krb5_child_init(krb5_auth_ctx, be_ctx); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Could not initialize krb5_child settings: [%s]\n", - strerror(ret)); + DEBUG(SSSDBG_FATAL_FAILURE, "Could not initialize krb5_child settings: " + "[%d]: %s\n", ret, sss_strerror(ret)); goto done; } - ad_options->auth_ctx = talloc_steal(ad_options, krb5_auth_ctx); - *ops = &ad_auth_ops; - *pvt_data = ad_options->auth_ctx; + ad_options->auth_ctx = krb5_auth_ctx; + *_auth_ctx = krb5_auth_ctx; + + ret = EOK; done: if (ret != EOK) { talloc_free(krb5_auth_ctx); } + return ret; } -int -sssm_ad_chpass_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +static errno_t ad_init_misc(struct be_ctx *be_ctx, + struct ad_options *ad_options, + struct ad_id_ctx *ad_id_ctx, + struct sdap_id_ctx *sdap_id_ctx) { errno_t ret; - if (!ad_options) { - ret = common_ad_init(bectx); - if (ret != EOK) { - return ret; - } + ret = ad_dyndns_init(be_ctx, ad_options); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failure setting up automatic DNS update\n"); + /* Continue without DNS updates */ } - if (ad_options->auth_ctx) { - /* Already initialized */ - *ops = &ad_chpass_ops; - *pvt_data = ad_options->auth_ctx; - return EOK; + ret = setup_tls_config(sdap_id_ctx->opts->basic); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get TLS options [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; } - ret = sssm_ad_auth_init(bectx, ops, pvt_data); - *ops = &ad_chpass_ops; - ad_options->auth_ctx = *pvt_data; - return ret; -} - -/* GPO parsing of PAM service names to Windows Logon Rights*/ -errno_t ad_gpo_parse_map_options(struct ad_access_ctx *access_ctx); + ret = sdap_idmap_init(sdap_id_ctx, sdap_id_ctx, + &sdap_id_ctx->opts->idmap_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Could not initialize ID mapping. In case ID mapping properties " + "changed on the server, please remove the SSSD database\n"); + return ret; + } -int -sssm_ad_access_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) -{ - errno_t ret; - struct ad_access_ctx *access_ctx; - struct ad_id_ctx *ad_id_ctx; - const char *filter; - const char *gpo_access_control_mode; - int gpo_cache_timeout; + ret = sdap_id_setup_tasks(be_ctx, sdap_id_ctx, sdap_id_ctx->opts->sdom, + ad_enumeration_send, ad_enumeration_recv, + ad_id_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup background tasks " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } - access_ctx = talloc_zero(bectx, struct ad_access_ctx); - if (!access_ctx) return ENOMEM; + sdap_id_ctx->opts->sdom->pvt = ad_id_ctx; - ret = sssm_ad_id_init(bectx, ops, (void **)&ad_id_ctx); + ret = sdap_setup_child(); if (ret != EOK) { - goto fail; + DEBUG(SSSDBG_CRIT_FAILURE, "sdap_setup_child() failed [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; } - access_ctx->ad_id_ctx = ad_id_ctx; - ret = dp_copy_options(access_ctx, ad_options->basic, AD_OPTS_BASIC, - &access_ctx->ad_options); + ret = ad_init_srv_plugin(be_ctx, ad_options); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Could not initialize access provider options: [%s]\n", - strerror(ret)); - goto fail; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup SRV plugin [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; } - /* Set up an sdap_access_ctx for checking expired/locked accounts */ - access_ctx->sdap_access_ctx = - talloc_zero(access_ctx, struct sdap_access_ctx); - if (!access_ctx->sdap_access_ctx) { - ret = ENOMEM; - goto fail; + ret = sdap_refresh_init(be_ctx->refresh_ctx, sdap_id_ctx); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh " + "will not work [%d]: %s\n", ret, sss_strerror(ret)); } - access_ctx->sdap_access_ctx->id_ctx = ad_id_ctx->sdap_id_ctx; - /* If ad_access_filter is set, the value of ldap_acess_order is - * expire, filter, otherwise only expire - */ - access_ctx->sdap_access_ctx->access_rule[0] = LDAP_ACCESS_EXPIRE; - filter = dp_opt_get_cstring(access_ctx->ad_options, AD_ACCESS_FILTER); - if (filter != NULL) { - /* The processing of the extended filter is performed during the access - * check itself - */ - access_ctx->sdap_access_ctx->filter = talloc_strdup( - access_ctx->sdap_access_ctx, - filter); - if (access_ctx->sdap_access_ctx->filter == NULL) { - ret = ENOMEM; - goto fail; - } + ret = ad_machine_account_password_renewal_init(be_ctx, ad_options); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot setup task for machine account " + "password renewal.\n"); + return ret; + } - access_ctx->sdap_access_ctx->access_rule[1] = LDAP_ACCESS_FILTER; - access_ctx->sdap_access_ctx->access_rule[2] = LDAP_ACCESS_EMPTY; - } else { - access_ctx->sdap_access_ctx->access_rule[1] = LDAP_ACCESS_EMPTY; + return EOK; +} + +errno_t sssm_ad_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct data_provider *provider, + const char *module_name, + void **_module_data) +{ + struct ad_init_ctx *init_ctx; + errno_t ret; + + init_ctx = talloc_zero(mem_ctx, struct ad_init_ctx); + if (init_ctx == NULL) { + return ENOMEM; } - /* GPO access control mode */ - gpo_access_control_mode = - dp_opt_get_string(access_ctx->ad_options, AD_GPO_ACCESS_CONTROL); - if (strcasecmp(gpo_access_control_mode, "disabled") == 0) { - access_ctx->gpo_access_control_mode = GPO_ACCESS_CONTROL_DISABLED; - } else if (strcasecmp(gpo_access_control_mode, "permissive") == 0) { - access_ctx->gpo_access_control_mode = GPO_ACCESS_CONTROL_PERMISSIVE; - } else if (strcasecmp(gpo_access_control_mode, "enforcing") == 0) { - access_ctx->gpo_access_control_mode = GPO_ACCESS_CONTROL_ENFORCING; - } else { - DEBUG(SSSDBG_FATAL_FAILURE, - "Unrecognized GPO access control mode: %s\n", - gpo_access_control_mode); - ret = EINVAL; - goto fail; + /* Always initialize options since it is needed everywhere. */ + ret = ad_init_options(mem_ctx, be_ctx, &init_ctx->options); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init AD options [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; } - /* GPO cache timeout */ - gpo_cache_timeout = - dp_opt_get_int(access_ctx->ad_options, AD_GPO_CACHE_TIMEOUT); - access_ctx->gpo_cache_timeout = gpo_cache_timeout; + /* Always initialize id_ctx since it is needed everywhere. */ + init_ctx->id_ctx = ad_id_ctx_init(init_ctx->options, be_ctx); + if (init_ctx->id_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize AD ID context\n"); + ret = ENOMEM; + goto done; + } - /* GPO logon maps */ + init_ctx->options->id_ctx = init_ctx->id_ctx; - ret = sss_hash_create(access_ctx, 10, &access_ctx->gpo_map_options_table); + ret = ad_get_id_options(init_ctx->options, be_ctx->cdb, be_ctx->conf_path, + &init_ctx->id_ctx->sdap_id_ctx->opts); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Could not create gpo_map_options hash table: [%s]\n", - strerror(ret)); - goto fail; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init AD id options\n"); + return ret; } - ret = ad_gpo_parse_map_options(access_ctx); + /* Setup miscellaneous things. */ + ret = ad_init_misc(be_ctx, init_ctx->options, init_ctx->id_ctx, + init_ctx->id_ctx->sdap_id_ctx); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Could not parse gpo_map_options (invalid config): [%s]\n", - strerror(ret)); - goto fail; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init AD module " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; } - *ops = &ad_access_ops; - *pvt_data = access_ctx; + /* Initialize auth_ctx only if one of the target is enabled. */ + if (dp_target_enabled(provider, module_name, DPT_AUTH, DPT_CHPASS)) { + ret = ad_init_auth_ctx(init_ctx, be_ctx, init_ctx->options, + &init_ctx->auth_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create auth context " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + } - return EOK; + *_module_data = init_ctx; + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(init_ctx); + } -fail: - talloc_free(access_ctx); return ret; } -static void -ad_shutdown(struct be_req *req) +errno_t sssm_ad_id_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - /* TODO: Clean up any internal data */ - sdap_handler_done(req, DP_ERR_OK, EOK, NULL); + struct ad_init_ctx *init_ctx; + struct ad_id_ctx *id_ctx; + + init_ctx = talloc_get_type(module_data, struct ad_init_ctx); + id_ctx = init_ctx->id_ctx; + + dp_set_method(dp_methods, DPM_ACCOUNT_HANDLER, + ad_account_info_handler_send, ad_account_info_handler_recv, id_ctx, + struct ad_id_ctx, struct be_acct_req, struct dp_reply_std); + + dp_set_method(dp_methods, DPM_CHECK_ONLINE, + sdap_online_check_handler_send, sdap_online_check_handler_recv, id_ctx->sdap_id_ctx, + struct sdap_id_ctx, void, struct dp_reply_std); + + return EOK; } -int sssm_ad_subdomains_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +errno_t sssm_ad_auth_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - int ret; - struct ad_id_ctx *id_ctx; - const char *ad_domain; + struct ad_init_ctx *init_ctx; + struct krb5_ctx *auth_ctx; - ret = sssm_ad_id_init(bectx, ops, (void **) &id_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ad_id_init failed.\n"); - return ret; - } + init_ctx = talloc_get_type(module_data, struct ad_init_ctx); + auth_ctx = init_ctx->auth_ctx; - if (ad_options == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Global AD options not available.\n"); - return EINVAL; + dp_set_method(dp_methods, DPM_AUTH_HANDLER, + krb5_pam_handler_send, krb5_pam_handler_recv, auth_ctx, + struct krb5_ctx, struct pam_data, struct pam_data *); + + return EOK; +} + +errno_t sssm_ad_chpass_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) +{ + return sssm_ad_auth_init(mem_ctx, be_ctx, module_data, dp_methods); +} + +errno_t sssm_ad_access_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) +{ + struct ad_init_ctx *init_ctx; + struct ad_access_ctx *access_ctx; + errno_t ret; + + init_ctx = talloc_get_type(module_data, struct ad_init_ctx); + + access_ctx = talloc_zero(mem_ctx, struct ad_access_ctx); + if (access_ctx == NULL) { + return ENOMEM; } - ad_domain = dp_opt_get_cstring(ad_options->basic, AD_DOMAIN); + access_ctx->ad_id_ctx = init_ctx->id_ctx; - ret = ad_subdom_init(bectx, id_ctx, ad_domain, ops, pvt_data); + ret = dp_copy_options(access_ctx, init_ctx->options->basic, AD_OPTS_BASIC, + &access_ctx->ad_options); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "ad_subdom_init failed.\n"); - return ret; + DEBUG(SSSDBG_CRIT_FAILURE, "Could not initialize access provider " + "options [%d]: %s\n", ret, sss_strerror(ret)); + goto done; } - return EOK; -} + ret = ad_init_sdap_access_ctx(access_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Could not initialize sdap access context " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + ret = ad_init_gpo(access_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Could not initialize GPO " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } -int sssm_ad_sudo_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) -{ -#ifdef BUILD_SUDO - struct ad_id_ctx *id_ctx; - int ret; + dp_set_method(dp_methods, DPM_ACCESS_HANDLER, + ad_pam_access_handler_send, ad_pam_access_handler_recv, access_ctx, + struct ad_access_ctx, struct pam_data, struct pam_data *); - DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing AD sudo handler\n"); + ret = EOK; - ret = sssm_ad_id_init(bectx, ops, (void **) &id_ctx); +done: if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ad_id_init failed.\n"); - return ret; + talloc_free(access_ctx); } - return ad_sudo_init(bectx, id_ctx, ops, pvt_data); -#else - DEBUG(SSSDBG_MINOR_FAILURE, "Sudo init handler called but SSSD is " - "built without sudo support, ignoring\n"); - return EOK; -#endif + return ret; } -int sssm_ad_autofs_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +errno_t sssm_ad_autofs_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { #ifdef BUILD_AUTOFS - struct ad_id_ctx *id_ctx; - int ret; + struct ad_init_ctx *init_ctx; DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing AD autofs handler\n"); + init_ctx = talloc_get_type(module_data, struct ad_init_ctx); - ret = sssm_ad_id_init(bectx, ops, (void **) &id_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ad_id_init failed.\n"); - return ret; - } - - return ad_autofs_init(bectx, id_ctx, ops, pvt_data); + return ad_autofs_init(mem_ctx, be_ctx, init_ctx->id_ctx, dp_methods); #else DEBUG(SSSDBG_MINOR_FAILURE, "Autofs init handler called but SSSD is " "built without autofs support, ignoring\n"); return EOK; #endif } + +errno_t sssm_ad_subdomains_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) +{ + struct ad_init_ctx *init_ctx; + + DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing AD subdomains handler\n"); + init_ctx = talloc_get_type(module_data, struct ad_init_ctx); + + return ad_subdomains_init(mem_ctx, be_ctx, init_ctx->id_ctx, dp_methods); +} + +errno_t sssm_ad_sudo_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) +{ +#ifdef BUILD_SUDO + struct ad_init_ctx *init_ctx; + + DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing AD sudo handler\n"); + init_ctx = talloc_get_type(module_data, struct ad_init_ctx); + + return ad_sudo_init(mem_ctx, be_ctx, init_ctx->id_ctx, dp_methods); +#else + DEBUG(SSSDBG_MINOR_FAILURE, "Sudo init handler called but SSSD is " + "built without sudo support, ignoring\n"); + return EOK; +#endif +} diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c index 1aee92cab..5b0bee866 100644 --- a/src/providers/ad/ad_subdomains.c +++ b/src/providers/ad/ad_subdomains.c @@ -29,6 +29,7 @@ #include "providers/ad/ad_common.h" #include "providers/ldap/sdap_idmap.h" +#include "providers/ldap/sdap_ops.h" #include "util/util_sss_idmap.h" #include #include @@ -56,42 +57,6 @@ /* do not refresh more often than every 5 seconds for now */ #define AD_SUBDOMAIN_REFRESH_LIMIT 5 -struct ad_subdomains_ctx { - struct be_ctx *be_ctx; - struct sdap_id_ctx *sdap_id_ctx; - struct sdap_domain *sdom; - struct sdap_id_conn_ctx *ldap_ctx; - struct sss_idmap_ctx *idmap_ctx; - char *domain_name; - - time_t last_refreshed; - struct tevent_timer *timer_event; - struct ad_id_ctx *ad_id_ctx; -}; - -struct ad_subdomains_req_ctx { - struct be_req *be_req; - struct ad_subdomains_ctx *sd_ctx; - struct sdap_id_op *sdap_op; - - char *current_filter; - size_t base_iter; - - struct ad_id_ctx *root_id_ctx; - struct sdap_id_op *root_op; - size_t root_base_iter; - struct sysdb_attrs *root_domain_attrs; - struct sss_domain_info *root_domain; - - size_t reply_count; - struct sysdb_attrs **reply; - - char *master_sid; - char *flat_name; - char *site; - char *forest; -}; - static errno_t ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, struct ad_id_ctx *id_ctx, @@ -119,8 +84,8 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, return EINVAL; } - ad_options = ad_create_2way_trust_options(id_ctx, realm, - ad_domain, hostname, keytab); + ad_options = ad_create_2way_trust_options(id_ctx, realm, ad_domain, + hostname, keytab); if (ad_options == NULL) { DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD options\n"); talloc_free(ad_options); @@ -192,34 +157,16 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, return EOK; } -static errno_t -ads_store_sdap_subdom(struct ad_subdomains_ctx *ctx, - struct sss_domain_info *parent) -{ - int ret; - struct sdap_domain *sditer; - struct ad_id_ctx *subdom_id_ctx; - - ret = sdap_domain_subdom_add(ctx->sdap_id_ctx, ctx->sdom, parent); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_domain_subdom_add failed.\n"); - return ret; - } +struct ad_subdomains_ctx { + struct be_ctx *be_ctx; + struct ad_id_ctx *ad_id_ctx; + struct sdap_id_ctx *sdap_id_ctx; - DLIST_FOR_EACH(sditer, ctx->sdom) { - if (IS_SUBDOMAIN(sditer->dom) && sditer->pvt == NULL) { - ret = ad_subdom_ad_ctx_new(ctx->be_ctx, ctx->ad_id_ctx, - sditer->dom, &subdom_id_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "ad_subdom_ad_ctx_new failed.\n"); - } else { - sditer->pvt = subdom_id_ctx; - } - } - } + struct sdap_domain *sdom; + char *domain_name; - return EOK; -} + time_t last_refreshed; +}; static errno_t ad_subdom_enumerates(struct sss_domain_info *parent, struct sysdb_attrs *attrs, @@ -239,7 +186,7 @@ static errno_t ad_subdom_enumerates(struct sss_domain_info *parent, } static errno_t -ad_subdom_store(struct ad_subdomains_ctx *ctx, +ad_subdom_store(struct sdap_idmap_ctx *idmap_ctx, struct sss_domain_info *domain, struct sysdb_attrs *subdom_attrs, bool enumerate) @@ -293,10 +240,8 @@ ad_subdom_store(struct ad_subdomains_ctx *ctx, goto done; } - err = sss_idmap_bin_sid_to_sid(ctx->idmap_ctx, - el->values[0].data, - el->values[0].length, - &sid_str); + err = sss_idmap_bin_sid_to_sid(idmap_ctx->map, el->values[0].data, + el->values[0].length, &sid_str); if (err != IDMAP_SUCCESS) { DEBUG(SSSDBG_MINOR_FAILURE, "Could not convert SID: [%s].\n", idmap_error_string(err)); @@ -304,10 +249,7 @@ ad_subdom_store(struct ad_subdomains_ctx *ctx, goto done; } - mpg = sdap_idmap_domain_has_algorithmic_mapping( - ctx->sdap_id_ctx->opts->idmap_ctx, - name, - sid_str); + mpg = sdap_idmap_domain_has_algorithmic_mapping(idmap_ctx, name, sid_str); ret = sysdb_subdomain_store(domain->sysdb, name, realm, flat, sid_str, mpg, enumerate, domain->forest, 0); @@ -318,32 +260,37 @@ ad_subdom_store(struct ad_subdomains_ctx *ctx, ret = EOK; done: - sss_idmap_free_sid(ctx->sdap_id_ctx->opts->idmap_ctx->map, sid_str); + sss_idmap_free_sid(idmap_ctx->map, sid_str); talloc_free(tmp_ctx); return ret; } -static errno_t ad_subdomains_refresh(struct ad_subdomains_ctx *ctx, - int count, bool root_domain, - struct sysdb_attrs **reply, - bool *changes) +static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx, + struct sdap_idmap_ctx *idmap_ctx, + struct sdap_options *opts, + struct sysdb_attrs **subdomains, + size_t num_subdomains, + bool root_domain, + time_t *_last_refreshed, + bool *_changes) { struct sdap_domain *sdom; - struct sss_domain_info *domain, *dom; - bool handled[count]; + struct sss_domain_info *domain; + struct sss_domain_info *dom; + bool handled[num_subdomains]; const char *value; const char *root_name = NULL; - int c, h; + size_t c, h; int ret; bool enumerate; - domain = ctx->be_ctx->domain; - memset(handled, 0, sizeof(bool) * count); + domain = be_ctx->domain; + memset(handled, 0, sizeof(bool) * num_subdomains); h = 0; if (root_domain) { - ret = sysdb_attrs_get_string(reply[0], AD_AT_TRUST_PARTNER, + ret = sysdb_attrs_get_string(subdomains[0], AD_AT_TRUST_PARTNER, &root_name); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); @@ -363,11 +310,12 @@ static errno_t ad_subdomains_refresh(struct ad_subdomains_ctx *ctx, continue; } - for (c = 0; c < count; c++) { + for (c = 0; c < num_subdomains; c++) { if (handled[c]) { continue; } - ret = sysdb_attrs_get_string(reply[c], AD_AT_TRUST_PARTNER, &value); + ret = sysdb_attrs_get_string(subdomains[c], AD_AT_TRUST_PARTNER, + &value); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); goto done; @@ -377,7 +325,7 @@ static errno_t ad_subdomains_refresh(struct ad_subdomains_ctx *ctx, } } - if (c >= count) { + if (c >= num_subdomains) { /* ok this subdomain does not exist anymore, let's clean up */ sss_domain_set_state(dom, DOM_DISABLED); ret = sysdb_subdomain_delete(dom->sysdb, dom->name); @@ -385,29 +333,29 @@ static errno_t ad_subdomains_refresh(struct ad_subdomains_ctx *ctx, goto done; } - sdom = sdap_domain_get(ctx->sdap_id_ctx->opts, dom); + sdom = sdap_domain_get(opts, dom); if (sdom == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Domain does not exist?\n"); continue; } /* Remove the subdomain from the list of LDAP domains */ - sdap_domain_remove(ctx->sdap_id_ctx->opts, dom); + sdap_domain_remove(opts, dom); be_ptask_destroy(&sdom->enum_task); be_ptask_destroy(&sdom->cleanup_task); /* terminate all requests for this subdomain so we can free it */ - be_terminate_domain_requests(ctx->be_ctx, dom->name); + dp_terminate_domain_requests(be_ctx->provider, dom->name); talloc_zfree(sdom); } else { /* ok let's try to update it */ - ret = ad_subdom_enumerates(domain, reply[c], &enumerate); + ret = ad_subdom_enumerates(domain, subdomains[c], &enumerate); if (ret != EOK) { goto done; } - ret = ad_subdom_store(ctx, domain, reply[c], enumerate); + ret = ad_subdom_store(idmap_ctx, domain, subdomains[c], enumerate); if (ret) { /* Nothing we can do about the error. Let's at least try * to reuse the existing domains @@ -420,29 +368,29 @@ static errno_t ad_subdomains_refresh(struct ad_subdomains_ctx *ctx, } } - if (count == h) { + if (num_subdomains == h) { /* all domains were already accounted for and have been updated */ ret = EOK; - *changes = false; + *_changes = false; goto done; } /* if we get here it means we have changes to the subdomains list */ - *changes = true; + *_changes = true; - for (c = 0; c < count; c++) { + for (c = 0; c < num_subdomains; c++) { if (handled[c]) { continue; } /* Nothing we can do about the error. Let's at least try * to reuse the existing domains. */ - ret = ad_subdom_enumerates(domain, reply[c], &enumerate); + ret = ad_subdom_enumerates(domain, subdomains[c], &enumerate); if (ret != EOK) { goto done; } - ret = ad_subdom_store(ctx, domain, reply[c], enumerate); + ret = ad_subdom_store(idmap_ctx, domain, subdomains[c], enumerate); if (ret) { DEBUG(SSSDBG_MINOR_FAILURE, "Failed to parse subdom data, " "will try to use cached subdomain\n"); @@ -453,39 +401,132 @@ static errno_t ad_subdomains_refresh(struct ad_subdomains_ctx *ctx, done: if (ret != EOK) { - ctx->last_refreshed = 0; + *_last_refreshed = 0; } else { - ctx->last_refreshed = time(NULL); + *_last_refreshed = time(NULL); + } + + return ret; +} + +static errno_t ad_subdomains_process(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + size_t nsd, struct sysdb_attrs **sd, + struct sysdb_attrs *root, + size_t *_nsd_out, + struct sysdb_attrs ***_sd_out) +{ + size_t i, sdi; + struct sysdb_attrs **sd_out; + const char *sd_name; + errno_t ret; + + if (root == NULL) { + /* We are connected directly to the root domain. The 'sd' + * list is complete and we can just use it + */ + *_nsd_out = nsd; + *_sd_out = sd; + return EOK; + } + + /* If we searched for root separately, we must: + * a) treat the root domain as a subdomain + * b) filter the subdomain we are connected to from the subdomain + * list, from our point of view, it's the master domain + */ + sd_out = talloc_zero_array(mem_ctx, struct sysdb_attrs *, nsd+1); + if (sd_out == NULL) { + return ENOMEM; + } + + sdi = 0; + for (i = 0; i < nsd; i++) { + ret = sysdb_attrs_get_string(sd[i], AD_AT_TRUST_PARTNER, &sd_name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); + goto fail; + } + + if (strcasecmp(sd_name, domain->name) == 0) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "Not including primary domain %s in the subdomain list\n", + domain->name); + continue; + } + + sd_out[sdi] = talloc_steal(sd_out, sd[i]); + sdi++; } + /* Now include the root */ + sd_out[sdi] = talloc_steal(sd_out, root); + + *_nsd_out = sdi+1; + *_sd_out = sd_out; + return EOK; + +fail: + talloc_free(sd_out); return ret; } -static errno_t ad_subdom_reinit(struct ad_subdomains_ctx *ctx) +static errno_t +ads_store_sdap_subdom(struct ad_subdomains_ctx *ctx, + struct sss_domain_info *parent) +{ + int ret; + struct sdap_domain *sditer; + struct ad_id_ctx *subdom_id_ctx; + + ret = sdap_domain_subdom_add(ctx->sdap_id_ctx, ctx->sdom, parent); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sdap_domain_subdom_add failed.\n"); + return ret; + } + + DLIST_FOR_EACH(sditer, ctx->sdom) { + if (IS_SUBDOMAIN(sditer->dom) && sditer->pvt == NULL) { + ret = ad_subdom_ad_ctx_new(ctx->be_ctx, ctx->ad_id_ctx, + sditer->dom, &subdom_id_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ad_subdom_ad_ctx_new failed.\n"); + } else { + sditer->pvt = subdom_id_ctx; + } + } + } + + return EOK; +} + +static errno_t ad_subdom_reinit(struct ad_subdomains_ctx *subdoms_ctx) { + const char *path; errno_t ret; - ret = sss_write_krb5_conf_snippet( - dp_opt_get_string(ctx->ad_id_ctx->ad_options->basic, - AD_KRB5_CONFD_PATH)); + path = dp_opt_get_string(subdoms_ctx->ad_id_ctx->ad_options->basic, + AD_KRB5_CONFD_PATH); + + ret = sss_write_krb5_conf_snippet(path); if (ret != EOK) { DEBUG(SSSDBG_MINOR_FAILURE, "sss_write_krb5_conf_snippet failed.\n"); /* Just continue */ } - ret = sysdb_update_subdomains(ctx->be_ctx->domain); + ret = sysdb_update_subdomains(subdoms_ctx->be_ctx->domain); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_subdomains failed.\n"); return ret; } - ret = sss_write_domain_mappings(ctx->be_ctx->domain); + ret = sss_write_domain_mappings(subdoms_ctx->be_ctx->domain); if (ret != EOK) { DEBUG(SSSDBG_MINOR_FAILURE, "sss_krb5_write_mappings failed.\n"); /* Just continue */ } - ret = ads_store_sdap_subdom(ctx, ctx->be_ctx->domain); + ret = ads_store_sdap_subdom(subdoms_ctx, subdoms_ctx->be_ctx->domain); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "ads_store_sdap_subdom failed.\n"); return ret; @@ -494,364 +535,260 @@ static errno_t ad_subdom_reinit(struct ad_subdomains_ctx *ctx) return EOK; } -static void ad_subdomains_get_conn_done(struct tevent_req *req); -static void ad_subdomains_master_dom_done(struct tevent_req *req); -static errno_t ad_subdomains_get_root(struct ad_subdomains_req_ctx *ctx); -static errno_t ad_subdomains_get_slave(struct ad_subdomains_req_ctx *ctx); +struct ad_get_slave_domain_state { + struct tevent_context *ev; + struct ad_subdomains_ctx *sd_ctx; + struct be_ctx *be_ctx; + struct sdap_options *opts; + struct sdap_idmap_ctx *idmap_ctx; + struct sysdb_attrs *root_attrs; + struct sdap_id_op *sdap_op; +}; + +static errno_t ad_get_slave_domain_retry(struct tevent_req *req); +static void ad_get_slave_domain_connect_done(struct tevent_req *subreq); +static void ad_get_slave_domain_done(struct tevent_req *subreq); -static void ad_subdomains_retrieve(struct ad_subdomains_ctx *ctx, - struct be_req *be_req) +static struct tevent_req * +ad_get_slave_domain_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ad_subdomains_ctx *sd_ctx, + struct sysdb_attrs *root_attrs, + struct ad_id_ctx *root_id_ctx) { - struct ad_subdomains_req_ctx *req_ctx = NULL; + struct ad_get_slave_domain_state *state; struct tevent_req *req; - int dp_error = DP_ERR_FATAL; - int ret; + errno_t ret; - req_ctx = talloc_zero(be_req, struct ad_subdomains_req_ctx); - if (req_ctx == NULL) { - ret = ENOMEM; - goto done; + req = tevent_req_create(mem_ctx, &state, + struct ad_get_slave_domain_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } - req_ctx->be_req = be_req; - req_ctx->sd_ctx = ctx; - req_ctx->current_filter = NULL; - req_ctx->base_iter = 0; - req_ctx->root_base_iter = 0; - req_ctx->root_id_ctx = NULL; - req_ctx->root_op = NULL; - req_ctx->root_domain = NULL; - req_ctx->root_domain_attrs = NULL; - req_ctx->reply_count = 0; - req_ctx->reply = NULL; - - req_ctx->sdap_op = sdap_id_op_create(req_ctx, - ctx->ldap_ctx->conn_cache); - if (req_ctx->sdap_op == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed.\n"); + state->ev = ev; + state->sd_ctx = sd_ctx; + state->be_ctx = sd_ctx->be_ctx; + state->opts = root_id_ctx->sdap_id_ctx->opts; + state->idmap_ctx = root_id_ctx->sdap_id_ctx->opts->idmap_ctx; + state->root_attrs = root_attrs; + + state->sdap_op = sdap_id_op_create(state, root_id_ctx->ldap_ctx->conn_cache); + if (state->sdap_op == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create() failed\n"); ret = ENOMEM; - goto done; + goto immediately; } - req = sdap_id_op_connect_send(req_ctx->sdap_op, req_ctx, &ret); - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed: %d(%s).\n", - ret, strerror(ret)); - goto done; + ret = ad_get_slave_domain_retry(req); + if (ret == EAGAIN) { + /* asynchronous processing */ + return req; } - tevent_req_set_callback(req, ad_subdomains_get_conn_done, req_ctx); - - return; - -done: - talloc_free(req_ctx); +immediately: if (ret == EOK) { - dp_error = DP_ERR_OK; + tevent_req_done(req); + } else { + tevent_req_error(req, ret); } - be_req_terminate(be_req, dp_error, ret, NULL); + tevent_req_post(req, ev); + + return req; } -static void ad_subdomains_get_conn_done(struct tevent_req *req) +static errno_t ad_get_slave_domain_retry(struct tevent_req *req) { + struct ad_get_slave_domain_state *state; + struct tevent_req *subreq; int ret; - int dp_error = DP_ERR_FATAL; - struct ad_subdomains_req_ctx *ctx; - ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx); + state = tevent_req_data(req, struct ad_get_slave_domain_state); - ret = sdap_id_op_connect_recv(req, &dp_error); - talloc_zfree(req); - if (ret) { - if (dp_error == DP_ERR_OFFLINE) { - DEBUG(SSSDBG_MINOR_FAILURE, - "No AD server is available, cannot get the " - "subdomain list while offline\n"); - } else { - DEBUG(SSSDBG_OP_FAILURE, - "Failed to connect to AD server: [%d](%s)\n", - ret, strerror(ret)); - } - - goto fail; + subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_connect_send() failed " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; } - req = ad_master_domain_send(ctx, ctx->sd_ctx->be_ctx->ev, - ctx->sd_ctx->ldap_ctx, - ctx->sdap_op, - ctx->sd_ctx->domain_name); - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "ad_master_domain_send failed.\n"); - ret = ENOMEM; - goto fail; - } - tevent_req_set_callback(req, ad_subdomains_master_dom_done, ctx); - return; + tevent_req_set_callback(subreq, ad_get_slave_domain_connect_done, req); -fail: - be_req_terminate(ctx->be_req, dp_error, ret, NULL); + return EAGAIN; } -static void ad_subdomains_master_dom_done(struct tevent_req *req) +static void ad_get_slave_domain_connect_done(struct tevent_req *subreq) { - struct ad_subdomains_req_ctx *ctx; + struct ad_get_slave_domain_state *state; + struct tevent_req *req = NULL; + int dp_error; errno_t ret; - const char *realm; + const char *attrs[] = { AD_AT_FLATNAME, AD_AT_TRUST_PARTNER, + AD_AT_SID, AD_AT_TRUST_TYPE, + AD_AT_TRUST_ATTRS, NULL }; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_get_slave_domain_state); - ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx); + ret = sdap_id_op_connect_recv(subreq, &dp_error); + talloc_zfree(subreq); - ret = ad_master_domain_recv(req, ctx, - &ctx->flat_name, &ctx->master_sid, - &ctx->site, &ctx->forest); - talloc_zfree(req); if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Cannot retrieve master domain info\n"); - goto done; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to connect to LDAP " + "[%d]: %s\n", ret, sss_strerror(ret)); + if (dp_error == DP_ERR_OFFLINE) { + DEBUG(SSSDBG_MINOR_FAILURE, "No AD server is available, " + "cannot get the subdomain list while offline\n"); + ret = ERR_OFFLINE; + } + tevent_req_error(req, ret); + return; } - realm = dp_opt_get_cstring(ctx->sd_ctx->ad_id_ctx->ad_options->basic, - AD_KRB5_REALM); - if (realm == NULL) { - DEBUG(SSSDBG_CONF_SETTINGS, "Missing realm.\n"); - ret = EINVAL; - goto done; + subreq = sdap_search_bases_send(state, state->ev, state->opts, + sdap_id_op_handle(state->sdap_op), + state->opts->sdom->search_bases, + NULL, false, 0, + SLAVE_DOMAIN_FILTER, attrs); + if (subreq == NULL) { + tevent_req_error(req, ret); + return; } - ret = sysdb_master_domain_add_info(ctx->sd_ctx->be_ctx->domain, - realm, - ctx->flat_name, ctx->master_sid, - ctx->forest); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Cannot save master domain info\n"); - goto done; - } + tevent_req_set_callback(subreq, ad_get_slave_domain_done, req); + return; +} - if (ctx->forest == NULL || - strcasecmp(ctx->sd_ctx->be_ctx->domain->name, ctx->forest) != 0) { - DEBUG(SSSDBG_TRACE_FUNC, - "SSSD needs to look up the forest root domain\n"); - ret = ad_subdomains_get_root(ctx); - } else { - DEBUG(SSSDBG_TRACE_FUNC, - "Connected to forest root, looking up child domains..\n"); +static void ad_get_slave_domain_done(struct tevent_req *subreq) +{ + struct ad_get_slave_domain_state *state; + struct tevent_req *req; + struct sysdb_attrs **reply; + size_t reply_count; + struct sysdb_attrs **subdoms; + size_t nsubdoms; + bool has_changes; + int dp_error; + errno_t ret; - ctx->root_op = ctx->sdap_op; - ctx->root_id_ctx = ctx->sd_ctx->ad_id_ctx; + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_get_slave_domain_state); - ret = ad_subdomains_get_slave(ctx); + ret = sdap_search_bases_recv(subreq, state, &reply_count, &reply); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup slave domain data " + "[%d]: %s\n", ret, sss_strerror(ret)); + /* We continue to finish sdap_id_op. */ } - if (ret == EAGAIN) { + ret = sdap_id_op_done(state->sdap_op, ret, &dp_error); + if (dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + ret = ad_get_slave_domain_retry(req); + if (ret != EOK) { + goto done; + } return; + } else if (dp_error == DP_ERR_OFFLINE) { + ret = ERR_OFFLINE; + goto done; } else if (ret != EOK) { goto done; } -done: - be_req_terminate(ctx->be_req, DP_ERR_FATAL, ret, NULL); -} - -static void ad_subdomains_get_root_domain_done(struct tevent_req *req); + /* Based on whether we are connected to the forest root or not, we might + * need to exclude the subdomain we are connected to from the list of + * subdomains. + */ + ret = ad_subdomains_process(state, state->be_ctx->domain, + reply_count, reply, state->root_attrs, + &nsubdoms, &subdoms); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot process subdomain list\n"); + tevent_req_error(req, ret); + return; + } -static errno_t ad_subdomains_get_root(struct ad_subdomains_req_ctx *ctx) -{ - struct tevent_req *req; - struct sdap_search_base *base; - struct sdap_id_ctx *sdap_id_ctx; - char *filter; - const char *forest_root_attrs[] = { AD_AT_FLATNAME, AD_AT_TRUST_PARTNER, - AD_AT_SID, AD_AT_TRUST_TYPE, - AD_AT_TRUST_ATTRS, NULL }; - - sdap_id_ctx = ctx->sd_ctx->sdap_id_ctx; - base = sdap_id_ctx->opts->sdom->search_bases[ctx->root_base_iter]; - if (base == NULL) { - return EOK; + /* Got all the subdomains, let's process them. */ + ret = ad_subdomains_refresh(state->be_ctx, state->idmap_ctx, state->opts, + subdoms, nsubdoms, false, + &state->sd_ctx->last_refreshed, + &has_changes); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to refresh subdomains.\n"); + goto done; } - filter = talloc_asprintf(ctx, FOREST_ROOT_FILTER_FMT, ctx->forest); - if (filter == NULL) { - return ENOMEM; + DEBUG(SSSDBG_TRACE_LIBS, "There are %schanges\n", + has_changes ? "" : "no "); + + if (has_changes) { + ret = ad_subdom_reinit(state->sd_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not reinitialize subdomains\n"); + goto done; + } } - req = sdap_get_generic_send(ctx, ctx->sd_ctx->be_ctx->ev, - sdap_id_ctx->opts, - sdap_id_op_handle(ctx->sdap_op), - base->basedn, LDAP_SCOPE_SUBTREE, - filter, forest_root_attrs, - NULL, 0, - dp_opt_get_int(sdap_id_ctx->opts->basic, - SDAP_SEARCH_TIMEOUT), - false); + state->sd_ctx->last_refreshed = time(NULL); + ret = EOK; - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n"); - return ENOMEM; +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; } - tevent_req_set_callback(req, ad_subdomains_get_root_domain_done, ctx); - return EAGAIN; + tevent_req_done(req); } -static struct sss_domain_info *ads_get_root_domain(struct ad_subdomains_req_ctx *ctx); -static struct ad_id_ctx *ads_get_root_id_ctx(struct ad_subdomains_req_ctx *ctx); -static void ad_subdomains_root_conn_done(struct tevent_req *req); - -static void ad_subdomains_get_root_domain_done(struct tevent_req *req) +static errno_t ad_get_slave_domain_recv(struct tevent_req *req) { - int ret; - size_t reply_count; - struct sysdb_attrs **reply = NULL; - struct ad_subdomains_req_ctx *ctx; - int dp_error = DP_ERR_FATAL; - bool has_changes = false; - - ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx); - - ret = sdap_get_generic_recv(req, ctx, &reply_count, &reply); - talloc_zfree(req); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send request failed.\n"); - goto fail; - } - - if (reply_count == 0) { - /* If no root domain was found in the default search base, try the - * next one, if available - */ - ctx->root_base_iter++; - ret = ad_subdomains_get_root(ctx); - if (ret == EAGAIN) { - return; - } - - goto fail; - } else if (reply_count > 1) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Multiple results for root domain search, " - "domain list might be incomplete!\n"); - - ctx->root_op = ctx->sdap_op; - ctx->root_id_ctx = ctx->sd_ctx->ad_id_ctx; - - ret = ad_subdomains_get_slave(ctx); - if (ret == EAGAIN) { - return; - } - - goto fail; - } - /* Exactly one result, good. */ - - /* We won't use the operation to the local LDAP anymore, but - * read from the forest root - */ - ret = sdap_id_op_done(ctx->sdap_op, ret, &dp_error); - if (ret != EOK) { - if (dp_error == DP_ERR_OFFLINE) { - DEBUG(SSSDBG_MINOR_FAILURE, - "No AD server is available, cannot get the " - "subdomain list while offline\n"); - } else { - DEBUG(SSSDBG_OP_FAILURE, - "Failed to search the AD server: [%d](%s)\n", - ret, strerror(ret)); - } - goto fail; - } - - ret = ad_subdomains_refresh(ctx->sd_ctx, 1, true, reply, &has_changes); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "ad_subdomains_refresh failed.\n"); - goto fail; - } - - if (has_changes) { - ret = ad_subdom_reinit(ctx->sd_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Could not reinitialize subdomains\n"); - goto fail; - } - } - - ctx->root_domain_attrs = reply[0]; - ctx->root_domain = ads_get_root_domain(ctx); - if (ctx->root_domain == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "Could not find the root domain\n"); - ret = EFAULT; - goto fail; - } - - ctx->root_id_ctx = ads_get_root_id_ctx(ctx); - if (ctx->root_id_ctx == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "Cannot create id ctx for the root domain\n"); - ret = EFAULT; - goto fail; - } - - ctx->root_op = sdap_id_op_create(ctx, - ctx->root_id_ctx->ldap_ctx->conn_cache); - if (ctx->root_op == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed.\n"); - ret = ENOMEM; - goto fail; - } - - req = sdap_id_op_connect_send(ctx->root_op, ctx, &ret); - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed: %d(%s).\n", - ret, strerror(ret)); - goto fail; - } + TEVENT_REQ_RETURN_ON_ERROR(req); - tevent_req_set_callback(req, ad_subdomains_root_conn_done, ctx); - return; - -fail: - if (ret == EOK) { - ctx->sd_ctx->last_refreshed = time(NULL); - dp_error = DP_ERR_OK; - } - be_req_terminate(ctx->be_req, dp_error, ret, NULL); + return EOK; } -static struct sss_domain_info *ads_get_root_domain(struct ad_subdomains_req_ctx *ctx) +static struct sss_domain_info * +ads_get_root_domain(struct be_ctx *be_ctx, struct sysdb_attrs *attrs) { - errno_t ret; - const char *name; struct sss_domain_info *root; + const char *name; + errno_t ret; - ret = sysdb_attrs_get_string(ctx->root_domain_attrs, AD_AT_TRUST_PARTNER, &name); + ret = sysdb_attrs_get_string(attrs, AD_AT_TRUST_PARTNER, &name); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); return NULL; } /* With a subsequent run, the root should already be known */ - root = find_domain_by_name(ctx->sd_ctx->be_ctx->domain, - name, false); + root = find_domain_by_name(be_ctx->domain, name, false); return root; } -static struct ad_id_ctx *ads_get_root_id_ctx(struct ad_subdomains_req_ctx *ctx) +static struct ad_id_ctx * +ads_get_root_id_ctx(struct be_ctx *be_ctx, + struct ad_id_ctx *ad_id_ctx, + struct sss_domain_info *root_domain, + struct sdap_options *opts) { errno_t ret; struct sdap_domain *sdom; struct ad_id_ctx *root_id_ctx; - sdom = sdap_domain_get(ctx->sd_ctx->ad_id_ctx->sdap_id_ctx->opts, - ctx->root_domain); + sdom = sdap_domain_get(opts, root_domain); if (sdom == NULL) { DEBUG(SSSDBG_OP_FAILURE, - "Cannot get the sdom for %s!\n", ctx->root_domain->name); + "Cannot get the sdom for %s!\n", root_domain->name); return NULL; } if (sdom->pvt == NULL) { - ret = ad_subdom_ad_ctx_new(ctx->sd_ctx->be_ctx, - ctx->sd_ctx->ad_id_ctx, - ctx->root_domain, + ret = ad_subdom_ad_ctx_new(be_ctx, ad_id_ctx, root_domain, &root_id_ctx); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "ad_subdom_ad_ctx_new failed.\n"); @@ -867,362 +804,585 @@ static struct ad_id_ctx *ads_get_root_id_ctx(struct ad_subdomains_req_ctx *ctx) return root_id_ctx; } -static void ad_subdomains_root_conn_done(struct tevent_req *req) -{ - int ret; - int dp_error = DP_ERR_FATAL; - struct ad_subdomains_req_ctx *ctx; +struct ad_get_root_domain_state { + struct ad_subdomains_ctx *sd_ctx; + struct be_ctx *be_ctx; + struct sdap_idmap_ctx *idmap_ctx; + struct sdap_options *opts; + + struct ad_id_ctx *root_id_ctx; + struct sysdb_attrs *root_domain_attrs; +}; - ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx); +static void ad_get_root_domain_done(struct tevent_req *subreq); - ret = sdap_id_op_connect_recv(req, &dp_error); - talloc_zfree(req); - if (ret) { - be_mark_dom_offline(ctx->root_domain, be_req_get_be_ctx(ctx->be_req)); +static struct tevent_req * +ad_get_root_domain_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *forest, + struct sdap_handle *sh, + struct ad_subdomains_ctx *sd_ctx) +{ + struct ad_get_root_domain_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + struct sdap_options *opts; + errno_t ret; + const char *filter; + const char *attrs[] = { AD_AT_FLATNAME, AD_AT_TRUST_PARTNER, + AD_AT_SID, AD_AT_TRUST_TYPE, + AD_AT_TRUST_ATTRS, NULL }; - DEBUG(SSSDBG_OP_FAILURE, - "Failed to connect to AD server: [%d](%s)\n", - ret, strerror(ret)); - goto fail; + req = tevent_req_create(mem_ctx, &state, struct ad_get_root_domain_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } - ret = ad_subdomains_get_slave(ctx); - if (ret == EAGAIN) { - return; - } else if (ret != EOK) { - goto fail; + if (forest != NULL && strcasecmp(sd_ctx->be_ctx->domain->name, forest) == 0) { + state->root_id_ctx = sd_ctx->ad_id_ctx; + state->root_domain_attrs = NULL; + ret = EOK; + goto immediately; } -fail: - be_req_terminate(ctx->be_req, dp_error, ret, NULL); -} + DEBUG(SSSDBG_TRACE_FUNC, "Looking up the forest root domain.\n"); -static void ad_subdomains_get_slave_domain_done(struct tevent_req *req); + state->sd_ctx = sd_ctx; + state->opts = opts = sd_ctx->sdap_id_ctx->opts; + state->be_ctx = sd_ctx->be_ctx; + state->idmap_ctx = opts->idmap_ctx; -static errno_t ad_subdomains_get_slave(struct ad_subdomains_req_ctx *ctx) -{ - struct tevent_req *req; - struct sdap_search_base *base; - const char *slave_dom_attrs[] = { AD_AT_FLATNAME, AD_AT_TRUST_PARTNER, - AD_AT_SID, AD_AT_TRUST_TYPE, - AD_AT_TRUST_ATTRS, NULL }; + filter = talloc_asprintf(state, FOREST_ROOT_FILTER_FMT, forest); + if (filter == NULL) { + ret = ENOMEM; + goto immediately; + } - base = ctx->root_id_ctx->sdap_id_ctx->opts->sdom->search_bases[ctx->base_iter]; - if (base == NULL) { - return EOK; + subreq = sdap_search_bases_return_first_send(state, ev, opts, sh, + opts->sdom->search_bases, + NULL, false, 0, filter, attrs); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; } - req = sdap_get_generic_send(ctx, ctx->sd_ctx->be_ctx->ev, - ctx->root_id_ctx->sdap_id_ctx->opts, - sdap_id_op_handle(ctx->root_op), - base->basedn, LDAP_SCOPE_SUBTREE, - SLAVE_DOMAIN_FILTER, slave_dom_attrs, - NULL, 0, - dp_opt_get_int(ctx->root_id_ctx->sdap_id_ctx->opts->basic, - SDAP_SEARCH_TIMEOUT), - false); + tevent_req_set_callback(subreq, ad_get_root_domain_done, req); - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n"); - return ENOMEM; + return req; + +immediately: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); } + tevent_req_post(req, ev); - tevent_req_set_callback(req, ad_subdomains_get_slave_domain_done, ctx); - return EAGAIN; + return req; } -static errno_t ad_subdomains_process(TALLOC_CTX *mem_ctx, - struct sss_domain_info *domain, - size_t nsd, struct sysdb_attrs **sd, - struct sysdb_attrs *root, - size_t *_nsd_out, - struct sysdb_attrs ***_sd_out) +static void ad_get_root_domain_done(struct tevent_req *subreq) { - size_t i, sdi; - struct sysdb_attrs **sd_out; - const char *sd_name; + struct tevent_req *req; + struct ad_get_root_domain_state *state; + struct sysdb_attrs **reply; + struct sss_domain_info *root_domain; + size_t reply_count; + bool has_changes; errno_t ret; - if (root == NULL) { - /* We are connected directly to the root domain. The 'sd' - * list is complete and we can just use it - */ - *_nsd_out = nsd; - *_sd_out = sd; - return EOK; + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_get_root_domain_state); + + ret = sdap_search_bases_return_first_recv(subreq, state, &reply_count, + &reply); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to lookup forest root information " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; } - /* If we searched for root separately, we must: - * a) treat the root domain as a subdomain - * b) filter the subdomain we are connected to from the subdomain - * list, from our point of view, it's the master domain - */ - sd_out = talloc_zero_array(mem_ctx, struct sysdb_attrs *, nsd+1); - if (sd_out == NULL) { - return ENOMEM; + if (reply_count == 0) { + DEBUG(SSSDBG_OP_FAILURE, "No information provided for root domain\n"); + ret = ENOENT; + goto done; + } else if (reply_count > 1) { + DEBUG(SSSDBG_CRIT_FAILURE, "Multiple results for root domain search, " + "domain list might be incomplete!\n"); + ret = ERR_MALFORMED_ENTRY; + goto done; } - sdi = 0; - for (i = 0; i < nsd; i++) { - ret = sysdb_attrs_get_string(sd[i], AD_AT_TRUST_PARTNER, &sd_name); + ret = ad_subdomains_refresh(state->be_ctx, state->idmap_ctx, state->opts, + reply, reply_count, true, + &state->sd_ctx->last_refreshed, + &has_changes); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ad_subdomains_refresh failed [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + if (has_changes) { + ret = ad_subdom_reinit(state->sd_ctx); if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); - goto fail; + DEBUG(SSSDBG_OP_FAILURE, "Could not reinitialize subdomains\n"); + goto done; } + } - if (strcasecmp(sd_name, domain->name) == 0) { - DEBUG(SSSDBG_TRACE_INTERNAL, - "Not including primary domain %s in the subdomain list\n", - domain->name); - continue; - } + state->root_domain_attrs = reply[0]; + root_domain = ads_get_root_domain(state->be_ctx, reply[0]); + if (root_domain == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Could not find the root domain\n"); + ret = EFAULT; + goto done; + } - sd_out[sdi] = talloc_steal(sd_out, sd[i]); - sdi++; + state->root_id_ctx = ads_get_root_id_ctx(state->be_ctx, + state->sd_ctx->ad_id_ctx, + root_domain, state->opts); + if (state->root_id_ctx == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot create id ctx for the root domain\n"); + ret = EFAULT; + goto done; } - /* Now include the root */ - sd_out[sdi] = talloc_steal(sd_out, root); + ret = EOK; - *_nsd_out = sdi+1; - *_sd_out = sd_out; - return EOK; +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } -fail: - talloc_free(sd_out); - return ret; + tevent_req_done(req); } -static void ad_subdomains_get_slave_domain_done(struct tevent_req *req) +static errno_t ad_get_root_domain_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct sysdb_attrs **_attrs, + struct ad_id_ctx **_id_ctx) { - int ret; - size_t reply_count; - struct sysdb_attrs **reply = NULL; - struct ad_subdomains_req_ctx *ctx; - int dp_error = DP_ERR_FATAL; - bool refresh_has_changes = false; - size_t nsubdoms; - struct sysdb_attrs **subdoms; + struct ad_get_root_domain_state *state = NULL; + state = tevent_req_data(req, struct ad_get_root_domain_state); - ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx); + TEVENT_REQ_RETURN_ON_ERROR(req); - ret = sdap_get_generic_recv(req, ctx, &reply_count, &reply); - talloc_zfree(req); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send request failed.\n"); - goto done; + *_attrs = talloc_steal(mem_ctx, state->root_domain_attrs); + *_id_ctx = state->root_id_ctx; + + return EOK; +} + +struct ad_subdomains_refresh_state { + struct tevent_context *ev; + struct be_ctx *be_ctx; + struct ad_subdomains_ctx *sd_ctx; + struct sdap_id_op *sdap_op; + struct sdap_id_ctx *id_ctx; + struct ad_options *ad_options; +}; + +static errno_t ad_subdomains_refresh_retry(struct tevent_req *req); +static void ad_subdomains_refresh_connect_done(struct tevent_req *subreq); +static void ad_subdomains_refresh_master_done(struct tevent_req *subreq); +static void ad_subdomains_refresh_root_done(struct tevent_req *subreq); +static void ad_subdomains_refresh_done(struct tevent_req *subreq); + +static struct tevent_req * +ad_subdomains_refresh_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ad_subdomains_ctx *sd_ctx) +{ + struct ad_subdomains_refresh_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ad_subdomains_refresh_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } - if (reply_count) { - ctx->reply = talloc_realloc(ctx, ctx->reply, struct sysdb_attrs *, - ctx->reply_count + reply_count); - if (ctx->reply == NULL) { - ret = ENOMEM; - goto done; - } - memcpy(ctx->reply+ctx->reply_count, reply, - reply_count * sizeof(struct sysdb_attrs *)); - ctx->reply_count += reply_count; + state->ev = ev; + state->be_ctx = sd_ctx->be_ctx; + state->sd_ctx = sd_ctx; + state->id_ctx = sd_ctx->sdap_id_ctx; + state->ad_options = sd_ctx->ad_id_ctx->ad_options; + + state->sdap_op = sdap_id_op_create(state, + sd_ctx->sdap_id_ctx->conn->conn_cache); + if (state->sdap_op == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create() failed\n"); + ret = ENOMEM; + goto immediately; } - ctx->base_iter++; - ret = ad_subdomains_get_slave(ctx); + ret = ad_subdomains_refresh_retry(req); if (ret == EAGAIN) { - /* Search in progress */ - return; + /* asynchronous processing */ + 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_subdomains_refresh_retry(struct tevent_req *req) +{ + struct ad_subdomains_refresh_state *state; + struct tevent_req *subreq; + int ret; + + state = tevent_req_data(req, struct ad_subdomains_refresh_state); + + subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_connect_send() failed " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; } - ret = sdap_id_op_done(ctx->root_op, ret, &dp_error); + tevent_req_set_callback(subreq, ad_subdomains_refresh_connect_done, req); + + return EAGAIN; +} + +static void ad_subdomains_refresh_connect_done(struct tevent_req *subreq) +{ + struct ad_subdomains_refresh_state *state; + struct tevent_req *req; + int dp_error; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_subdomains_refresh_state); + + ret = sdap_id_op_connect_recv(subreq, &dp_error); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to connect to LDAP " + "[%d]: %s\n", ret, sss_strerror(ret)); if (dp_error == DP_ERR_OFFLINE) { - DEBUG(SSSDBG_MINOR_FAILURE, - "No AD server is available, cannot get the " - "subdomain list while offline\n"); - } else { - DEBUG(SSSDBG_OP_FAILURE, - "Failed to search the AD server: [%d](%s)\n", - ret, strerror(ret)); + DEBUG(SSSDBG_MINOR_FAILURE, "No AD server is available, " + "cannot get the subdomain list while offline\n"); + ret = ERR_OFFLINE; } tevent_req_error(req, ret); return; } - /* Based on whether we are connected to the forest root or not, we might - * need to exclude the subdomain we are connected to from the list of - * subdomains - */ - ret = ad_subdomains_process(ctx, ctx->sd_ctx->be_ctx->domain, - ctx->reply_count, ctx->reply, - ctx->root_domain_attrs, &nsubdoms, &subdoms); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Cannot process subdomain list\n"); - tevent_req_error(req, ret); + subreq = ad_master_domain_send(state, state->ev, state->id_ctx->conn, + state->sdap_op, state->sd_ctx->domain_name); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); return; } - /* Got all the subdomains, let's process them */ - ret = ad_subdomains_refresh(ctx->sd_ctx, nsubdoms, false, subdoms, - &refresh_has_changes); + tevent_req_set_callback(subreq, ad_subdomains_refresh_master_done, req); + return; +} + +static void ad_subdomains_refresh_master_done(struct tevent_req *subreq) +{ + struct ad_subdomains_refresh_state *state; + struct tevent_req *req; + const char *realm; + char *master_sid; + char *flat_name; + char *forest; + char *site; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_subdomains_refresh_state); + + ret = ad_master_domain_recv(subreq, state, &flat_name, &master_sid, + &site, &forest); + talloc_zfree(subreq); if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Failed to refresh subdomains.\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get master domain information " + "[%d]: %s\n", ret, sss_strerror(ret)); goto done; } - DEBUG(SSSDBG_TRACE_LIBS, "There are %schanges\n", - refresh_has_changes ? "" : "no "); + realm = dp_opt_get_cstring(state->ad_options->basic, AD_KRB5_REALM); + if (realm == NULL) { + DEBUG(SSSDBG_CONF_SETTINGS, "Missing realm.\n"); + ret = EINVAL; + goto done; + } - if (refresh_has_changes) { - ret = ad_subdom_reinit(ctx->sd_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Could not reinitialize subdomains\n"); - goto done; - } + ret = sysdb_master_domain_add_info(state->be_ctx->domain, realm, + flat_name, master_sid, forest); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot save master domain info [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; } - ret = EOK; + subreq = ad_get_root_domain_send(state, state->ev, forest, + sdap_id_op_handle(state->sdap_op), + state->sd_ctx); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ad_subdomains_refresh_root_done, req); + return; + done: - if (ret == EOK) { - ctx->sd_ctx->last_refreshed = time(NULL); - dp_error = DP_ERR_OK; + if (ret != EOK) { + tevent_req_error(req, ret); + return; } - be_req_terminate(ctx->be_req, dp_error, ret, NULL); + + tevent_req_done(req); } -static void ad_subdom_online_cb(void *pvt); +static void ad_subdomains_refresh_root_done(struct tevent_req *subreq) +{ + struct ad_subdomains_refresh_state *state; + struct tevent_req *req; + struct ad_id_ctx *root_id_ctx; + struct sysdb_attrs *root_attrs; + int dp_error; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_subdomains_refresh_state); + + ret = ad_get_root_domain_recv(state, subreq, &root_attrs, &root_id_ctx); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get forest root [%d]: %s\n", + ret, sss_strerror(ret)); + root_attrs = NULL; + root_id_ctx = NULL; + /* We continue to finish sdap_id_op. */ + } + + /* We finish sdap_id_op here since we connect + * to forest root for slave domains. */ + ret = sdap_id_op_done(state->sdap_op, ret, &dp_error); + if (dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + ret = ad_subdomains_refresh_retry(req); + if (ret != EOK) { + tevent_req_error(req, ret); + } + return; + } else if (dp_error == DP_ERR_OFFLINE) { + tevent_req_error(req, ERR_OFFLINE); + return; + } else if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + subreq = ad_get_slave_domain_send(state, state->ev, state->sd_ctx, + root_attrs, root_id_ctx); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + tevent_req_set_callback(subreq, ad_subdomains_refresh_done, req); + return; +} -static void ad_subdom_timer_refresh(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval current_time, - void *pvt) +static void ad_subdomains_refresh_done(struct tevent_req *subreq) { - ad_subdom_online_cb(pvt); + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + + ret = ad_get_slave_domain_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to get subdomains [%d]: %s\n", + ret, sss_strerror(ret)); + } + + if (ret != EOK) { + DEBUG(SSSDBG_TRACE_FUNC, "Unable to refresh subdomains [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Subdomains refreshed.\n"); + tevent_req_done(req); } -static void ad_subdom_be_req_callback(struct be_req *be_req, - int dp_err, int dp_ret, - const char *errstr) +static errno_t ad_subdomains_refresh_recv(struct tevent_req *req) { - talloc_free(be_req); + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; } -static void ad_subdom_online_cb(void *pvt) +struct ad_subdomains_handler_state { + struct dp_reply_std reply; +}; + +static void ad_subdomains_handler_done(struct tevent_req *subreq); + +static struct tevent_req * +ad_subdomains_handler_send(TALLOC_CTX *mem_ctx, + struct ad_subdomains_ctx *sd_ctx, + struct dp_subdomains_data *data, + struct dp_req_params *params) { - struct ad_subdomains_ctx *ctx; - struct be_req *be_req; - struct timeval tv; - uint32_t refresh_interval; - - ctx = talloc_get_type(pvt, struct ad_subdomains_ctx); - if (!ctx) { - DEBUG(SSSDBG_CRIT_FAILURE, "Bad private pointer\n"); - return; + struct ad_subdomains_handler_state *state; + struct tevent_req *req; + struct tevent_req *subreq; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ad_subdomains_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } - refresh_interval = ctx->be_ctx->domain->subdomain_refresh_interval; - be_req = be_req_create(ctx, NULL, ctx->be_ctx, "AD subdomains", - ad_subdom_be_req_callback, NULL); - if (be_req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "be_req_create() failed.\n"); - return; + if (sd_ctx->last_refreshed > time(NULL) - AD_SUBDOMAIN_REFRESH_LIMIT) { + DEBUG(SSSDBG_TRACE_FUNC, "Subdomains were recently refreshed, " + "nothing to do\n"); + ret = EOK; + goto immediately; } - ad_subdomains_retrieve(ctx, be_req); - - tv = tevent_timeval_current_ofs(refresh_interval, 0); - ctx->timer_event = tevent_add_timer(ctx->be_ctx->ev, ctx, tv, - ad_subdom_timer_refresh, ctx); - if (!ctx->timer_event) { - DEBUG(SSSDBG_MINOR_FAILURE, "Failed to add subdom timer event\n"); + subreq = ad_subdomains_refresh_send(state, params->ev, sd_ctx); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; } + + tevent_req_set_callback(subreq, ad_subdomains_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 ad_subdom_offline_cb(void *pvt) +static void ad_subdomains_handler_done(struct tevent_req *subreq) { - struct ad_subdomains_ctx *ctx; + struct ad_subdomains_handler_state *state; + struct tevent_req *req; + errno_t ret; - ctx = talloc_get_type(pvt, struct ad_subdomains_ctx); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_subdomains_handler_state); - if (ctx) { - talloc_zfree(ctx->timer_event); - } + ret = ad_subdomains_refresh_recv(subreq); + talloc_zfree(subreq); + + /* TODO For backward compatibility we always return EOK to DP now. */ + dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ret, NULL); + tevent_req_done(req); } -void ad_subdomains_handler(struct be_req *be_req) +static errno_t ad_subdomains_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data) { - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - struct ad_subdomains_ctx *ctx; - time_t now; - - ctx = talloc_get_type(be_ctx->bet_info[BET_SUBDOMAINS].pvt_bet_data, - struct ad_subdomains_ctx); - if (!ctx) { - be_req_terminate(be_req, DP_ERR_FATAL, EINVAL, NULL); - return; - } + struct ad_subdomains_handler_state *state; - now = time(NULL); + state = tevent_req_data(req, struct ad_subdomains_handler_state); - if (ctx->last_refreshed > now - AD_SUBDOMAIN_REFRESH_LIMIT) { - be_req_terminate(be_req, DP_ERR_OK, EOK, NULL); - return; - } + TEVENT_REQ_RETURN_ON_ERROR(req); + + *data = state->reply; - ad_subdomains_retrieve(ctx, be_req); + return EOK; } -struct bet_ops ad_subdomains_ops = { - .handler = ad_subdomains_handler, - .finalize = NULL -}; +static struct tevent_req * +ad_subdomains_ptask_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct be_ptask *be_ptask, + void *pvt) +{ + struct ad_subdomains_ctx *sd_ctx; + sd_ctx = talloc_get_type(pvt, struct ad_subdomains_ctx); -int ad_subdom_init(struct be_ctx *be_ctx, - struct ad_id_ctx *id_ctx, - const char *ad_domain, - struct bet_ops **ops, - void **pvt_data) + return ad_subdomains_refresh_send(mem_ctx, ev, sd_ctx); +} + +static errno_t +ad_subdomains_ptask_recv(struct tevent_req *req) { - struct ad_subdomains_ctx *ctx; - int ret; - enum idmap_error_code err; + return ad_subdomains_refresh_recv(req); +} - ctx = talloc_zero(id_ctx, struct ad_subdomains_ctx); - if (ctx == NULL) { +errno_t ad_subdomains_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ad_id_ctx *ad_id_ctx, + struct dp_method *dp_methods) +{ + struct ad_subdomains_ctx *sd_ctx; + const char *ad_domain; + time_t period; + errno_t ret; + + ad_domain = dp_opt_get_string(ad_id_ctx->ad_options->basic, AD_DOMAIN); + + sd_ctx = talloc_zero(mem_ctx, struct ad_subdomains_ctx); + if (sd_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); return ENOMEM; } - ctx->be_ctx = be_ctx; - ctx->sdom = id_ctx->sdap_id_ctx->opts->sdom; - ctx->ldap_ctx = id_ctx->ldap_ctx; - ctx->sdap_id_ctx = id_ctx->sdap_id_ctx; - ctx->domain_name = talloc_strdup(ctx, ad_domain); - if (ctx->domain_name == NULL) { + sd_ctx->be_ctx = be_ctx; + sd_ctx->sdom = ad_id_ctx->sdap_id_ctx->opts->sdom; + sd_ctx->sdap_id_ctx = ad_id_ctx->sdap_id_ctx; + sd_ctx->domain_name = talloc_strdup(sd_ctx, ad_domain); + if (sd_ctx->domain_name == NULL) { DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); return ENOMEM; } - ctx->ad_id_ctx = id_ctx; - *ops = &ad_subdomains_ops; - *pvt_data = ctx; + sd_ctx->ad_id_ctx = ad_id_ctx; - ret = be_add_online_cb(ctx, be_ctx, ad_subdom_online_cb, ctx, NULL); - if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, "Failed to add subdom online callback\n"); - } + dp_set_method(dp_methods, DPM_DOMAINS_HANDLER, + ad_subdomains_handler_send, ad_subdomains_handler_recv, sd_ctx, + struct ad_subdomains_ctx, struct dp_subdomains_data, struct dp_reply_std); - ret = be_add_offline_cb(ctx, be_ctx, ad_subdom_offline_cb, ctx, NULL); + period = be_ctx->domain->subdomain_refresh_interval; + ret = be_ptask_create(sd_ctx, be_ctx, period, 0, 0, 0, period, + BE_PTASK_OFFLINE_DISABLE, 0, + ad_subdomains_ptask_send, ad_subdomains_ptask_recv, sd_ctx, + "Subdomains Refresh", NULL); if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, "Failed to add subdom offline callback\n"); - } - - err = sss_idmap_init(sss_idmap_talloc, ctx, sss_idmap_talloc_free, - &ctx->idmap_ctx); - if (err != IDMAP_SUCCESS) { - DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize idmap context.\n"); - return EFAULT; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup ptask " + "[%d]: %s\n", ret, sss_strerror(ret)); + /* Ignore, responders will trigger refresh from time to time. */ } - ret = ad_subdom_reinit(ctx); + ret = ad_subdom_reinit(sd_ctx); if (ret != EOK) { DEBUG(SSSDBG_MINOR_FAILURE, "Could not reinitialize subdomains. " "Users from trusted domains might not be resolved correctly\n"); diff --git a/src/providers/ad/ad_subdomains.h b/src/providers/ad/ad_subdomains.h index b4b0e765d..adc286b2b 100644 --- a/src/providers/ad/ad_subdomains.h +++ b/src/providers/ad/ad_subdomains.h @@ -28,10 +28,9 @@ #include "providers/backend.h" #include "providers/ad/ad_common.h" -int ad_subdom_init(struct be_ctx *be_ctx, - struct ad_id_ctx *id_ctx, - const char *ad_domain, - struct bet_ops **ops, - void **pvt_data); +errno_t ad_subdomains_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ad_id_ctx *ad_id_ctx, + struct dp_method *dp_methods); #endif /* _AD_SUBDOMAINS_H_ */ diff --git a/src/providers/ad/ad_sudo.c b/src/providers/ad/ad_sudo.c index 53ce5af59..026eab1fd 100644 --- a/src/providers/ad/ad_sudo.c +++ b/src/providers/ad/ad_sudo.c @@ -25,21 +25,21 @@ #include "providers/ad/ad_common.h" #include "providers/ldap/sdap_sudo.h" -int ad_sudo_init(struct be_ctx *be_ctx, - struct ad_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data) +errno_t ad_sudo_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx, + struct dp_method *dp_methods) { - int ret; + errno_t ret; struct ad_options *ad_options; struct sdap_options *ldap_options; DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing sudo AD back end\n"); - ret = sdap_sudo_init(be_ctx, id_ctx->sdap_id_ctx, ops, pvt_data); + ret = sdap_sudo_init(mem_ctx, be_ctx, id_ctx->sdap_id_ctx, dp_methods); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize LDAP SUDO [%d]: %s\n", - ret, strerror(ret)); + ret, sss_strerror(ret)); return ret; } diff --git a/src/providers/backend.h b/src/providers/backend.h index e91ed9c3e..739e935fc 100644 --- a/src/providers/backend.h +++ b/src/providers/backend.h @@ -35,49 +35,9 @@ #define BE_SRV_IDENTIFIER "_srv_" struct be_ctx; -struct bet_ops; -struct be_req; - -typedef int (*bet_init_fn_t)(TALLOC_CTX *, struct bet_ops **, void **); -typedef void (*be_shutdown_fn)(void *); -typedef void (*be_req_fn_t)(struct be_req *); -typedef void (*be_async_callback_t)(struct be_req *, int, int, const char *); typedef void (*be_callback_t)(void *); -enum bet_type { - BET_NULL = 0, - BET_ID, - BET_AUTH, - BET_ACCESS, - BET_CHPASS, - BET_SUDO, - BET_AUTOFS, - BET_SELINUX, - BET_HOSTID, - BET_SUBDOMAINS, - BET_MAX -}; - -struct bet_data { - enum bet_type bet_type; - const char *option_name; - const char *mod_init_fn_name_fmt; -}; - -struct loaded_be { - char *be_name; - void *handle; -}; - -struct bet_info { - enum bet_type bet_type; - struct bet_ops *bet_ops; - void *pvt_bet_data; - char *mod_name; - struct bet_queue_item *req_queue; -}; - struct be_offline_status { time_t went_offline; bool offline; @@ -90,13 +50,6 @@ struct be_resolv_ctx { enum restrict_family family_order; }; -struct be_client { - struct be_ctx *bectx; - struct sbus_connection *conn; - struct tevent_timer *timeout; - bool initialized; -}; - struct be_failover_ctx; struct be_cb; @@ -130,35 +83,14 @@ struct be_ctx { struct be_ptask *check_if_online_ptask; struct sbus_connection *mon_conn; - struct sbus_connection *sbus_srv; - - struct be_client *nss_cli; - struct be_client *pam_cli; - struct be_client *sudo_cli; - struct be_client *autofs_cli; - struct be_client *ssh_cli; - struct be_client *pac_cli; - struct be_client *ifp_cli; - - struct loaded_be loaded_be[BET_MAX]; - struct bet_info bet_info[BET_MAX]; struct be_refresh_ctx *refresh_ctx; size_t check_online_ref_count; - /* List of ongoing requests */ - struct be_req *active_requests; - struct data_provider *provider; }; -struct bet_ops { - be_req_fn_t check_online; - be_req_fn_t handler; - be_req_fn_t finalize; -}; - struct be_acct_req { int entry_type; int attr_type; @@ -168,28 +100,6 @@ struct be_acct_req { char *domain; }; -struct be_sudo_req { - uint32_t type; - char **rules; -}; - -struct be_autofs_req { - char *mapname; - bool invalidate; -}; - -struct be_subdom_req { - bool force; - char *domain_hint; -}; - -struct be_host_req { - uint32_t type; - int filter_type; - char *name; - char *alias; -}; - bool be_is_offline(struct be_ctx *ctx); void be_mark_offline(struct be_ctx *ctx); void be_mark_dom_offline(struct sss_domain_info *dom, struct be_ctx *ctx); @@ -295,40 +205,4 @@ const char *be_fo_get_active_server_name(struct be_ctx *ctx, errno_t be_res_init(struct be_ctx *ctx); -/* be_req helpers */ - -/* Create a back end request and call fn when done. Please note the - * request name is not duplicated. The caller should either provide - * a static string or steal a dynamic string onto req context. - */ -struct be_req *be_req_create(TALLOC_CTX *mem_ctx, - struct be_client *becli, - struct be_ctx *be_ctx, - const char *name, - be_async_callback_t fn, - void *pvt_fn_data); -struct be_ctx *be_req_get_be_ctx(struct be_req *be_req); - -void *be_req_get_data(struct be_req *be_req); - -void be_req_terminate(struct be_req *be_req, - int dp_err_type, int errnum, const char *errstr); - -void be_terminate_domain_requests(struct be_ctx *be_ctx, - const char *domain); - -/* Request account information */ -struct tevent_req * -be_get_account_info_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct be_client *becli, - struct be_ctx *be_ctx, - struct be_acct_req *ar); - -errno_t be_get_account_info_recv(struct tevent_req *req, - TALLOC_CTX *mem_ctx, - int *_err_maj, - int *_err_min, - const char **_err_msg); - #endif /* __DP_BACKEND_H___ */ diff --git a/src/providers/data_provider/dp_custom_data.h b/src/providers/data_provider/dp_custom_data.h index 298f91de4..decc0f4e8 100644 --- a/src/providers/data_provider/dp_custom_data.h +++ b/src/providers/data_provider/dp_custom_data.h @@ -25,6 +25,36 @@ /* Request handler private data. */ +struct dp_sudo_data { + uint32_t type; + char **rules; +}; + +struct dp_hostid_data { + const char *name; + const char *alias; +}; + +struct dp_autofs_data { + const char *mapname; +}; + +struct dp_subdomains_data { + const char *domain_hint; +}; + +/* TODO rename be_acct_req to dp_id_data to be consistent + This can be done after the original code is removed. +struct dp_id_data { + uint32_t entry_type; + uint32_t attr_type; + uint32_t filter_type; + const char *filter_value; + const char *extra_value; + const char *domain; +}; +*/ + /* Reply private data. */ struct dp_reply_std { diff --git a/src/providers/data_provider/dp_iface.c b/src/providers/data_provider/dp_iface.c index 5eaf7e2a3..6a6ca6769 100644 --- a/src/providers/data_provider/dp_iface.c +++ b/src/providers/data_provider/dp_iface.c @@ -28,12 +28,12 @@ struct iface_dp iface_dp = { {&iface_dp_meta, 0}, - .pamHandler = NULL, - .sudoHandler = NULL, - .autofsHandler = NULL, - .hostHandler = NULL, - .getDomains = NULL, - .getAccountInfo = NULL + .pamHandler = dp_pam_handler, + .sudoHandler = dp_sudo_handler, + .autofsHandler = dp_autofs_handler, + .hostHandler = dp_host_handler, + .getDomains = dp_subdomains_handler, + .getAccountInfo = dp_get_account_info_handler }; static struct sbus_iface_map dp_map[] = { diff --git a/src/providers/data_provider/dp_iface.h b/src/providers/data_provider/dp_iface.h index 2991bf7aa..eeef220e3 100644 --- a/src/providers/data_provider/dp_iface.h +++ b/src/providers/data_provider/dp_iface.h @@ -31,4 +31,32 @@ errno_t dp_register_sbus_interface(struct sbus_connection *conn, struct dp_client *pvt); +errno_t dp_get_account_info_handler(struct sbus_request *sbus_req, + void *dp_cli, + uint32_t dp_flags, + uint32_t entry_type, + uint32_t attr_type, + const char *filter, + const char *domain, + const char *extra); + +errno_t dp_pam_handler(struct sbus_request *sbus_req, void *dp_cli); + +errno_t dp_sudo_handler(struct sbus_request *sbus_req, void *dp_cli); + +errno_t dp_host_handler(struct sbus_request *sbus_req, + void *dp_cli, + uint32_t dp_flags, + const char *name, + const char *alias); + +errno_t dp_autofs_handler(struct sbus_request *sbus_req, + void *dp_cli, + uint32_t dp_flags, + const char *mapname); + +errno_t dp_subdomains_handler(struct sbus_request *sbus_req, + void *dp_cli, + const char *domain_hint); + #endif /* DP_IFACE_H_ */ diff --git a/src/providers/data_provider/dp_target_auth.c b/src/providers/data_provider/dp_target_auth.c new file mode 100644 index 000000000..78c4cce7e --- /dev/null +++ b/src/providers/data_provider/dp_target_auth.c @@ -0,0 +1,302 @@ +/* + Authors: + Pavel Březina + + Copyright (C) 2016 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 +#include + +#include "sbus/sssd_dbus.h" +#include "providers/data_provider/dp_private.h" +#include "providers/data_provider/dp_iface.h" +#include "providers/backend.h" +#include "util/util.h" + +static void dp_pam_reply(struct sbus_request *sbus_req, + const char *request_name, + struct pam_data *pd) +{ + DBusMessage *reply; + dbus_bool_t dbret; + + DP_REQ_DEBUG(SSSDBG_TRACE_LIBS, request_name, + "Sending result [%d][%s]", pd->pam_status, pd->domain); + + reply = dbus_message_new_method_return(sbus_req->message); + if (reply == NULL) { + DP_REQ_DEBUG(SSSDBG_TRACE_LIBS, request_name, + "Unable to acquire reply message"); + return; + } + + dbret = dp_pack_pam_response(reply, pd); + if (!dbret) { + DP_REQ_DEBUG(SSSDBG_TRACE_LIBS, request_name, + "Unable to generate reply message"); + dbus_message_unref(reply); + return; + } + + sbus_request_finish(sbus_req, reply); + dbus_message_unref(reply); + return; +} + +static errno_t pam_data_create(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct be_ctx *be_ctx, + struct pam_data **_pd) +{ + DBusError dbus_error; + struct pam_data *pd; + bool bret; + + dbus_error_init(&dbus_error); + bret = dp_unpack_pam_request(sbus_req->message, mem_ctx, &pd, &dbus_error); + if (bret == false) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse message!\n"); + return EINVAL; + } + + pd->pam_status = PAM_SYSTEM_ERR; + if (pd->domain == NULL) { + pd->domain = talloc_strdup(pd, be_ctx->domain->name); + if (pd->domain == NULL) { + talloc_free(pd); + return ENOMEM; + } + } + + *_pd = pd; + + return EOK; +} + +static void choose_target(struct data_provider *provider, + struct pam_data *pd, + enum dp_targets *_target, + enum dp_methods *_method, + const char **_req_name) +{ + enum dp_targets target; + enum dp_methods method; + const char *name; + + switch (pd->cmd) { + case SSS_PAM_AUTHENTICATE: + target = DPT_AUTH; + method = DPM_AUTH_HANDLER; + name = "PAM Authenticate"; + break; + case SSS_PAM_PREAUTH: + target = DPT_AUTH; + method = DPM_AUTH_HANDLER; + name = "PAM Preauth"; + break; + case SSS_PAM_ACCT_MGMT: + target = DPT_ACCESS; + method = DPM_ACCESS_HANDLER; + name = "PAM Account"; + break; + case SSS_PAM_CHAUTHTOK_PRELIM: + target = DPT_CHPASS; + method = DPM_AUTH_HANDLER; + name = "PAM Chpass 1st"; + break; + case SSS_PAM_CHAUTHTOK: + target = DPT_CHPASS; + method = DPM_AUTH_HANDLER; + name = "PAM Chpass 2nd"; + break; + case SSS_PAM_OPEN_SESSION: + target = DP_TARGET_SENTINEL; + method = DP_METHOD_SENTINEL; + name = "PAM Open Session"; + pd->pam_status = PAM_SUCCESS; + break; + case SSS_PAM_SETCRED: + target = DP_TARGET_SENTINEL; + method = DP_METHOD_SENTINEL; + name = "PAM Set Credentials"; + pd->pam_status = PAM_SUCCESS; + break; + case SSS_PAM_CLOSE_SESSION: + target = DP_TARGET_SENTINEL; + method = DP_METHOD_SENTINEL; + name = "PAM Close Session"; + pd->pam_status = PAM_SUCCESS; + break; + default: + DEBUG(SSSDBG_TRACE_LIBS, "Unsupported PAM command [%d].\n", + pd->cmd); + target = DP_TARGET_SENTINEL; + method = DP_METHOD_SENTINEL; + name = "PAM Unsupported"; + pd->pam_status = PAM_MODULE_UNKNOWN; + break; + } + + /* Check that target is configured. */ + if (target != DP_TARGET_SENTINEL + && !dp_target_enabled(provider, NULL, target)) { + target = DP_TARGET_SENTINEL; + method = DP_METHOD_SENTINEL; + pd->pam_status = PAM_MODULE_UNKNOWN; + } + + *_target = target; + *_method = method; + *_req_name = name; +} + +struct dp_pam_handler_state { + struct data_provider *provider; + struct dp_client *dp_cli; + struct sbus_request *sbus_req; + const char *request_name; +}; + +void dp_pam_handler_step_done(struct tevent_req *req); +void dp_pam_handler_selinux_done(struct tevent_req *req); + +errno_t dp_pam_handler(struct sbus_request *sbus_req, void *sbus_data) +{ + struct dp_pam_handler_state *state; + struct data_provider *provider; + struct pam_data *pd = NULL; + struct dp_client *dp_cli; + enum dp_targets target; + enum dp_methods method; + const char *req_name; + struct tevent_req *req; + errno_t ret; + + dp_cli = talloc_get_type(sbus_data, struct dp_client); + provider = dp_client_provider(dp_cli); + + state = talloc_zero(sbus_req, struct dp_pam_handler_state); + if (state == NULL) { + ret = ENOMEM; + goto done; + } + + ret = pam_data_create(state, sbus_req, provider->be_ctx, &pd); + if (ret != EOK) { + return ret; + } + + state->provider = provider; + state->dp_cli = dp_cli; + state->sbus_req = sbus_req; + + DEBUG(SSSDBG_CONF_SETTINGS, "Got request with the following data\n"); + DEBUG_PAM_DATA(SSSDBG_CONF_SETTINGS, pd); + + choose_target(provider, pd, &target, &method, &req_name); + if (target == DP_TARGET_SENTINEL) { + /* Just send the result. Pam data are freed with this call. */ + dp_pam_reply(sbus_req, req_name, pd); + return EOK; + } + + req = dp_req_send(state, provider, dp_cli, pd->domain, req_name, + target, method, 0, pd, &state->request_name); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(req, dp_pam_handler_step_done, state); + +done: + if (ret != EOK) { + talloc_free(pd); + } + + return ret; +} + +static bool should_invoke_selinux(struct data_provider *provider, + struct pam_data *pd) +{ + if (!dp_method_enabled(provider, DPT_SELINUX, DPM_SELINUX_HANDLER)) { + return false; + } + + if (pd->cmd == SSS_PAM_ACCT_MGMT && pd->pam_status == PAM_SUCCESS) { + return true; + } + + return false; +} + +void dp_pam_handler_step_done(struct tevent_req *req) +{ + struct dp_pam_handler_state *state; + struct pam_data *pd; + errno_t ret; + + state = tevent_req_callback_data(req, struct dp_pam_handler_state); + + ret = dp_req_recv(state, req, struct pam_data *, &pd); + talloc_zfree(req); + if (ret != EOK) { + dp_req_reply_error(state->sbus_req, state->request_name, ret); + return; + } + + if (!should_invoke_selinux(state->provider, pd)) { + /* State and request related data are freed with sbus_req. */ + dp_pam_reply(state->sbus_req, state->request_name, pd); + return; + } + + req = dp_req_send(state, state->provider, state->dp_cli, pd->domain, + "PAM SELinux", DPT_SELINUX, DPM_SELINUX_HANDLER, + 0, pd, NULL); + if (req == NULL) { + DP_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->request_name, + "Unable to process SELinux, killing request..."); + talloc_free(state->sbus_req); + return; + } + + tevent_req_set_callback(req, dp_pam_handler_selinux_done, state); +} + +void dp_pam_handler_selinux_done(struct tevent_req *req) +{ + struct dp_pam_handler_state *state; + struct pam_data *pd; + errno_t ret; + + state = tevent_req_callback_data(req, struct dp_pam_handler_state); + + ret = dp_req_recv(state, req, struct pam_data *, &pd); + talloc_zfree(req); + if (ret != EOK) { + dp_req_reply_error(state->sbus_req, state->request_name, ret); + return; + } + + /* State and request related data are freed with sbus_req. */ + dp_pam_reply(state->sbus_req, state->request_name, pd); + return; +} diff --git a/src/providers/data_provider/dp_target_autofs.c b/src/providers/data_provider/dp_target_autofs.c new file mode 100644 index 000000000..13b12f5dd --- /dev/null +++ b/src/providers/data_provider/dp_target_autofs.c @@ -0,0 +1,55 @@ +/* + Authors: + Pavel Březina + + Copyright (C) 2016 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 "sbus/sssd_dbus.h" +#include "providers/data_provider/dp_private.h" +#include "providers/data_provider/dp_iface.h" +#include "providers/backend.h" +#include "util/util.h" + +errno_t dp_autofs_handler(struct sbus_request *sbus_req, + void *dp_cli, + uint32_t dp_flags, + const char *mapname) +{ + struct dp_autofs_data *data; + const char *key; + + if (mapname == NULL) { + return EINVAL; + } + + data = talloc_zero(sbus_req, struct dp_autofs_data); + if (data == NULL) { + return ENOMEM; + } + + data->mapname = mapname; + key = mapname; + + dp_req_with_reply(dp_cli, NULL, "AutoFS", key, sbus_req, DPT_AUTOFS, + DPM_AUTOFS_HANDLER, dp_flags, data, + dp_req_reply_std, struct dp_reply_std); + + return EOK; +} diff --git a/src/providers/data_provider/dp_target_hostid.c b/src/providers/data_provider/dp_target_hostid.c new file mode 100644 index 000000000..a47601f0b --- /dev/null +++ b/src/providers/data_provider/dp_target_hostid.c @@ -0,0 +1,63 @@ +/* + Authors: + Pavel Březina + + Copyright (C) 2016 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 "sbus/sssd_dbus.h" +#include "providers/data_provider/dp_private.h" +#include "providers/data_provider/dp_iface.h" +#include "providers/backend.h" +#include "util/util.h" + +errno_t dp_host_handler(struct sbus_request *sbus_req, + void *dp_cli, + uint32_t dp_flags, + const char *name, + const char *alias) +{ + struct dp_hostid_data *data; + const char *key; + + if (name == NULL) { + return EINVAL; + } + + data = talloc_zero(sbus_req, struct dp_hostid_data); + if (data == NULL) { + return ENOMEM; + } + + data->name = name; + data->alias = alias[0] == '\0' ? NULL : alias; + + key = talloc_asprintf(data, "%s:%s", name, + (data->alias == NULL ? "(null)" : data->alias)); + if (key == NULL) { + talloc_free(data); + return ENOMEM; + } + + dp_req_with_reply(dp_cli, NULL, "HostID", key, sbus_req, DPT_HOSTID, + DPM_HOSTID_HANDLER, dp_flags, data, + dp_req_reply_std, struct dp_reply_std); + + return EOK; +} diff --git a/src/providers/data_provider/dp_target_id.c b/src/providers/data_provider/dp_target_id.c new file mode 100644 index 000000000..855bcd895 --- /dev/null +++ b/src/providers/data_provider/dp_target_id.c @@ -0,0 +1,311 @@ +/* + Authors: + Pavel Březina + + Copyright (C) 2016 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 "sbus/sssd_dbus.h" +#include "providers/data_provider/dp_private.h" +#include "providers/data_provider/dp_iface.h" +#include "providers/backend.h" +#include "util/util.h" + +#define FILTER_TYPE(str, type) {str "=", sizeof(str "=") - 1, type} + +static bool check_attr_type(uint32_t attr_type) +{ + switch (attr_type) { + case BE_ATTR_CORE: + case BE_ATTR_MEM: + case BE_ATTR_ALL: + return true; + default: + return false; + } + + return false; +} + +static bool check_and_parse_filter(struct be_acct_req *data, + const char *filter, + const char *extra) +{ + /* We will use sizeof() to determine the length of a string so we don't + * call strlen over and over again with each request. Not a bottleneck, + * but unnecessary and simple to avoid. */ + static struct { + const char *name; + size_t lenght; + uint32_t type; + } types[] = {FILTER_TYPE("name", BE_FILTER_NAME), + FILTER_TYPE("idnumber", BE_FILTER_IDNUM), + FILTER_TYPE(DP_SEC_ID, BE_FILTER_SECID), + FILTER_TYPE(DP_CERT, BE_FILTER_CERT), + FILTER_TYPE(DP_WILDCARD, BE_FILTER_WILDCARD), + {0, 0, 0}}; + int i; + + if (filter == NULL) { + return false; + } + + for (i = 0; types[i].name != NULL; i++) { + if (strncmp(filter, types[i].name, types[i].lenght) == 0) { + data->filter_type = types[i].type; + data->filter_value = discard_const(&filter[types[i].lenght]); /* todo remove discard const */ + data->extra_value = discard_const(extra); /* todo remove discard const */ + return true; + } + } + + if (strcmp(filter, ENUM_INDICATOR) == 0) { + data->filter_type = BE_FILTER_ENUM; + data->filter_value = NULL; + data->extra_value = NULL; + return true; + } + + return false; +} + +struct dp_initgr_ctx { + const char *username; + const char *domain; + uint32_t gnum; + uint32_t *groups; +}; + +static struct dp_initgr_ctx *create_initgr_ctx(TALLOC_CTX *mem_ctx, + const char *domain, + struct ldb_result *res) +{ + struct dp_initgr_ctx *ctx; + const char *username; + unsigned int i; + errno_t ret; + + ctx = talloc_zero(mem_ctx, struct dp_initgr_ctx); + if (ctx == NULL) { + return NULL; + } + + username = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL); + if (username == NULL) { + ret = EINVAL; + goto done; + } + + ctx->username = talloc_strdup(ctx, username); + if (ctx->username == NULL) { + ret = ENOMEM; + goto done; + } + + ctx->domain = talloc_strdup(ctx, domain); + if (ctx->domain == NULL) { + ret = ENOMEM; + goto done; + } + + ctx->groups = talloc_array(mem_ctx, uint32_t, res->count); + if (ctx->groups == NULL) { + ret = ENOMEM; + goto done; + } + + /* The first GID is the primary so it might be duplicated + * later in the list. */ + for (ctx->gnum = 0, i = 0; i < res->count; i++) { + ctx->groups[ctx->gnum] = ldb_msg_find_attr_as_uint(res->msgs[i], + SYSDB_GIDNUM, 0); + /* If 0 it may be a non-posix group, so we skip it. */ + if (ctx->groups[ctx->gnum] != 0) { + ctx->gnum++; + } + } + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(ctx); + return NULL; + } + + return ctx; +} + +static void dp_req_initgr_pp(const char *req_name, + struct data_provider *provider, + struct dp_initgr_ctx *ctx, + struct dp_reply_std *reply) +{ + struct dp_client *dp_cli; + DBusMessage *msg; + dbus_bool_t dbret; + int num; + + dp_cli = provider->clients[DPC_NSS]; + if (dp_cli == NULL) { + return; + } + + msg = dbus_message_new_method_call(NULL, + DP_PATH, + DATA_PROVIDER_REV_IFACE, + DATA_PROVIDER_REV_IFACE_INITGRCHECK); + if (msg == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n"); + return; + } + + num = ctx->gnum; + dbret = dbus_message_append_args(msg, + DBUS_TYPE_STRING, &ctx->username, + DBUS_TYPE_STRING, &ctx->domain, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, + &ctx->groups, num, + DBUS_TYPE_INVALID); + if (!dbret) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n"); + dbus_message_unref(msg); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, + "Ordering NSS responder to update memory cache\n"); + + sbus_conn_send_reply(dp_client_conn(dp_cli), msg); + dbus_message_unref(msg); + + return; +} + +static errno_t dp_initgroups(struct sbus_request *sbus_req, + struct dp_client *dp_cli, + const char *key, + uint32_t dp_flags, + struct be_acct_req *data) +{ + struct be_ctx *be_ctx; + struct data_provider *provider; + struct sss_domain_info *domain; + struct dp_initgr_ctx *ctx; + struct ldb_result *res; + errno_t ret; + + provider = dp_client_provider(dp_cli); + be_ctx = provider->be_ctx; + + if (data->domain == NULL) { + domain = be_ctx->domain; + } else { + domain = find_domain_by_name(be_ctx->domain, data->domain, true); + if (domain == NULL) { + return ERR_DOMAIN_NOT_FOUND; + } + } + + ret = sysdb_initgroups(sbus_req, domain, data->filter_value, &res); + if (ret == ENOENT || (ret == EOK && res->count == 0)) { + /* There is no point in concacting NSS responder. Proceed as usual. */ + return EAGAIN; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get initgroups [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + ctx = create_initgr_ctx(sbus_req, data->domain, res); + if (ctx == NULL) { + ret = ENOMEM; + goto done; + } + + dp_req_with_reply_pp(dp_cli, data->domain, "Initgroups", key, + sbus_req, DPT_ID, DPM_ACCOUNT_HANDLER, dp_flags, data, + dp_req_initgr_pp, ctx, struct dp_initgr_ctx, + dp_req_reply_std, struct dp_reply_std); + + ret = EOK; + +done: + talloc_free(res); + return ret; +} + +errno_t dp_get_account_info_handler(struct sbus_request *sbus_req, + void *dp_cli, + uint32_t dp_flags, + uint32_t entry_type, + uint32_t attr_type, + const char *filter, + const char *domain, + const char *extra) +{ + struct be_acct_req *data; + const char *key; + errno_t ret; + + if (!check_attr_type(attr_type)) { + return EINVAL; + } + + data = talloc_zero(sbus_req, struct be_acct_req); + if (data == NULL) { + return ENOMEM; + } + + data->entry_type = entry_type; + data->attr_type = attr_type; + data->domain = discard_const(domain); /* todo remove discard const */ + + if (!check_and_parse_filter(data, filter, extra)) { + ret = EINVAL; + goto done; + } + + key = talloc_asprintf(data, "%u:%u:%s:%s:%s", data->entry_type, + data->attr_type, extra, domain, filter); + if (key == NULL) { + ret = ENOMEM; + goto done; + } + + if ((data->entry_type & BE_REQ_TYPE_MASK) == BE_REQ_INITGROUPS) { + ret = dp_initgroups(sbus_req, dp_cli, key, dp_flags, data); + if (ret != EAGAIN) { + goto done; + } + } + + dp_req_with_reply(dp_cli, domain, "Account", key, + sbus_req, DPT_ID, DPM_ACCOUNT_HANDLER, dp_flags, data, + dp_req_reply_std, struct dp_reply_std); + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(data); + } + + return ret; +} diff --git a/src/providers/data_provider/dp_target_subdomains.c b/src/providers/data_provider/dp_target_subdomains.c new file mode 100644 index 000000000..85e37f063 --- /dev/null +++ b/src/providers/data_provider/dp_target_subdomains.c @@ -0,0 +1,50 @@ +/* + Authors: + Pavel Březina + + Copyright (C) 2016 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 "sbus/sssd_dbus.h" +#include "providers/data_provider/dp_private.h" +#include "providers/data_provider/dp_iface.h" +#include "providers/backend.h" +#include "util/util.h" + +errno_t dp_subdomains_handler(struct sbus_request *sbus_req, + void *dp_cli, + const char *domain_hint) +{ + struct dp_subdomains_data *data; + const char *key; + + data = talloc_zero(sbus_req, struct dp_subdomains_data); + if (data == NULL) { + return ENOMEM; + } + + data->domain_hint = domain_hint; + key = domain_hint[0] == '\0' ? "" : domain_hint; + + dp_req_with_reply(dp_cli, NULL, "Subdomains", key, sbus_req, + DPT_SUBDOMAINS, DPM_DOMAINS_HANDLER, 0, data, + dp_req_reply_std, struct dp_reply_std); + + return EOK; +} diff --git a/src/providers/data_provider/dp_target_sudo.c b/src/providers/data_provider/dp_target_sudo.c new file mode 100644 index 000000000..37add97fe --- /dev/null +++ b/src/providers/data_provider/dp_target_sudo.c @@ -0,0 +1,199 @@ +/* + Authors: + Pavel Březina + + Copyright (C) 2016 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 "sbus/sssd_dbus.h" +#include "providers/data_provider/dp_private.h" +#include "providers/data_provider/dp_iface.h" +#include "providers/backend.h" +#include "util/util.h" + +static errno_t dp_sudo_parse_message(TALLOC_CTX *mem_ctx, + DBusMessage *msg, + uint32_t *_dp_flags, + uint32_t *_sudo_type, + char ***_rules) +{ + DBusError error; + DBusMessageIter iter; + DBusMessageIter array_iter; + uint32_t dp_flags; + uint32_t sudo_type; + uint32_t num_rules; + const char *rule; + char **rules = NULL; + uint32_t i; + errno_t ret; + + dbus_error_init(&error); + dbus_message_iter_init(msg, &iter); + + /* get dp flags */ + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n"); + ret = EIO; + goto done; + } + + dbus_message_iter_get_basic(&iter, &dp_flags); + dbus_message_iter_next(&iter); /* step behind the request type */ + + /* get type of the request */ + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n"); + ret = EIO; + goto done; + } + + dbus_message_iter_get_basic(&iter, &sudo_type); + dbus_message_iter_next(&iter); /* step behind the request type */ + + /* get additional arguments according to the request type */ + switch (sudo_type) { + case BE_REQ_SUDO_FULL: + /* no arguments required */ + break; + case BE_REQ_SUDO_RULES: + /* additional arguments: + * rules_num + * rules[rules_num] + */ + /* read rules_num */ + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n"); + ret = EIO; + goto done; + } + + dbus_message_iter_get_basic(&iter, &num_rules); + + rules = talloc_zero_array(mem_ctx, char *, num_rules + 1); + if (rules == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_array() failed.\n"); + ret = ENOMEM; + goto done; + } + + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n"); + ret = EIO; + goto done; + } + + dbus_message_iter_recurse(&iter, &array_iter); + + /* read the rules */ + for (i = 0; i < num_rules; i++) { + if (dbus_message_iter_get_arg_type(&array_iter) + != DBUS_TYPE_STRING) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n"); + ret = EIO; + goto done; + } + + dbus_message_iter_get_basic(&array_iter, &rule); + rules[i] = talloc_strdup(rules, rule); + if (rules[i] == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed.\n"); + ret = ENOMEM; + goto done; + } + + dbus_message_iter_next(&array_iter); + } + + rules[num_rules] = NULL; + + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type %d\n", sudo_type); + return EINVAL; + } + + *_dp_flags = dp_flags; + *_sudo_type = sudo_type; + *_rules = rules; + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(rules); + } + + return ret; +} + +static const char *dp_sudo_get_key(uint32_t type) +{ + switch (type) { + case BE_REQ_SUDO_FULL: + return "full-refresh"; + case BE_REQ_SUDO_RULES: + return NULL; + } + + return NULL; +} + +static const char *dp_sudo_get_name(uint32_t type) +{ + switch (type) { + case BE_REQ_SUDO_FULL: + return "SUDO Full Refresh"; + case BE_REQ_SUDO_RULES: + return "SUDO Rules Refresh"; + } + + return NULL; +} + +errno_t dp_sudo_handler(struct sbus_request *sbus_req, void *dp_cli) +{ + struct dp_sudo_data *data; + uint32_t dp_flags; + const char *key; + const char *name; + errno_t ret; + + data = talloc_zero(sbus_req, struct dp_sudo_data); + if (data == NULL) { + return ENOMEM; + } + + ret = dp_sudo_parse_message(data, sbus_req->message, &dp_flags, + &data->type, &data->rules); + if (ret != EOK) { + return ret; + } + + key = dp_sudo_get_key(data->type); + name = dp_sudo_get_name(data->type); + + dp_req_with_reply(dp_cli, NULL, name, key, sbus_req, DPT_SUDO, + DPM_SUDO_HANDLER, dp_flags, data, + dp_req_reply_std, struct dp_reply_std); + + return EOK; +} diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c index 58c32dca4..78efed851 100644 --- a/src/providers/data_provider_be.c +++ b/src/providers/data_provider_be.c @@ -49,12 +49,6 @@ #include "resolv/async_resolv.h" #include "monitor/monitor_interfaces.h" -#define MSG_TARGET_NO_CONFIGURED "sssd_be: The requested target is not configured" - -#define ACCESS_PERMIT "permit" -#define ACCESS_DENY "deny" -#define NO_PROVIDER "none" - static int data_provider_res_init(struct sbus_request *dbus_req, void *data); static int data_provider_go_offline(struct sbus_request *dbus_req, void *data); static int data_provider_reset_offline(struct sbus_request *dbus_req, void *data); @@ -73,557 +67,12 @@ struct mon_cli_iface monitor_be_methods = { .sysbusReconnect = NULL, }; -static int client_registration(struct sbus_request *dbus_req, void *data); -static int be_get_account_info(struct sbus_request *dbus_req, void *user_data); -static int be_pam_handler(struct sbus_request *dbus_req, void *user_data); -static int be_sudo_handler(struct sbus_request *dbus_req, void *user_data); -static int be_autofs_handler(struct sbus_request *dbus_req, void *user_data); -static int be_host_handler(struct sbus_request *dbus_req, void *user_data); -static int be_get_subdomains(struct sbus_request *dbus_req, void *user_data); - -struct data_provider_iface be_methods = { - { &data_provider_iface_meta, 0 }, - .RegisterService = client_registration, - .pamHandler = be_pam_handler, - .sudoHandler = be_sudo_handler, - .autofsHandler = be_autofs_handler, - .hostHandler = be_host_handler, - .getDomains = be_get_subdomains, - .getAccountInfo = be_get_account_info, -}; - -static struct bet_data bet_data[] = { - {BET_NULL, NULL, NULL}, - {BET_ID, CONFDB_DOMAIN_ID_PROVIDER, "sssm_%s_id_init"}, - {BET_AUTH, CONFDB_DOMAIN_AUTH_PROVIDER, "sssm_%s_auth_init"}, - {BET_ACCESS, CONFDB_DOMAIN_ACCESS_PROVIDER, "sssm_%s_access_init"}, - {BET_CHPASS, CONFDB_DOMAIN_CHPASS_PROVIDER, "sssm_%s_chpass_init"}, - {BET_SUDO, CONFDB_DOMAIN_SUDO_PROVIDER, "sssm_%s_sudo_init"}, - {BET_AUTOFS, CONFDB_DOMAIN_AUTOFS_PROVIDER, "sssm_%s_autofs_init"}, - {BET_SELINUX, CONFDB_DOMAIN_SELINUX_PROVIDER, "sssm_%s_selinux_init"}, - {BET_HOSTID, CONFDB_DOMAIN_HOSTID_PROVIDER, "sssm_%s_hostid_init"}, - {BET_SUBDOMAINS, CONFDB_DOMAIN_SUBDOMAINS_PROVIDER, "sssm_%s_subdomains_init"}, - {BET_MAX, NULL, NULL} -}; - -struct bet_queue_item { - struct bet_queue_item *prev; - struct bet_queue_item *next; - - TALLOC_CTX *mem_ctx; - struct be_req *be_req; - be_req_fn_t fn; - -}; - -static const char *dp_err_to_string(int dp_err_type) -{ - switch (dp_err_type) { - case DP_ERR_OK: - return "Success"; - case DP_ERR_OFFLINE: - return "Provider is Offline"; - case DP_ERR_TIMEOUT: - return "Request timed out"; - case DP_ERR_FATAL: - return "Internal Error"; - default: - break; - } - - return "Unknown Error"; -} - -static const char *safe_be_req_err_msg(const char *msg_in, - int dp_err_type) -{ - bool ok; - - if (msg_in == NULL) { - /* No custom error, just use default */ - return dp_err_to_string(dp_err_type); - } - - ok = sss_utf8_check((const uint8_t *) msg_in, - strlen(msg_in)); - if (!ok) { - DEBUG(SSSDBG_MINOR_FAILURE, - "Back end message [%s] contains invalid non-UTF8 character, " \ - "using default\n", msg_in); - return dp_err_to_string(dp_err_type); - } - - return msg_in; -} - -#define REQ_PHASE_ACCESS 0 -#define REQ_PHASE_SELINUX 1 - -struct be_req { - struct be_client *becli; - struct be_ctx *be_ctx; - struct sss_domain_info *domain; - void *req_data; - - be_async_callback_t fn; - void *pvt; - - /* This is utilized in access provider - * request handling to indicate if access or - * selinux provider is calling the callback. - */ - int phase; - - /* Just for nicer debugging */ - const char *req_name; - - struct be_req *prev; - struct be_req *next; -}; - -static int be_req_destructor(struct be_req *be_req) -{ - DLIST_REMOVE(be_req->be_ctx->active_requests, be_req); - - return 0; -} - -struct be_req *be_req_create(TALLOC_CTX *mem_ctx, - struct be_client *becli, - struct be_ctx *be_ctx, - const char *name, - be_async_callback_t fn, - void *pvt_fn_data) -{ - struct be_req *be_req; - - be_req = talloc_zero(mem_ctx, struct be_req); - if (be_req == NULL) return NULL; - - be_req->becli = becli; - be_req->be_ctx = be_ctx; - be_req->domain = be_ctx->domain; - be_req->fn = fn; - be_req->pvt = pvt_fn_data; - be_req->req_name = talloc_strdup(be_req, name); - if (be_req->req_name == NULL) { - talloc_free(be_req); - return NULL; - } - - /* Add this request to active request list and make sure it is - * removed on termination. */ - DLIST_ADD(be_ctx->active_requests, be_req); - talloc_set_destructor(be_req, be_req_destructor); - - return be_req; -} - -static errno_t be_req_set_domain(struct be_req *be_req, const char *domain) -{ - struct sss_domain_info *dom = NULL; - - dom = find_domain_by_name(be_req->be_ctx->domain, domain, true); - if (dom == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unknown domain [%s]!\n", domain); - return ERR_DOMAIN_NOT_FOUND; - } - - DEBUG(SSSDBG_TRACE_FUNC, "Changing request domain from [%s] to [%s]\n", - be_req->domain->name, dom->name); - be_req->domain = dom; - - return EOK; -} - -struct be_ctx *be_req_get_be_ctx(struct be_req *be_req) -{ - return be_req->be_ctx; -} - -void *be_req_get_data(struct be_req *be_req) -{ - return be_req->req_data; -} - -void be_req_terminate(struct be_req *be_req, - int dp_err_type, int errnum, const char *errstr) -{ - if (be_req->fn == NULL) return; - be_req->fn(be_req, dp_err_type, errnum, errstr); -} - -static errno_t be_sbus_reply(struct sbus_request *sbus_req, - dbus_uint16_t err_maj, - dbus_uint32_t err_min, - const char *err_msg) -{ - errno_t ret; - const char *safe_err_msg; - - /* Only return a reply if one was requested - * There may not be one if this request began - * while we were offline - */ - if (sbus_req == NULL) { - return EOK; - } - - safe_err_msg = safe_be_req_err_msg(err_msg, err_maj); - - if (err_maj == DP_ERR_FATAL && err_min == ENODEV) { - DEBUG(SSSDBG_TRACE_LIBS, - "Cannot handle request: %s", - err_msg ? err_msg : "Handler not configured\n"); - } else { - DEBUG(SSSDBG_TRACE_LIBS, - "Request processed. Returned [%s]:%d,%d,%s\n", - dp_err_to_string(err_maj), err_maj, err_min, err_msg); - } - - ret = sbus_request_return_and_finish(sbus_req, - DBUS_TYPE_UINT16, &err_maj, - DBUS_TYPE_UINT32, &err_min, - DBUS_TYPE_STRING, &safe_err_msg, - DBUS_TYPE_INVALID); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "sbus_request_return_and_finish failed: [%d]: %s\n", - ret, sss_strerror(ret)); - } - - return ret; -} - -static errno_t be_sbus_req_reply(struct sbus_request *sbus_req, - int dp_err_type, - int errnum, - const char *errstr) -{ - dbus_uint16_t err_maj; - dbus_uint32_t err_min; - - err_maj = dp_err_type; - err_min = errnum; - - return be_sbus_reply(sbus_req, err_maj, err_min, errstr); -} - -static void be_req_default_callback(struct be_req *be_req, - int dp_err_type, - int errnum, - const char *errstr) -{ - struct sbus_request *dbus_req; - - DEBUG(SSSDBG_TRACE_FUNC, "Replying to %s request\n", be_req->req_name); - - dbus_req = (struct sbus_request *) be_req->pvt; - - be_sbus_req_reply(dbus_req, dp_err_type, errnum, errstr); - talloc_free(be_req); -} - -/* Send back an immediate reply and set the sbus_request to NULL - * so that we are sure the request is not reused in the future - */ -static errno_t be_offline_reply(struct sbus_request **sbus_req_ptr) -{ - struct sbus_request *dbus_req; - errno_t ret; - - if (sbus_req_ptr == NULL) { - return EINVAL; - } - dbus_req = *sbus_req_ptr; - - ret = be_sbus_req_reply(dbus_req, DP_ERR_OFFLINE, EAGAIN, - "Fast reply - offline"); - *sbus_req_ptr = NULL; - return ret; -} - -struct be_sbus_reply_data { - dbus_uint16_t err_maj; - dbus_uint32_t err_min; - const char *err_msg; -}; - -#define BE_SBUS_REPLY_DATA_INIT { .err_maj = DP_ERR_FATAL, \ - .err_min = ERR_INTERNAL, \ - .err_msg = "Fatal error" \ - }; - -static inline void be_sbus_reply_data_set(struct be_sbus_reply_data *rdata, - dbus_uint16_t err_maj, - dbus_uint32_t err_min, - const char *err_msg) -{ - if (rdata == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Bug: Attempt to set NULL be_sbus_reply_data\n"); - return; - } - - rdata->err_maj = err_maj; - rdata->err_min = err_min; - rdata->err_msg = err_msg; -} - -static inline errno_t be_sbus_req_reply_data(struct sbus_request *sbus_req, - struct be_sbus_reply_data *data) -{ - return be_sbus_reply(sbus_req, data->err_maj, - data->err_min, data->err_msg); -} - -void be_terminate_domain_requests(struct be_ctx *be_ctx, - const char *domain) -{ - struct be_req *be_req; - struct be_req *next_be_req; - - DEBUG(SSSDBG_TRACE_FUNC, "Terminating requests for domain [%s]\n", - domain); - - if (domain == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "BUG: domain is NULL\n"); - return; - } - - be_req = be_ctx->active_requests; - while (be_req) { - /* save pointer to next request in case be_req will be freed */ - next_be_req = be_req->next; - if (strcmp(domain, be_req->domain->name) == 0) { - be_req_terminate(be_req, DP_ERR_FATAL, ERR_DOMAIN_NOT_FOUND, - sss_strerror(ERR_DOMAIN_NOT_FOUND)); - } - be_req = next_be_req; - } -} - -struct be_async_req { - be_req_fn_t fn; - struct be_req *req; -}; - -static void be_async_req_handler(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval tv, void *pvt) -{ - struct be_async_req *async_req; - - async_req = talloc_get_type(pvt, struct be_async_req); - - async_req->fn(async_req->req); -} - -struct be_spy { - TALLOC_CTX *freectx; - struct be_spy *double_agent; -}; - -static int be_spy_destructor(struct be_spy *spy) -{ - /* If there's a double_agent, set its - * freectx to NULL so that we don't - * try to loop. When that spy fires, - * it will just be a no-op. - */ - spy->double_agent->freectx = NULL; - talloc_zfree(spy->freectx); - return 0; -} - -static errno_t be_spy_create(TALLOC_CTX *mem_ctx, struct be_req *be_req) -{ - errno_t ret; - struct be_spy *cli_spy = NULL; - struct be_spy *req_spy = NULL; - - /* Attach a spy for the be_client so that if it dies, - * we can free the be_req automatically. - */ - cli_spy = talloc_zero(be_req->becli, struct be_spy); - if (!cli_spy) { - ret = ENOMEM; - goto done; - } - cli_spy->freectx = be_req; - - /* Also create a spy on the be_req so that we - * can free the other spy when the be_req - * completes successfully. - */ - req_spy = talloc_zero(be_req, struct be_spy); - if (!req_spy) { - ret = ENOMEM; - goto done; - } - req_spy->freectx = cli_spy; - - /* Create paired spy links to prevent loops */ - cli_spy->double_agent = req_spy; - req_spy->double_agent = cli_spy; - - /* Now create the destructors that will actually free - * the opposing spies. - */ - talloc_set_destructor(cli_spy, be_spy_destructor); - talloc_set_destructor(req_spy, be_spy_destructor); - - - /* Now steal the be_req onto the mem_ctx so that it - * will be guaranteed that this data will be - * available for the full duration of execution. - */ - talloc_steal(mem_ctx, be_req); - - ret = EOK; -done: - if (ret != EOK) { - talloc_free(cli_spy); - talloc_free(req_spy); - } - return ret; -} - -/* This function alters the memory hierarchy of the be_req - * to ensure memory safety during shutdown. It creates a - * spy on the be_cli object so that it will free the be_req - * if the client is freed. - * - * It is generally allocated atop the private data context - * for the appropriate back-end against which it is being - * filed. - */ -static errno_t be_file_request(TALLOC_CTX *mem_ctx, - struct be_req *be_req, - be_req_fn_t fn) -{ - errno_t ret; - struct be_async_req *areq; - struct tevent_timer *te; - struct timeval tv; - - if (!fn || !be_req) return EINVAL; - - ret = be_spy_create(mem_ctx, be_req); - if (ret != EOK) return ret; - - areq = talloc(be_req, struct be_async_req); - if (!areq) { - return ENOMEM; - } - areq->fn = fn; - areq->req = be_req; - - /* fire immediately */ - tv.tv_sec = 0; - tv.tv_usec = 0; - - te = tevent_add_timer(be_req->be_ctx->ev, be_req, - tv, be_async_req_handler, areq); - if (te == NULL) { - return EIO; - } - - return EOK; -} - -static errno_t be_queue_request(TALLOC_CTX *queue_mem_ctx, - struct bet_queue_item **req_queue, - TALLOC_CTX *req_mem_ctx, - struct be_req *be_req, - be_req_fn_t fn) -{ - struct bet_queue_item *item; - int ret; - - if (*req_queue == NULL) { - DEBUG(SSSDBG_TRACE_ALL, "Queue is empty, " \ - "running request immediately.\n"); - ret = be_file_request(req_mem_ctx, be_req, fn); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "be_file_request failed.\n"); - return ret; - } - } - - item = talloc_zero(queue_mem_ctx, struct bet_queue_item); - if (item == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed, cannot add item to " \ - "request queue.\n"); - } else { - DEBUG(SSSDBG_TRACE_ALL, "Adding request to queue.\n"); - item->mem_ctx = req_mem_ctx; - item->be_req = be_req; - item->fn = fn; - - DLIST_ADD_END(*req_queue, item, struct bet_queue_item *); - } - - return EOK; -} - -static void be_queue_next_request(struct be_req *be_req, enum bet_type type) -{ - struct bet_queue_item *item; - struct bet_queue_item *current = NULL; - struct bet_queue_item **req_queue; - struct sbus_request *dbus_req; - int ret; - struct be_req *next_be_req = NULL; - - req_queue = &be_req->becli->bectx->bet_info[type].req_queue; - - if (*req_queue == NULL) { - DEBUG(SSSDBG_TRACE_ALL, "Queue is empty, nothing to do.\n"); - return; - } - - DLIST_FOR_EACH(item, *req_queue) { - if (item->be_req == be_req) { - current = item; - break; - } - } - - if (current != NULL) { - DLIST_REMOVE(*req_queue, current); - } - - if (*req_queue == NULL) { - DEBUG(SSSDBG_TRACE_ALL, "Request queue is empty.\n"); - return; - } - - next_be_req = (*req_queue)->be_req; - - ret = be_file_request((*req_queue)->mem_ctx, next_be_req, (*req_queue)->fn); - if (ret == EOK) { - DEBUG(SSSDBG_TRACE_ALL, "Queued request filed successfully.\n"); - return; - } - - DEBUG(SSSDBG_OP_FAILURE, "be_file_request failed.\n"); - - be_queue_next_request(next_be_req, type); - - dbus_req = (struct sbus_request *) next_be_req->pvt; - - be_sbus_req_reply(dbus_req, DP_ERR_FATAL, ret, - "Cannot file back end request"); - talloc_free(next_be_req); -} - bool be_is_offline(struct be_ctx *ctx) { return ctx->offstat.offline; } -static void check_if_online(struct be_ctx *ctx); +static void check_if_online(struct be_ctx *be_ctx); static errno_t try_to_go_online(TALLOC_CTX *mem_ctx, @@ -776,1629 +225,125 @@ static void be_reset_offline(struct be_ctx *ctx) be_run_online_cb(ctx); } -static void get_subdomains_callback(struct be_req *req, - int dp_err_type, - int errnum, - const char *errstr) +static void be_check_online_done(struct tevent_req *req); + +static errno_t be_check_online_request(struct be_ctx *be_ctx) { - struct sbus_request *dbus_req; + struct tevent_req *req; + + be_ctx->offstat.went_offline = time(NULL); + reset_fo(be_ctx); - be_queue_next_request(req, BET_SUBDOMAINS); + req = dp_req_send(be_ctx, be_ctx->provider, NULL, NULL, "Online Check", + DPT_ID, DPM_CHECK_ONLINE, 0, NULL, NULL); + if (req == NULL) { + return ENOMEM; + } - dbus_req = (struct sbus_request *) req->pvt; + tevent_req_set_callback(req, be_check_online_done, be_ctx); - be_sbus_req_reply(dbus_req, dp_err_type, errnum, errstr); - talloc_free(req); + return EOK; } -static int be_get_subdomains(struct sbus_request *dbus_req, void *user_data) +static void be_check_online_done(struct tevent_req *req) { - struct be_subdom_req *req; - struct be_req *be_req = NULL; - struct be_client *becli; - char *domain_hint; - struct be_sbus_reply_data req_reply = BE_SBUS_REPLY_DATA_INIT; - int ret; - - becli = talloc_get_type(user_data, struct be_client); - if (!becli) return EINVAL; + struct be_ctx *be_ctx; + struct dp_reply_std reply; + errno_t ret; - if (!sbus_request_parse_or_finish(dbus_req, - DBUS_TYPE_STRING, &domain_hint, - DBUS_TYPE_INVALID)) - return EOK; /* handled */ + be_ctx = tevent_req_callback_data(req, struct be_ctx); - /* return an error if corresponding backend target is not configured */ - if (becli->bectx->bet_info[BET_SUBDOMAINS].bet_ops == NULL) { - DEBUG(SSSDBG_TRACE_INTERNAL, "Undefined backend target.\n"); - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENODEV, - "Subdomains back end target is not configured"); - goto immediate; + ret = dp_req_recv_ptr(be_ctx, req, struct dp_reply_std, &reply); + talloc_zfree(req); + if (ret != EOK) { + goto done; } - DEBUG(SSSDBG_TRACE_FUNC, "Got get subdomains [%s]\n", - domain_hint == NULL ? "no hint": domain_hint ); - - /* If we are offline return immediately - */ - if (becli->bectx->offstat.offline) { - DEBUG(SSSDBG_TRACE_FUNC, "Cannot proceed, provider is offline.\n"); - be_sbus_reply_data_set(&req_reply, DP_ERR_OFFLINE, EAGAIN, - "Provider is offline"); - goto immediate; + switch (reply.dp_error) { + case DP_ERR_OK: + DEBUG(SSSDBG_TRACE_FUNC, "Backend is online\n"); + break; + case DP_ERR_OFFLINE: + DEBUG(SSSDBG_TRACE_FUNC, "Backend is offline\n"); + break; + default: + DEBUG(SSSDBG_TRACE_FUNC, "Error during online check [%d]: %s\n", + ret, sss_strerror(ret)); + break; } - /* process request */ + be_ctx->check_online_ref_count--; - be_req = be_req_create(becli, becli, becli->bectx, "get subdomains", - get_subdomains_callback, dbus_req); - if (!be_req) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENOMEM, - "Out of memory"); - goto immediate; + if (reply.dp_error != DP_ERR_OK && be_ctx->check_online_ref_count > 0) { + ret = be_check_online_request(be_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create check online req.\n"); + goto done; + } + return; } - req = talloc(be_req, struct be_subdom_req); - if (!req) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENOMEM, - "Out of memory"); - goto immediate; - } - req->domain_hint = talloc_strdup(req, domain_hint); - if (!req->domain_hint) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENOMEM, - "Out of memory"); - goto immediate; +done: + be_ctx->check_online_ref_count = 0; + if (reply.dp_error != DP_ERR_OFFLINE) { + if (reply.dp_error != DP_ERR_OK) { + reset_fo(be_ctx); + } + be_reset_offline(be_ctx); } +} + +static void check_if_online(struct be_ctx *be_ctx) +{ + errno_t ret; - be_req->req_data = req; + be_run_unconditional_online_cb(be_ctx); - ret = be_queue_request(becli->bectx, - &becli->bectx->bet_info[BET_SUBDOMAINS].req_queue, - becli->bectx, - be_req, - becli->bectx->bet_info[BET_SUBDOMAINS].bet_ops->handler); - if (ret != EOK) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ret, - "Cannot file back end request"); - goto immediate; + if (!be_is_offline(be_ctx)) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "Backend is already online, nothing to do.\n"); + return; } - return EOK; + /* Make sure nobody tries to go online while we are checking */ + be_ctx->offstat.went_offline = time(NULL); -immediate: - talloc_free(be_req); - be_sbus_req_reply_data(dbus_req, &req_reply); - return EOK; -} + DEBUG(SSSDBG_TRACE_INTERNAL, "Trying to go back online!\n"); -struct be_initgr_prereq { - char *user; - char *domain; - uint32_t gnum; - uint32_t *groups; + be_ctx->check_online_ref_count++; - void *orig_pvt_data; - int orig_dp_err_type; - int orig_errnum; - const char *orig_errstr; -}; + if (be_ctx->check_online_ref_count != 1) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "There is an online check already running.\n"); + return; + } -static void acctinfo_callback_initgr_wrap(struct be_req *be_req) -{ - struct be_initgr_prereq *pr = talloc_get_type(be_req->pvt, - struct be_initgr_prereq); - - be_req->pvt = pr->orig_pvt_data; - be_req_default_callback(be_req, pr->orig_dp_err_type, - pr->orig_errnum, pr->orig_errstr); -} - -static void acctinfo_callback_initgr_sbus(DBusPendingCall *pending, void *ptr) -{ - struct be_req *be_req = talloc_get_type(ptr, struct be_req); - - dbus_pending_call_unref(pending); - - acctinfo_callback_initgr_wrap(be_req); -} - -static void acctinfo_initgroups_callback(struct be_req *be_req, - int dp_err_type, - int errnum, - const char *errstr) -{ - struct be_initgr_prereq *pr = talloc_get_type(be_req->pvt, - struct be_initgr_prereq); - DBusMessage *msg = NULL; - dbus_bool_t dbret; - int num; - int ret; - - pr->orig_dp_err_type = dp_err_type; - pr->orig_errnum = errnum; - pr->orig_errstr = errstr; - - if (!be_req->be_ctx->nss_cli || !be_req->be_ctx->nss_cli->conn) { - DEBUG(SSSDBG_MINOR_FAILURE, "NSS Service not conected\n"); - ret = EACCES; - goto done; - } - - /* Set up null request */ - msg = dbus_message_new_method_call(NULL, - DP_PATH, - DATA_PROVIDER_REV_IFACE, - DATA_PROVIDER_REV_IFACE_INITGRCHECK); - if (!msg) { - DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n"); - ret = ENOMEM; - goto done; - } - - num = pr->gnum; - dbret = dbus_message_append_args(msg, - DBUS_TYPE_STRING, &pr->user, - DBUS_TYPE_STRING, &pr->domain, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, - &pr->groups, num, - DBUS_TYPE_INVALID); - if (!dbret) { - DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n"); - ret = ENOMEM; - goto done; - } - - /* ping the NSS service, no reply expected */ - ret = sbus_conn_send(be_req->be_ctx->nss_cli->conn, msg, -1, - acctinfo_callback_initgr_sbus, be_req, NULL); - if (ret != EOK) { - DEBUG(SSSDBG_TRACE_FUNC, - "Error contacting NSS responder: %d [%s]\n", - ret, strerror(ret)); - } - -done: - if (msg) { - dbus_message_unref(msg); - } - if (ret != EOK) { - /* return immediately if we cannot contact nss provider */ - acctinfo_callback_initgr_wrap(be_req); - } -} - -static errno_t be_initgroups_prereq(struct be_req *be_req) -{ - struct be_acct_req *ar = talloc_get_type(be_req_get_data(be_req), - struct be_acct_req); - struct be_initgr_prereq *pr; - struct ldb_result *res; - errno_t ret; - const char *tmpstr; - int i; - - ret = sysdb_initgroups(be_req, be_req->be_ctx->domain, ar->filter_value, - &res); - if (ret && ret != ENOENT) { - return ret; - } - /* if the user is completely missing there is no need to contact NSS, - * it would be a noop */ - if (ret == ENOENT || res->count == 0) { - /* yet unknown, ignore */ - return EOK; - } - - pr = talloc(be_req, struct be_initgr_prereq); - if (!pr) { - return ENOMEM; - } - pr->groups = talloc_array(pr, gid_t, res->count); - if (!pr->groups) { - return ENOMEM; - } - tmpstr = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL); - if (!tmpstr) { - return EINVAL; - } - pr->user = talloc_strdup(pr, tmpstr); - if (!pr->user) { - return ENOMEM; - } - pr->domain = talloc_strdup(pr, be_req->be_ctx->domain->name); - if (!pr->domain) { - return ENOMEM; - } - /* The first GID is the primary so it might be duplicated - * later in the list */ - for (pr->gnum = 0, i = 0; i < res->count; i++) { - pr->groups[pr->gnum] = ldb_msg_find_attr_as_uint(res->msgs[i], - SYSDB_GIDNUM, 0); - /* if 0 it may be a non-posix group, so we skip it */ - if (pr->groups[pr->gnum] != 0) { - pr->gnum++; - } - } - - talloc_zfree(res); - - pr->orig_pvt_data = be_req->pvt; - be_req->pvt = pr; - be_req->fn = acctinfo_initgroups_callback; - - return EOK; -} - -static errno_t -be_file_account_request(struct be_req *be_req, struct be_acct_req *ar) -{ - errno_t ret; - struct be_ctx *be_ctx = be_req->be_ctx; - - be_req->req_data = ar; - - /* see if we need a pre request call, only done for initgroups for now */ - if ((ar->entry_type & 0xFF) == BE_REQ_INITGROUPS) { - ret = be_initgroups_prereq(be_req); - if (ret) { - DEBUG(SSSDBG_CRIT_FAILURE, "Prerequest failed\n"); - return ret; - } - } - - /* process request */ - ret = be_file_request(be_ctx, be_req, - be_ctx->bet_info[BET_ID].bet_ops->handler); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Failed to file request\n"); - return ret; - } - - return EOK; -} - -static errno_t -split_name_extended(TALLOC_CTX *mem_ctx, - const char *filter, - char **name, - char **extended) -{ - char *p; - - *name = talloc_strdup(mem_ctx, filter); - if (!*name) { - return ENOENT; - } - - p = strchr(*name, ':'); - if (p) { - /* Extended info included */ - *p = '\0'; - - *extended = p + 1; - } else { - *extended = NULL; - } - - return EOK; -} - -static void -be_get_account_info_done(struct be_req *be_req, - int dp_err, int dp_ret, - const char *errstr); - -struct be_get_account_info_state { - int err_maj; - int err_min; - const char *err_msg; -}; - -struct tevent_req * -be_get_account_info_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct be_client *becli, - struct be_ctx *be_ctx, - struct be_acct_req *ar) -{ - struct tevent_req *req; - struct be_get_account_info_state *state; - struct be_req *be_req; - errno_t ret; - - req = tevent_req_create(mem_ctx, &state, - struct be_get_account_info_state); - if (!req) return NULL; - - be_req = be_req_create(state, becli, be_ctx, "get account info", - be_get_account_info_done, req); - if (!be_req) { - ret = ENOMEM; - goto done; - } - - ret = be_req_set_domain(be_req, ar->domain); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set request domain [%d]: %s\n", - ret, sss_strerror(ret)); - goto done; - } - - ret = be_file_account_request(be_req, ar); - if (ret != EOK) { - goto done; - } - - return req; - -done: - tevent_req_error(req, ret); - tevent_req_post(req, ev); - return req; -} - -static void -be_get_account_info_done(struct be_req *be_req, - int dp_err, int dp_ret, - const char *errstr) -{ - struct tevent_req *req; - struct be_get_account_info_state *state; - - req = talloc_get_type(be_req->pvt, struct tevent_req); - state = tevent_req_data(req, struct be_get_account_info_state); - - state->err_maj = dp_err; - state->err_min = dp_ret; - if (errstr) { - state->err_msg = talloc_strdup(state, errstr); - if (state->err_msg == NULL) { - talloc_free(be_req); - tevent_req_error(req, ENOMEM); - return; - } - } - - talloc_free(be_req); - tevent_req_done(req); -} - -errno_t be_get_account_info_recv(struct tevent_req *req, - TALLOC_CTX *mem_ctx, - int *_err_maj, - int *_err_min, - const char **_err_msg) -{ - struct be_get_account_info_state *state; - - state = tevent_req_data(req, struct be_get_account_info_state); - - TEVENT_REQ_RETURN_ON_ERROR(req); - - if (_err_maj) { - *_err_maj = state->err_maj; - } - - if (_err_min) { - *_err_min = state->err_min; - } - - if (_err_msg) { - *_err_msg = talloc_steal(mem_ctx, state->err_msg); - } - - return EOK; -} - -static int be_get_account_info(struct sbus_request *dbus_req, void *user_data) -{ - struct be_acct_req *req; - struct be_req *be_req; - struct be_client *becli; - uint32_t type; - char *filter; - char *domain; - uint32_t attr_type; - int ret; - struct be_sbus_reply_data req_reply = BE_SBUS_REPLY_DATA_INIT; - - be_req = NULL; - - becli = talloc_get_type(user_data, struct be_client); - if (!becli) return EINVAL; - - if (!sbus_request_parse_or_finish(dbus_req, - DBUS_TYPE_UINT32, &type, - DBUS_TYPE_UINT32, &attr_type, - DBUS_TYPE_STRING, &filter, - DBUS_TYPE_STRING, &domain, - DBUS_TYPE_INVALID)) - return EOK; /* handled */ - - DEBUG(SSSDBG_FUNC_DATA, - "Got request for [%#x][%s][%d][%s]\n", type, be_req2str(type), - attr_type, filter); - - /* If we are offline and fast reply was requested - * return offline immediately - */ - if ((type & BE_REQ_FAST) && becli->bectx->offstat.offline) { - ret = be_offline_reply(&dbus_req); - if (ret != EOK) { - return ret; - } - - /* This reply will be queued and sent - * when we reenter the mainloop. - * - * Continue processing in case we are - * going back online. - */ - } - - be_req = be_req_create(becli, becli, becli->bectx, "get account info", - be_req_default_callback, dbus_req); - if (!be_req) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENOMEM, - "Out of memory"); - goto done; - } - - ret = be_req_set_domain(be_req, domain); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set request domain [%d]: %s\n", - ret, sss_strerror(ret)); - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, - ret, sss_strerror(ret)); - goto done; - } - - req = talloc_zero(be_req, struct be_acct_req); - if (!req) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENOMEM, - "Out of memory"); - goto done; - } - req->entry_type = type; - req->attr_type = (int)attr_type; - req->domain = talloc_strdup(req, domain); - if (!req->domain) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENOMEM, - "Out of memory"); - goto done; - } - - if ((attr_type != BE_ATTR_CORE) && - (attr_type != BE_ATTR_MEM) && - (attr_type != BE_ATTR_ALL)) { - /* Unrecognized attr type */ - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, EINVAL, - "Invalid Attrs Parameter"); - goto done; - } - - if (filter) { - ret = EOK; - if (strncmp(filter, "name=", 5) == 0) { - req->filter_type = BE_FILTER_NAME; - ret = split_name_extended(req, &filter[5], - &req->filter_value, - &req->extra_value); - } else if (strncmp(filter, "idnumber=", 9) == 0) { - req->filter_type = BE_FILTER_IDNUM; - ret = split_name_extended(req, &filter[9], - &req->filter_value, - &req->extra_value); - } else if (strncmp(filter, DP_SEC_ID"=", DP_SEC_ID_LEN + 1) == 0) { - req->filter_type = BE_FILTER_SECID; - ret = split_name_extended(req, &filter[DP_SEC_ID_LEN + 1], - &req->filter_value, - &req->extra_value); - } else if (strncmp(filter, DP_CERT"=", DP_CERT_LEN + 1) == 0) { - req->filter_type = BE_FILTER_CERT; - ret = split_name_extended(req, &filter[DP_CERT_LEN + 1], - &req->filter_value, - &req->extra_value); - } else if (strncmp(filter, DP_WILDCARD"=", DP_WILDCARD_LEN + 1) == 0) { - req->filter_type = BE_FILTER_WILDCARD; - ret = split_name_extended(req, &filter[DP_WILDCARD_LEN + 1], - &req->filter_value, - &req->extra_value); - } else if (strcmp(filter, ENUM_INDICATOR) == 0) { - req->filter_type = BE_FILTER_ENUM; - req->filter_value = NULL; - req->extra_value = NULL; - } else { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, EINVAL, - "Invalid filter"); - goto done; - } - - if (ret != EOK) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, EINVAL, - "Invalid filter"); - goto done; - } - - } else { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, EINVAL, - "Missing filter parameter"); - goto done; - } - - ret = be_file_account_request(be_req, req); - if (ret != EOK) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, EINVAL, - "Cannot file account request"); - goto done; - } - - return EOK; - -done: - talloc_free(be_req); - be_sbus_req_reply_data(dbus_req, &req_reply); - return EOK; -} - -static void be_pam_handler_callback(struct be_req *req, - int dp_err_type, - int errnum, - const char *errstr) -{ - struct be_client *becli = req->becli; - struct sbus_request *dbus_req; - struct pam_data *pd; - DBusMessage *reply; - dbus_bool_t dbret; - errno_t ret; - - DEBUG(SSSDBG_CONF_SETTINGS, "Backend returned: (%d, %d, %s) [%s]\n", - dp_err_type, errnum, errstr?errstr:"", - dp_err_to_string(dp_err_type)); - - pd = talloc_get_type(be_req_get_data(req), struct pam_data); - - if (pd->cmd == SSS_PAM_ACCT_MGMT && - pd->pam_status == PAM_SUCCESS && - req->phase == REQ_PHASE_ACCESS && - dp_err_type == DP_ERR_OK) { - if (!becli->bectx->bet_info[BET_SELINUX].bet_ops) { - DEBUG(SSSDBG_TRACE_FUNC, - "SELinux provider doesn't exist, " - "not sending the request to it.\n"); - } else { - req->phase = REQ_PHASE_SELINUX; - - /* Now is the time to call SELinux provider */ - ret = be_file_request(becli->bectx->bet_info[BET_SELINUX].pvt_bet_data, - req, - becli->bectx->bet_info[BET_SELINUX].bet_ops->handler); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "be_file_request failed.\n"); - goto done; - } - return; - } - } - - DEBUG(SSSDBG_CONF_SETTINGS, - "Sending result [%d][%s]\n", pd->pam_status, pd->domain); - dbus_req = (struct sbus_request *)req->pvt; - reply = dbus_message_new_method_return(dbus_req->message); - if (reply == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, - "dbus_message_new_method_return failed, cannot send reply.\n"); - goto done; - } - - dbret = dp_pack_pam_response(reply, pd); - if (!dbret) { - DEBUG(SSSDBG_CRIT_FAILURE, "Failed to generate dbus reply\n"); - dbus_message_unref(reply); - goto done; - } - - sbus_request_finish(dbus_req, reply); - dbus_message_unref(reply); - - DEBUG(SSSDBG_CONF_SETTINGS, - "Sent result [%d][%s]\n", pd->pam_status, pd->domain); - -done: - talloc_free(req); -} - -static int be_pam_handler(struct sbus_request *dbus_req, void *user_data) -{ - DBusError dbus_error; - DBusMessage *reply; - struct be_client *becli; - dbus_bool_t ret; - struct pam_data *pd = NULL; - struct be_req *be_req = NULL; - enum bet_type target = BET_NULL; - - becli = talloc_get_type(user_data, struct be_client); - if (!becli) return EINVAL; - - be_req = be_req_create(becli, becli, becli->bectx, "PAM", - be_pam_handler_callback, dbus_req); - if (!be_req) { - DEBUG(SSSDBG_TRACE_LIBS, "talloc_zero failed.\n"); - return ENOMEM; - } - - dbus_error_init(&dbus_error); - - ret = dp_unpack_pam_request(dbus_req->message, be_req, &pd, &dbus_error); - if (!ret) { - DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse message!\n"); - talloc_free(be_req); - return EIO; - } - - pd->pam_status = PAM_SYSTEM_ERR; - if (pd->domain == NULL) { - pd->domain = talloc_strdup(pd, becli->bectx->domain->name); - if (pd->domain == NULL) { - talloc_free(be_req); - return ENOMEM; - } - } - - ret = be_req_set_domain(be_req, pd->domain); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set request domain [%d]: %s\n", - ret, sss_strerror(ret)); - pd->pam_status = PAM_SYSTEM_ERR; - goto done; - } - - DEBUG(SSSDBG_CONF_SETTINGS, "Got request with the following data\n"); - DEBUG_PAM_DATA(SSSDBG_CONF_SETTINGS, pd); - - switch (pd->cmd) { - case SSS_PAM_AUTHENTICATE: - case SSS_PAM_PREAUTH: - target = BET_AUTH; - break; - case SSS_PAM_ACCT_MGMT: - target = BET_ACCESS; - be_req->phase = REQ_PHASE_ACCESS; - break; - case SSS_PAM_CHAUTHTOK: - case SSS_PAM_CHAUTHTOK_PRELIM: - target = BET_CHPASS; - break; - case SSS_PAM_OPEN_SESSION: - case SSS_PAM_SETCRED: - case SSS_PAM_CLOSE_SESSION: - pd->pam_status = PAM_SUCCESS; - goto done; - break; - default: - DEBUG(SSSDBG_TRACE_LIBS, - "Unsupported PAM command [%d].\n", pd->cmd); - pd->pam_status = PAM_MODULE_UNKNOWN; - goto done; - } - - /* return PAM_MODULE_UNKNOWN if corresponding backend target is not - * configured - */ - if (!becli->bectx->bet_info[target].bet_ops) { - DEBUG(SSSDBG_TRACE_LIBS, "Undefined backend target.\n"); - pd->pam_status = PAM_MODULE_UNKNOWN; - goto done; - } - - be_req->req_data = pd; - - ret = be_file_request(becli->bectx->bet_info[target].pvt_bet_data, - be_req, - becli->bectx->bet_info[target].bet_ops->handler); - if (ret != EOK) { - DEBUG(SSSDBG_TRACE_LIBS, "be_file_request failed.\n"); - goto done; - } - - return EOK; - -done: - - DEBUG(SSSDBG_CONF_SETTINGS, "Sending result [%d][%s]\n", - pd->pam_status, pd->domain); - - reply = dbus_message_new_method_return(dbus_req->message); - if (reply == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, - "dbus_message_new_method_return failed, cannot send reply.\n"); - return ENOMEM; - } - - ret = dp_pack_pam_response(reply, pd); - if (!ret) { - DEBUG(SSSDBG_CRIT_FAILURE, "Failed to generate dbus reply\n"); - talloc_free(be_req); - dbus_message_unref(reply); - return EIO; - } - - /* send reply back immediately */ - sbus_request_finish(dbus_req, reply); - dbus_message_unref(reply); - - talloc_free(be_req); - - return EOK; -} - -static int be_sudo_handler(struct sbus_request *dbus_req, void *user_data) -{ - DBusError dbus_error; - DBusMessageIter iter; - DBusMessageIter array_iter; - struct be_client *be_cli = NULL; - struct be_req *be_req = NULL; - struct be_sudo_req *sudo_req = NULL; - int ret = 0; - uint32_t type; - uint32_t rules_num = 0; - const char *rule = NULL; - const char *err_msg = NULL; - int i; - - DEBUG(SSSDBG_TRACE_FUNC, "Entering be_sudo_handler()\n"); - - be_cli = talloc_get_type(user_data, struct be_client); - if (be_cli == NULL) { - return EINVAL; - } - - /* create be request */ - be_req = be_req_create(be_cli, be_cli, be_cli->bectx, "sudo", - be_req_default_callback, dbus_req); - if (be_req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); - return ENOMEM; - } - - dbus_error_init(&dbus_error); - dbus_message_iter_init(dbus_req->message, &iter); - - /* get type of the request */ - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) { - DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n"); - ret = EIO; - err_msg = "Invalid D-Bus message format"; - goto fail; - } - dbus_message_iter_get_basic(&iter, &type); - dbus_message_iter_next(&iter); /* step behind the request type */ - - /* If we are offline and fast reply was requested - * return offline immediately - */ - if ((type & BE_REQ_FAST) && be_cli->bectx->offstat.offline) { - be_offline_reply(&dbus_req); - be_req->pvt = NULL; - /* This reply will be queued and sent - * when we reenter the mainloop. - * - * Continue processing in case we are - * going back online. - */ - } - - /* get and set sudo request data */ - sudo_req = talloc_zero(be_req, struct be_sudo_req); - if (sudo_req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); - goto fail; - } - - sudo_req->type = (~BE_REQ_FAST) & type; - - /* get additional arguments according to the request type */ - switch (sudo_req->type) { - case BE_REQ_SUDO_FULL: - /* no arguments required */ - break; - case BE_REQ_SUDO_RULES: - /* additional arguments: - * rules_num - * rules[rules_num] - */ - /* read rules_num */ - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) { - DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n"); - ret = EIO; - err_msg = "Invalid D-Bus message format"; - goto fail; - } - - dbus_message_iter_get_basic(&iter, &rules_num); - - sudo_req->rules = talloc_array(sudo_req, char*, rules_num + 1); - if (sudo_req->rules == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_array failed.\n"); - ret = ENOMEM; - goto fail; - } - - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { - DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n"); - ret = EIO; - err_msg = "Invalid D-Bus message format"; - goto fail; - } - - dbus_message_iter_recurse(&iter, &array_iter); - - /* read the rules */ - for (i = 0; i < rules_num; i++) { - if (dbus_message_iter_get_arg_type(&array_iter) - != DBUS_TYPE_STRING) { - DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n"); - ret = EIO; - err_msg = "Invalid D-Bus message format"; - goto fail; - } - - dbus_message_iter_get_basic(&array_iter, &rule); - sudo_req->rules[i] = talloc_strdup(sudo_req->rules, rule); - if (sudo_req->rules[i] == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n"); - ret = ENOMEM; - goto fail; - } - - dbus_message_iter_next(&array_iter); - } - - sudo_req->rules[rules_num] = NULL; - - break; - default: - DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type %d\n", sudo_req->type); - ret = EINVAL; - err_msg = "Invalid DP request type"; - goto fail; - } - - be_req->req_data = sudo_req; - - /* return an error if corresponding backend target is not configured */ - if (!be_cli->bectx->bet_info[BET_SUDO].bet_ops) { - DEBUG(SSSDBG_CRIT_FAILURE, "Undefined backend target.\n"); - ret = ENODEV; - goto fail; - } - - ret = be_file_request(be_cli->bectx->bet_info[BET_SUDO].pvt_bet_data, - be_req, - be_cli->bectx->bet_info[BET_SUDO].bet_ops->handler); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "be_file_request failed.\n"); - err_msg = "Cannot file back end request"; - goto fail; - } - - return EOK; - -fail: - /* send reply back immediately */ - be_req_default_callback(be_req, DP_ERR_FATAL, ret, - err_msg ? err_msg : strerror(ret)); - return EOK; -} - -static int be_autofs_handler(struct sbus_request *dbus_req, void *user_data) -{ - struct be_client *be_cli = NULL; - struct be_req *be_req = NULL; - struct be_autofs_req *be_autofs_req = NULL; - int ret = 0; - uint32_t type; - char *filter; - char *filter_val; - struct be_sbus_reply_data req_reply = BE_SBUS_REPLY_DATA_INIT; - - DEBUG(SSSDBG_TRACE_FUNC, "Entering be_autofs_handler()\n"); - - be_cli = talloc_get_type(user_data, struct be_client); - if (be_cli == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Cannot get back end client context\n"); - return EINVAL; - } - - if (!sbus_request_parse_or_finish(dbus_req, - DBUS_TYPE_UINT32, &type, - DBUS_TYPE_STRING, &filter, - DBUS_TYPE_INVALID)) - return EOK; /* handled */ - - /* If we are offline and fast reply was requested - * return offline immediately - */ - if ((type & BE_REQ_FAST) && be_cli->bectx->offstat.offline) { - be_offline_reply(&dbus_req); - /* This reply will be queued and sent - * when we reenter the mainloop. - * - * Continue processing in case we are - * going back online. - */ - } - - if (filter) { - if (strncmp(filter, "mapname=", 8) == 0) { - filter_val = &filter[8]; - } else { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, EINVAL, - "Invalid filter"); - goto done; - } - } else { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, EINVAL, - "Missing filter parameter"); - goto done; - } - - /* create be request */ - be_req = be_req_create(be_cli, be_cli, be_cli->bectx, "autofs", - be_req_default_callback, dbus_req); - if (be_req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENOMEM, - "Out of memory"); - goto done; - } - - /* set autofs request data */ - be_autofs_req = talloc_zero(be_req, struct be_autofs_req); - if (be_autofs_req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENOMEM, - "Out of memory"); - goto done; - } - - be_autofs_req->mapname = talloc_strdup(be_autofs_req, filter_val); - if (be_autofs_req->mapname == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n"); - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENOMEM, - "Out of memory"); - goto done; - } - - be_req->req_data = be_autofs_req; - - if (!be_cli->bectx->bet_info[BET_AUTOFS].bet_ops) { - DEBUG(SSSDBG_CRIT_FAILURE, "Undefined backend target.\n"); - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENODEV, - "Autofs back end target is not configured"); - goto done; - } - - ret = be_file_request(be_cli->bectx->bet_info[BET_AUTOFS].pvt_bet_data, - be_req, - be_cli->bectx->bet_info[BET_AUTOFS].bet_ops->handler); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "be_file_request failed.\n"); - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ret, - "Cannot file back end request"); - goto done; - } - - return EOK; - -done: - talloc_free(be_req); - be_sbus_req_reply_data(dbus_req, &req_reply); - return EOK; -} - -static int be_host_handler(struct sbus_request *dbus_req, void *user_data) -{ - struct be_host_req *req; - struct be_req *be_req; - struct be_client *becli; - uint32_t flags; - char *filter; - int ret; - struct be_sbus_reply_data req_reply = BE_SBUS_REPLY_DATA_INIT; - - be_req = NULL; - - becli = talloc_get_type(user_data, struct be_client); - if (!becli) return EINVAL; - - if (!sbus_request_parse_or_finish(dbus_req, - DBUS_TYPE_UINT32, &flags, - DBUS_TYPE_STRING, &filter, - DBUS_TYPE_INVALID)) - return EOK; /* request finished */ - - DEBUG(SSSDBG_TRACE_LIBS, - "Got request for [%u][%s]\n", flags, filter); - - /* If we are offline and fast reply was requested - * return offline immediately - */ - if ((flags & BE_REQ_FAST) && becli->bectx->offstat.offline) { - /* Send back an immediate reply */ - be_offline_reply(&dbus_req); - - /* This reply will be queued and sent - * when we reenter the mainloop. - * - * Continue processing in case we are - * going back online. - */ - } - - be_req = be_req_create(becli, becli, becli->bectx, "hostinfo", - be_req_default_callback, dbus_req); - if (!be_req) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENOMEM, - "Out of memory"); - goto done; - } - - req = talloc(be_req, struct be_host_req); - if (!req) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENOMEM, - "Out of memory"); - goto done; - } - req->type = BE_REQ_HOST | (flags & BE_REQ_FAST); - - be_req->req_data = req; - - if (filter) { - ret = strncmp(filter, "name=", 5); - if (ret == 0) { - req->filter_type = BE_FILTER_NAME; - ret = split_name_extended(req, &filter[5], - &req->name, - &req->alias); - } - - if (ret) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, EINVAL, - "Invalid filter"); - goto done; - } - } else { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, EINVAL, - "Missing filter parameter"); - goto done; - } - - /* process request */ - - if (!becli->bectx->bet_info[BET_HOSTID].bet_ops) { - DEBUG(SSSDBG_CRIT_FAILURE, "Undefined backend target.\n"); - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ENODEV, - "HostID back end target is not configured"); - goto done; - } - - ret = be_file_request(becli->bectx->bet_info[BET_HOSTID].pvt_bet_data, - be_req, - becli->bectx->bet_info[BET_HOSTID].bet_ops->handler); - if (ret != EOK) { - be_sbus_reply_data_set(&req_reply, DP_ERR_FATAL, ret, - "Cannot file back end request"); - goto done; - } - - return EOK; - -done: - talloc_free(be_req); - be_sbus_req_reply_data(dbus_req, &req_reply); - return EOK; -} - -static int be_client_destructor(void *ctx) -{ - struct be_client *becli = talloc_get_type(ctx, struct be_client); - if (becli->bectx) { - if (becli->bectx->nss_cli == becli) { - DEBUG(SSSDBG_TRACE_FUNC, "Removed NSS client\n"); - becli->bectx->nss_cli = NULL; - } else if (becli->bectx->pam_cli == becli) { - DEBUG(SSSDBG_TRACE_FUNC, "Removed PAM client\n"); - becli->bectx->pam_cli = NULL; - } else if (becli->bectx->sudo_cli == becli) { - DEBUG(SSSDBG_TRACE_FUNC, "Removed SUDO client\n"); - becli->bectx->sudo_cli = NULL; - } else if (becli->bectx->autofs_cli == becli) { - DEBUG(SSSDBG_TRACE_FUNC, "Removed autofs client\n"); - becli->bectx->autofs_cli = NULL; - } else if (becli->bectx->ssh_cli == becli) { - DEBUG(SSSDBG_TRACE_FUNC, "Removed SSH client\n"); - becli->bectx->ssh_cli = NULL; - } else if (becli->bectx->pac_cli == becli) { - DEBUG(SSSDBG_TRACE_FUNC, "Removed PAC client\n"); - becli->bectx->pac_cli = NULL; - } else if (becli->bectx->ifp_cli == becli) { - DEBUG(SSSDBG_TRACE_FUNC, "Removed IFP client\n"); - becli->bectx->ifp_cli = NULL; - } else { - DEBUG(SSSDBG_CRIT_FAILURE, "Unknown client removed ...\n"); - } - } - return 0; -} - -static int client_registration(struct sbus_request *dbus_req, void *data) -{ - dbus_uint16_t version = DATA_PROVIDER_VERSION; - struct sbus_connection *conn; - struct be_client *becli; - DBusError dbus_error; - dbus_uint16_t cli_ver; - char *cli_name; - dbus_bool_t dbret; - int ret; - - conn = dbus_req->conn; - becli = talloc_get_type(data, struct be_client); - if (!becli) { - DEBUG(SSSDBG_FATAL_FAILURE, "Connection holds no valid init data\n"); - return EINVAL; - } - - /* First thing, cancel the timeout */ - DEBUG(SSSDBG_CONF_SETTINGS, "Cancel DP ID timeout [%p]\n", becli->timeout); - talloc_zfree(becli->timeout); - - dbus_error_init(&dbus_error); - - dbret = dbus_message_get_args(dbus_req->message, &dbus_error, - DBUS_TYPE_UINT16, &cli_ver, - DBUS_TYPE_STRING, &cli_name, - DBUS_TYPE_INVALID); - if (!dbret) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Failed to parse message, killing connection\n"); - if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error); - sbus_disconnect(conn); - /* FIXME: should we just talloc_zfree(conn) ? */ - return EIO; - } - - if (strcasecmp(cli_name, "NSS") == 0) { - becli->bectx->nss_cli = becli; - } else if (strcasecmp(cli_name, "PAM") == 0) { - becli->bectx->pam_cli = becli; - } else if (strcasecmp(cli_name, "SUDO") == 0) { - becli->bectx->sudo_cli = becli; - } else if (strcasecmp(cli_name, "autofs") == 0) { - becli->bectx->autofs_cli = becli; - } else if (strcasecmp(cli_name, "SSH") == 0) { - becli->bectx->ssh_cli = becli; - } else if (strcasecmp(cli_name, "PAC") == 0) { - becli->bectx->pac_cli = becli; - } else if (strcasecmp(cli_name, "InfoPipe") == 0) { - becli->bectx->ifp_cli = becli; - } else { - DEBUG(SSSDBG_CRIT_FAILURE, "Unknown client! [%s]\n", cli_name); - } - talloc_set_destructor((TALLOC_CTX *)becli, be_client_destructor); - - DEBUG(SSSDBG_CONF_SETTINGS, "Added Frontend client [%s]\n", cli_name); - - /* reply that all is ok */ - ret = sbus_request_return_and_finish(dbus_req, - DBUS_TYPE_UINT16, &version, - DBUS_TYPE_INVALID); - if (ret != EOK) { - sbus_disconnect(conn); - return ret; - } - - becli->initialized = true; - return EOK; -} - -static errno_t be_file_check_online_request(struct be_req *req) -{ - int ret; - - req->be_ctx->offstat.went_offline = time(NULL); - reset_fo(req->be_ctx); - - ret = be_file_request(req->be_ctx, req, - req->be_ctx->bet_info[BET_ID].bet_ops->check_online); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "be_file_request failed.\n"); - } - - return ret; -} - -static void check_online_callback(struct be_req *req, int dp_err_type, - int errnum, const char *errstr) -{ - int ret; - - DEBUG(SSSDBG_CONF_SETTINGS, "Backend returned: (%d, %d, %s) [%s]\n", - dp_err_type, errnum, errstr?errstr:"", - dp_err_to_string(dp_err_type)); - - req->be_ctx->check_online_ref_count--; - - if (dp_err_type != DP_ERR_OK && req->be_ctx->check_online_ref_count > 0) { - ret = be_file_check_online_request(req); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "be_file_check_online_request failed.\n"); - goto done; - } - return; - } - -done: - req->be_ctx->check_online_ref_count = 0; - if (dp_err_type != DP_ERR_OFFLINE) { - if (dp_err_type != DP_ERR_OK) { - reset_fo(req->be_ctx); - } - be_reset_offline(req->be_ctx); - } - - talloc_free(req); - - return; -} - -static void check_if_online(struct be_ctx *ctx) -{ - int ret; - struct be_req *be_req = NULL; - - be_run_unconditional_online_cb(ctx); - - if (ctx->offstat.offline == false) { - DEBUG(SSSDBG_TRACE_INTERNAL, - "Backend is already online, nothing to do.\n"); - return; - } - - /* Make sure nobody tries to go online while we are checking */ - ctx->offstat.went_offline = time(NULL); - - DEBUG(SSSDBG_TRACE_INTERNAL, "Trying to go back online!\n"); - - ctx->check_online_ref_count++; - - if (ctx->check_online_ref_count != 1) { - DEBUG(SSSDBG_TRACE_INTERNAL, - "There is an online check already running.\n"); - return; - } - - if (ctx->bet_info[BET_ID].bet_ops->check_online == NULL) { + if (!dp_method_enabled(be_ctx->provider, DPT_ID, DPM_CHECK_ONLINE)) { DEBUG(SSSDBG_TRACE_INTERNAL, "ID providers does not provide a check_online method.\n"); goto failed; } - be_req = be_req_create(ctx, NULL, ctx, "online check", - check_online_callback, NULL); - if (be_req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); - goto failed; - } - - ret = be_file_check_online_request(be_req); + ret = be_check_online_request(be_ctx); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "be_file_check_online_request failed.\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create check online req.\n"); goto failed; } return; failed: - ctx->check_online_ref_count--; + be_ctx->check_online_ref_count--; DEBUG(SSSDBG_CRIT_FAILURE, "Failed to run a check_online test.\n"); - talloc_free(be_req); - - if (ctx->check_online_ref_count == 0) { - reset_fo(ctx); - be_reset_offline(ctx); + if (be_ctx->check_online_ref_count == 0) { + reset_fo(be_ctx); + be_reset_offline(be_ctx); } return; } -static void init_timeout(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval t, void *ptr) -{ - struct be_client *becli; - - DEBUG(SSSDBG_OP_FAILURE, - "Client timed out before Identification [%p]!\n", te); - - becli = talloc_get_type(ptr, struct be_client); - - sbus_disconnect(becli->conn); - talloc_zfree(becli); -} - -static int be_client_init(struct sbus_connection *conn, void *data) -{ - struct be_ctx *bectx; - struct be_client *becli; - struct timeval tv; - - bectx = talloc_get_type(data, struct be_ctx); - - /* hang off this memory to the connection so that when the connection - * is freed we can potentially call a destructor */ - - becli = talloc(conn, struct be_client); - if (!becli) { - DEBUG(SSSDBG_FATAL_FAILURE,"Out of memory?!\n"); - talloc_zfree(conn); - return ENOMEM; - } - becli->bectx = bectx; - becli->conn = conn; - becli->initialized = false; - - /* Allow access from the SSSD user */ - sbus_allow_uid(conn, &bectx->uid); - - /* 5 seconds should be plenty */ - tv = tevent_timeval_current_ofs(5, 0); - - becli->timeout = tevent_add_timer(bectx->ev, becli, - tv, init_timeout, becli); - if (!becli->timeout) { - DEBUG(SSSDBG_FATAL_FAILURE,"Out of memory?!\n"); - talloc_zfree(conn); - return ENOMEM; - } - DEBUG(SSSDBG_CONF_SETTINGS, - "Set-up Backend ID timeout [%p]\n", becli->timeout); - - return sbus_conn_register_iface(conn, &be_methods.vtable, DP_PATH, becli); -} - -/* be_srv_init - * set up per-domain sbus channel */ -static int be_srv_init(struct be_ctx *ctx, - uid_t uid, gid_t gid) -{ - char *sbus_address; - int ret; - - /* Set up SBUS connection to the monitor */ - ret = dp_get_sbus_address(ctx, &sbus_address, ctx->domain->name); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, "Could not get sbus backend address.\n"); - return ret; - } - - ctx->uid = uid; - ctx->gid = gid; - - ret = sbus_new_server(ctx, ctx->ev, sbus_address, uid, gid, - true, &ctx->sbus_srv, be_client_init, ctx); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, "Could not set up sbus server.\n"); - return ret; - } - - return EOK; -} - -static void be_target_access_permit(struct be_req *be_req) -{ - struct pam_data *pd = - talloc_get_type(be_req_get_data(be_req), struct pam_data); - DEBUG(SSSDBG_TRACE_ALL, - "be_target_access_permit called, returning PAM_SUCCESS.\n"); - - pd->pam_status = PAM_SUCCESS; - be_req_terminate(be_req, DP_ERR_OK, PAM_SUCCESS, NULL); -} - -static struct bet_ops be_target_access_permit_ops = { - .check_online = NULL, - .handler = be_target_access_permit, - .finalize = NULL -}; - -static void be_target_access_deny(struct be_req *be_req) -{ - struct pam_data *pd = - talloc_get_type(be_req_get_data(be_req), struct pam_data); - DEBUG(SSSDBG_TRACE_ALL, - "be_target_access_deny called, returning PAM_PERM_DENIED.\n"); - - pd->pam_status = PAM_PERM_DENIED; - be_req_terminate(be_req, DP_ERR_OK, PAM_PERM_DENIED, NULL); -} - -static struct bet_ops be_target_access_deny_ops = { - .check_online = NULL, - .handler = be_target_access_deny, - .finalize = NULL -}; - -static int load_backend_module(struct be_ctx *ctx, - enum bet_type bet_type, - struct bet_info *bet_info, - const char *default_mod_name) -{ - TALLOC_CTX *tmp_ctx; - int ret = EINVAL; - bool already_loaded = false; - int lb=0; - char *mod_name = NULL; - char *path = NULL; - void *handle; - char *mod_init_fn_name = NULL; - bet_init_fn_t mod_init_fn = NULL; - - (*bet_info).bet_type = bet_type; - (*bet_info).mod_name = NULL; - (*bet_info).bet_ops = NULL; - (*bet_info).pvt_bet_data = NULL; - - if (bet_type <= BET_NULL || bet_type >= BET_MAX || - bet_type != bet_data[bet_type].bet_type) { - DEBUG(SSSDBG_OP_FAILURE, "invalid bet_type or bet_data corrupted.\n"); - return EINVAL; - } - - tmp_ctx = talloc_new(ctx); - if (!tmp_ctx) { - DEBUG(SSSDBG_TRACE_LIBS, "talloc_new failed.\n"); - return ENOMEM; - } - - ret = confdb_get_string(ctx->cdb, tmp_ctx, ctx->conf_path, - bet_data[bet_type].option_name, NULL, - &mod_name); - if (ret != EOK) { - ret = EFAULT; - goto done; - } - if (!mod_name) { - if (default_mod_name != NULL) { - DEBUG(SSSDBG_FUNC_DATA, - "no module name found in confdb, using [%s].\n", - default_mod_name); - mod_name = talloc_strdup(ctx, default_mod_name); - } else { - ret = ENOENT; - goto done; - } - } - - if (strcasecmp(mod_name, NO_PROVIDER) == 0) { - ret = ENOENT; - goto done; - } - - if (bet_type == BET_ACCESS) { - if (strcmp(mod_name, ACCESS_PERMIT) == 0) { - (*bet_info).bet_ops = &be_target_access_permit_ops; - (*bet_info).pvt_bet_data = NULL; - (*bet_info).mod_name = talloc_strdup(ctx, ACCESS_PERMIT); - - ret = EOK; - goto done; - } - if (strcmp(mod_name, ACCESS_DENY) == 0) { - (*bet_info).bet_ops = &be_target_access_deny_ops; - (*bet_info).pvt_bet_data = NULL; - (*bet_info).mod_name = talloc_strdup(ctx, ACCESS_DENY); - - ret = EOK; - goto done; - } - } - - mod_init_fn_name = talloc_asprintf(tmp_ctx, - bet_data[bet_type].mod_init_fn_name_fmt, - mod_name); - if (mod_init_fn_name == NULL) { - DEBUG(SSSDBG_TRACE_LIBS, "talloc_asprintf failed\n"); - ret = ENOMEM; - goto done; - } - - - lb = 0; - while(ctx->loaded_be[lb].be_name != NULL) { - if (strncmp(ctx->loaded_be[lb].be_name, mod_name, - strlen(mod_name)) == 0) { - DEBUG(SSSDBG_TRACE_LIBS, - "Backend [%s] already loaded.\n", mod_name); - already_loaded = true; - break; - } - - ++lb; - if (lb >= BET_MAX) { - DEBUG(SSSDBG_OP_FAILURE, "Backend context corrupted.\n"); - ret = EINVAL; - goto done; - } - } - - if (!already_loaded) { - path = talloc_asprintf(tmp_ctx, "%s/libsss_%s.so", - DATA_PROVIDER_PLUGINS_PATH, mod_name); - if (!path) { - ret = ENOMEM; - goto done; - } - - DEBUG(SSSDBG_TRACE_LIBS, - "Loading backend [%s] with path [%s].\n", mod_name, path); - handle = dlopen(path, RTLD_NOW); - if (!handle) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Unable to load %s module with path (%s), error: %s\n", - mod_name, path, dlerror()); - ret = ELIBACC; - goto done; - } - - ctx->loaded_be[lb].be_name = talloc_strdup(ctx, mod_name); - ctx->loaded_be[lb].handle = handle; - } - - mod_init_fn = (bet_init_fn_t)dlsym(ctx->loaded_be[lb].handle, - mod_init_fn_name); - if (mod_init_fn == NULL) { - if (default_mod_name != NULL && - strcmp(default_mod_name, mod_name) == 0 ) { - /* If the default is used and fails we indicate this to the caller - * by returning ENOENT. Ths way the caller can decide how to - * handle the different types of error conditions. */ - ret = ENOENT; - } else { - DEBUG(SSSDBG_FATAL_FAILURE, - "Unable to load init fn %s from module %s, error: %s\n", - mod_init_fn_name, mod_name, dlerror()); - ret = ELIBBAD; - } - goto done; - } - - ret = mod_init_fn(ctx, &(*bet_info).bet_ops, &(*bet_info).pvt_bet_data); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Error (%d) in module (%s) initialization (%s)!\n", - ret, mod_name, mod_init_fn_name); - goto done; - } - - (*bet_info).mod_name = talloc_strdup(ctx, mod_name); - - ret = EOK; - -done: - talloc_free(tmp_ctx); - return ret; -} - static void signal_be_offline(struct tevent_context *ev, struct tevent_signal *se, int signum, @@ -2421,308 +366,119 @@ static void signal_be_reset_offline(struct tevent_context *ev, check_if_online(ctx); } -int be_process_init_sudo(struct be_ctx *be_ctx) -{ - TALLOC_CTX *tmp_ctx = NULL; - char **services = NULL; - char *provider = NULL; - bool responder_enabled = false; - int i; - int ret; - - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); - return ENOMEM; - } - - ret = confdb_get_string_as_list(be_ctx->cdb, tmp_ctx, - CONFDB_MONITOR_CONF_ENTRY, - CONFDB_MONITOR_ACTIVE_SERVICES, &services); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, "Unable to read from confdb [%d]: %s\n", - ret, strerror(ret)); - goto done; - } - - for (i = 0; services[i] != NULL; i++) { - if (strcmp(services[i], "sudo") == 0) { - responder_enabled = true; - break; - } - } - - ret = confdb_get_string(be_ctx->cdb, tmp_ctx, be_ctx->conf_path, - CONFDB_DOMAIN_SUDO_PROVIDER, NULL, &provider); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, "Unable to read from confdb [%d]: %s\n", - ret, strerror(ret)); - goto done; - } - - if (!responder_enabled && provider == NULL) { - /* provider is not set explicitly */ - DEBUG(SSSDBG_TRACE_FUNC, - "SUDO is not listed in services, disabling SUDO module.\n"); - ret = ENOENT; - goto done; - } - - if (!responder_enabled && provider != NULL - && strcmp(provider, NO_PROVIDER) != 0) { - /* provider is set but responder is disabled */ - DEBUG(SSSDBG_MINOR_FAILURE, "SUDO provider is set, but it is not " - "listed in active services. SUDO support will not work!\n"); - } - - ret = load_backend_module(be_ctx, BET_SUDO, &be_ctx->bet_info[BET_SUDO], - be_ctx->bet_info[BET_ID].mod_name); - -done: - talloc_free(tmp_ctx); - return ret; -} - -int be_process_init(TALLOC_CTX *mem_ctx, - const char *be_domain, - uid_t uid, gid_t gid, - struct tevent_context *ev, - struct confdb_ctx *cdb) +errno_t be_process_init(TALLOC_CTX *mem_ctx, + const char *be_domain, + uid_t uid, + gid_t gid, + struct tevent_context *ev, + struct confdb_ctx *cdb) { - struct be_ctx *ctx; + uint32_t refresh_interval; struct tevent_signal *tes; - int ret; + struct be_ctx *be_ctx; + errno_t ret; - ctx = talloc_zero(mem_ctx, struct be_ctx); - if (!ctx) { - DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing be_ctx\n"); + be_ctx = talloc_zero(mem_ctx, struct be_ctx); + if (be_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "talloc_zero() failed\n"); return ENOMEM; } - ctx->ev = ev; - ctx->cdb = cdb; - ctx->identity = talloc_asprintf(ctx, "%%BE_%s", be_domain); - ctx->conf_path = talloc_asprintf(ctx, CONFDB_DOMAIN_PATH_TMPL, be_domain); - if (!ctx->identity || !ctx->conf_path) { + + be_ctx->ev = ev; + be_ctx->cdb = cdb; + be_ctx->identity = talloc_asprintf(be_ctx, "%%BE_%s", be_domain); + be_ctx->conf_path = talloc_asprintf(be_ctx, CONFDB_DOMAIN_PATH_TMPL, be_domain); + if (be_ctx->identity == NULL || be_ctx->conf_path == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!?\n"); ret = ENOMEM; - goto fail; + goto done; } - ret = be_init_failover(ctx); + ret = be_init_failover(be_ctx); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "fatal error initializing failover context\n"); - goto fail; + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize failover\n"); + goto done; } - ret = sssd_domain_init(ctx, cdb, be_domain, DB_PATH, &ctx->domain); + ret = sssd_domain_init(be_ctx, cdb, be_domain, DB_PATH, &be_ctx->domain); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, "fatal error opening cache database\n"); - goto fail; + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize domain\n"); + goto done; } - ret = sss_monitor_init(ctx, ctx->ev, &monitor_be_methods, - ctx->identity, DATA_PROVIDER_VERSION, - ctx, &ctx->mon_conn); + ret = sss_monitor_init(be_ctx, be_ctx->ev, &monitor_be_methods, + be_ctx->identity, DATA_PROVIDER_VERSION, + be_ctx, &be_ctx->mon_conn); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "fatal error setting up monitor bus\n"); - goto fail; + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize monitor connection\n"); + goto done; } /* We need this for subdomains support, as they have to store fully - * qualified user and group names for now */ - ret = sss_names_init(ctx->domain, cdb, - ctx->domain->name, &ctx->domain->names); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "fatal error setting fully qualified name format for %s\n", - ctx->domain->name); - goto fail; - } - - ret = be_srv_init(ctx, uid, gid); + * qualified user and group names for now. */ + ret = sss_names_init(be_ctx->domain, cdb, be_ctx->domain->name, + &be_ctx->domain->names); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up server bus\n"); - goto fail; + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to setup fully qualified name " + "format for %s\n", be_ctx->domain->name); + goto done; } /* Initialize be_refresh periodic task. */ - ctx->refresh_ctx = be_refresh_ctx_init(ctx); - if (ctx->refresh_ctx == NULL) { + be_ctx->refresh_ctx = be_refresh_ctx_init(be_ctx); + if (be_ctx->refresh_ctx == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n"); ret = ENOMEM; - goto fail; + goto done; } - if (ctx->domain->refresh_expired_interval > 0) { - ret = be_ptask_create(ctx, ctx, ctx->domain->refresh_expired_interval, - 30, 5, 0, ctx->domain->refresh_expired_interval, - BE_PTASK_OFFLINE_SKIP, 0, + refresh_interval = be_ctx->domain->refresh_expired_interval; + if (refresh_interval > 0) { + ret = be_ptask_create(be_ctx, be_ctx, refresh_interval, 30, 5, 0, + refresh_interval, BE_PTASK_OFFLINE_SKIP, 0, be_refresh_send, be_refresh_recv, - ctx->refresh_ctx, "Refresh Records", NULL); + be_ctx->refresh_ctx, "Refresh Records", NULL); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh periodic task\n"); - goto fail; - } - } - - ret = load_backend_module(ctx, BET_ID, - &ctx->bet_info[BET_ID], NULL); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "fatal error initializing data providers\n"); - goto fail; - } - DEBUG(SSSDBG_TRACE_INTERNAL, - "ID backend target successfully loaded from provider [%s].\n", - ctx->bet_info[BET_ID].mod_name); - - ret = load_backend_module(ctx, BET_AUTH, - &ctx->bet_info[BET_AUTH], - ctx->bet_info[BET_ID].mod_name); - if (ret != EOK) { - if (ret != ENOENT) { - DEBUG(SSSDBG_FATAL_FAILURE, - "fatal error initializing data providers\n"); - goto fail; - } - DEBUG(SSSDBG_MINOR_FAILURE, - "No authentication module provided for [%s] !!\n", - be_domain); - } else { - DEBUG(SSSDBG_TRACE_INTERNAL, - "AUTH backend target successfully loaded " - "from provider [%s].\n", ctx->bet_info[BET_AUTH].mod_name); - } - - ret = load_backend_module(ctx, BET_ACCESS, &ctx->bet_info[BET_ACCESS], - ACCESS_PERMIT); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to setup ACCESS backend.\n"); - goto fail; - } - DEBUG(SSSDBG_TRACE_INTERNAL, - "ACCESS backend target successfully loaded " - "from provider [%s].\n", ctx->bet_info[BET_ACCESS].mod_name); - - ret = load_backend_module(ctx, BET_CHPASS, - &ctx->bet_info[BET_CHPASS], - ctx->bet_info[BET_AUTH].mod_name); - if (ret != EOK) { - if (ret != ENOENT) { - DEBUG(SSSDBG_FATAL_FAILURE, - "fatal error initializing data providers\n"); - goto fail; - } - DEBUG(SSSDBG_MINOR_FAILURE, - "No change password module provided for [%s] !!\n", - be_domain); - } else { - DEBUG(SSSDBG_TRACE_INTERNAL, - "CHPASS backend target successfully loaded " - "from provider [%s].\n", ctx->bet_info[BET_CHPASS].mod_name); - } - - ret = be_process_init_sudo(ctx); - if (ret != EOK) { - if (ret != ENOENT) { - DEBUG(SSSDBG_FATAL_FAILURE, - "fatal error initializing data providers\n"); - goto fail; - } - DEBUG(SSSDBG_MINOR_FAILURE, - "No SUDO module provided for [%s] !!\n", be_domain); - } else { - DEBUG(SSSDBG_TRACE_INTERNAL, - "SUDO backend target successfully loaded " - "from provider [%s].\n", ctx->bet_info[BET_SUDO].mod_name); - } - - ret = load_backend_module(ctx, BET_AUTOFS, - &ctx->bet_info[BET_AUTOFS], - ctx->bet_info[BET_ID].mod_name); - if (ret != EOK) { - if (ret != ENOENT) { - DEBUG(SSSDBG_FATAL_FAILURE, - "fatal error initializing data providers\n"); - goto fail; - } - DEBUG(SSSDBG_MINOR_FAILURE, - "No autofs module provided for [%s] !!\n", be_domain); - } else { - DEBUG(SSSDBG_TRACE_INTERNAL, - "autofs backend target successfully loaded " - "from provider [%s].\n", ctx->bet_info[BET_AUTOFS].mod_name); - } - - ret = load_backend_module(ctx, BET_SELINUX, - &ctx->bet_info[BET_SELINUX], - ctx->bet_info[BET_ID].mod_name); - if (ret != EOK) { - if (ret != ENOENT) { - DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing data providers\n"); - goto fail; - } - DEBUG(SSSDBG_CRIT_FAILURE, "No selinux module provided for [%s] !!\n", - be_domain); - } else { - DEBUG(SSSDBG_TRACE_ALL, "selinux backend target successfully loaded " - "from provider [%s].\n", ctx->bet_info[BET_SELINUX].mod_name); - } - - ret = load_backend_module(ctx, BET_HOSTID, - &ctx->bet_info[BET_HOSTID], - ctx->bet_info[BET_ID].mod_name); - if (ret != EOK) { - if (ret != ENOENT) { - DEBUG(SSSDBG_FATAL_FAILURE, - "fatal error initializing data providers\n"); - goto fail; + goto done; } - DEBUG(SSSDBG_CRIT_FAILURE, - "No host info module provided for [%s] !!\n", be_domain); - } else { - DEBUG(SSSDBG_TRACE_ALL, - "HOST backend target successfully loaded from provider [%s].\n", - ctx->bet_info[BET_HOSTID].mod_name); } - ret = load_backend_module(ctx, BET_SUBDOMAINS, - &ctx->bet_info[BET_SUBDOMAINS], - ctx->bet_info[BET_ID].mod_name); + ret = dp_init(be_ctx->ev, be_ctx, be_ctx->uid, be_ctx->gid); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Subdomains are not supported for [%s] !!\n", be_domain); - } else { - DEBUG(SSSDBG_TRACE_ALL, "Get-Subdomains backend target successfully loaded " - "from provider [%s].\n", - ctx->bet_info[BET_SUBDOMAINS].mod_name); + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to setup data provider " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; } /* Handle SIGUSR1 to force offline behavior */ BlockSignals(false, SIGUSR1); - tes = tevent_add_signal(ctx->ev, ctx, SIGUSR1, 0, - signal_be_offline, ctx); + tes = tevent_add_signal(be_ctx->ev, be_ctx, SIGUSR1, 0, + signal_be_offline, be_ctx); if (tes == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to setup SIGUSR1 handler\n"); ret = EIO; - goto fail; + goto done; } /* Handle SIGUSR2 to force going online */ BlockSignals(false, SIGUSR2); - tes = tevent_add_signal(ctx->ev, ctx, SIGUSR2, 0, - signal_be_reset_offline, ctx); + tes = tevent_add_signal(be_ctx->ev, be_ctx, SIGUSR2, 0, + signal_be_reset_offline, be_ctx); if (tes == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to setup SIGUSR2 handler\n"); ret = EIO; - goto fail; + goto done; } - return EOK; + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(be_ctx); + } -fail: - talloc_free(ctx); return ret; } diff --git a/src/providers/data_provider_req.c b/src/providers/data_provider_req.c index 94aacb676..7750c2cf0 100644 --- a/src/providers/data_provider_req.c +++ b/src/providers/data_provider_req.c @@ -21,38 +21,33 @@ #include "providers/data_provider_req.h" -#define be_req_to_str(req_type, be_req_t) \ - ((req_type) & BE_REQ_FAST) ? "FAST " #be_req_t : #be_req_t +#define be_req_to_str(be_req_t) #be_req_t const char *be_req2str(dbus_uint32_t req_type) { switch (req_type & BE_REQ_TYPE_MASK) { case BE_REQ_USER: - return be_req_to_str(req_type, BE_REQ_USER); + return be_req_to_str(BE_REQ_USER); case BE_REQ_GROUP: - return be_req_to_str(req_type, BE_REQ_GROUP); + return be_req_to_str(BE_REQ_GROUP); case BE_REQ_INITGROUPS: - return be_req_to_str(req_type, BE_REQ_INITGROUPS); + return be_req_to_str(BE_REQ_INITGROUPS); case BE_REQ_NETGROUP: - return be_req_to_str(req_type, BE_REQ_NETGROUP); + return be_req_to_str(BE_REQ_NETGROUP); case BE_REQ_SERVICES: - return be_req_to_str(req_type, BE_REQ_SERVICES); + return be_req_to_str(BE_REQ_SERVICES); case BE_REQ_SUDO_FULL: - return be_req_to_str(req_type, BE_REQ_SUDO_FULL); + return be_req_to_str(BE_REQ_SUDO_FULL); case BE_REQ_SUDO_RULES: - return be_req_to_str(req_type, BE_REQ_SUDO_RULES); - case BE_REQ_AUTOFS: - return be_req_to_str(req_type, BE_REQ_AUTOFS); - case BE_REQ_HOST: - return be_req_to_str(req_type, BE_REQ_HOST); + return be_req_to_str(BE_REQ_SUDO_RULES); case BE_REQ_BY_SECID: - return be_req_to_str(req_type, BE_REQ_BY_SECID); + return be_req_to_str(BE_REQ_BY_SECID); case BE_REQ_USER_AND_GROUP: - return be_req_to_str(req_type, BE_REQ_USER_AND_GROUP); + return be_req_to_str(BE_REQ_USER_AND_GROUP); case BE_REQ_BY_UUID: - return be_req_to_str(req_type, BE_REQ_BY_UUID); + return be_req_to_str(BE_REQ_BY_UUID); case BE_REQ_BY_CERT: - return be_req_to_str(req_type, BE_REQ_BY_CERT); + return be_req_to_str(BE_REQ_BY_CERT); } return "UNKNOWN_REQ"; } diff --git a/src/providers/data_provider_req.h b/src/providers/data_provider_req.h index a2889cda5..2dde81e4e 100644 --- a/src/providers/data_provider_req.h +++ b/src/providers/data_provider_req.h @@ -33,14 +33,11 @@ #define BE_REQ_SERVICES 0x0005 #define BE_REQ_SUDO_FULL 0x0006 #define BE_REQ_SUDO_RULES 0x0007 -#define BE_REQ_AUTOFS 0x0009 -#define BE_REQ_HOST 0x0010 #define BE_REQ_BY_SECID 0x0011 #define BE_REQ_USER_AND_GROUP 0x0012 #define BE_REQ_BY_UUID 0x0013 #define BE_REQ_BY_CERT 0x0014 #define BE_REQ_TYPE_MASK 0x00FF -#define BE_REQ_FAST 0x1000 /** * @brief Convert request type to string for logging purpose. diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c index cc780217d..bebb47bf3 100644 --- a/src/providers/ipa/ipa_access.c +++ b/src/providers/ipa/ipa_access.c @@ -72,23 +72,6 @@ void hbac_debug_messages(const char *file, int line, } } -static void ipa_access_reply(struct hbac_ctx *hbac_ctx, int pam_status) -{ - struct be_req *be_req = hbac_ctx->be_req; - struct pam_data *pd; - pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); - pd->pam_status = pam_status; - - /* destroy HBAC context now to release all used resources and LDAP connection */ - talloc_zfree(hbac_ctx); - - if (pam_status == PAM_SUCCESS || pam_status == PAM_PERM_DENIED) { - be_req_terminate(be_req, DP_ERR_OK, pam_status, NULL); - } else { - be_req_terminate(be_req, DP_ERR_FATAL, pam_status, NULL); - } -} - enum hbac_result { HBAC_ALLOW = 1, HBAC_DENY, @@ -101,285 +84,180 @@ enum check_result { RULE_ERROR }; -static void ipa_hbac_check(struct tevent_req *req); -static int hbac_retry(struct hbac_ctx *hbac_ctx); -static void hbac_connect_done(struct tevent_req *subreq); -static bool hbac_check_step_result(struct hbac_ctx *hbac_ctx, int ret); - -static int hbac_get_host_info_step(struct hbac_ctx *hbac_ctx); +struct ipa_fetch_hbac_state { + struct tevent_context *ev; + struct be_ctx *be_ctx; + struct sdap_id_ctx *sdap_ctx; + struct ipa_access_ctx *access_ctx; + struct sdap_id_op *sdap_op; + struct dp_option *ipa_options; + struct time_rules_ctx *tr_ctx; + + struct sdap_search_base **search_bases; + + /* Hosts */ + size_t host_count; + struct sysdb_attrs **hosts; + size_t hostgroup_count; + struct sysdb_attrs **hostgroups; + struct sysdb_attrs *ipa_host; + + /* Rules */ + size_t rule_count; + struct sysdb_attrs **rules; -static void ipa_hbac_evaluate_rules(struct hbac_ctx *hbac_ctx); + /* Services */ + size_t service_count; + struct sysdb_attrs **services; + size_t servicegroup_count; + struct sysdb_attrs **servicegroups; +}; -void ipa_access_handler(struct be_req *be_req) +static errno_t ipa_fetch_hbac_retry(struct tevent_req *req); +static void ipa_fetch_hbac_connect_done(struct tevent_req *subreq); +static errno_t ipa_fetch_hbac_hostinfo(struct tevent_req *req); +static void ipa_fetch_hbac_hostinfo_done(struct tevent_req *subreq); +static void ipa_fetch_hbac_services_done(struct tevent_req *subreq); +static void ipa_fetch_hbac_rules_done(struct tevent_req *subreq); +static errno_t ipa_purge_hbac(struct sss_domain_info *domain); +static errno_t ipa_save_hbac(struct sss_domain_info *domain, + struct ipa_fetch_hbac_state *state); + +static struct tevent_req * +ipa_fetch_hbac_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct ipa_access_ctx *access_ctx) { - struct pam_data *pd; - struct ipa_access_ctx *ipa_access_ctx; + struct ipa_fetch_hbac_state *state; struct tevent_req *req; - struct sss_domain_info *dom; - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - - pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); - - ipa_access_ctx = talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data, - struct ipa_access_ctx); - - dom = be_ctx->domain; - if (strcasecmp(pd->domain, be_ctx->domain->name) != 0) { - /* Subdomain request, verify subdomain */ - dom = find_domain_by_name(be_ctx->domain, pd->domain, true); - } - - /* First, verify that this account isn't locked. - * We need to do this in case the auth phase was - * skipped (such as during GSSAPI single-sign-on - * or SSH public key exchange. - */ - req = sdap_access_send(be_req, be_ctx->ev, be_ctx, dom, - ipa_access_ctx->sdap_access_ctx, - ipa_access_ctx->sdap_access_ctx->id_ctx->conn, - pd); - if (!req) { - be_req_terminate(be_req, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL); - return; - } - tevent_req_set_callback(req, ipa_hbac_check, be_req); -} - -static void ipa_hbac_check(struct tevent_req *req) -{ - struct be_req *be_req; - struct be_ctx *be_ctx; - struct pam_data *pd; - struct hbac_ctx *hbac_ctx = NULL; - struct ipa_access_ctx *ipa_access_ctx; - int ret; - - be_req = tevent_req_callback_data(req, struct be_req); - be_ctx = be_req_get_be_ctx(be_req); - pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); - - ret = sdap_access_recv(req); - talloc_zfree(req); + time_t now, refresh_interval; + bool offline; + errno_t ret; - switch(ret) { - case EOK: - /* Account wasn't locked. Continue below - * to HBAC processing. - */ - break; - case ERR_ACCESS_DENIED: - /* Account was locked. Return permission denied - * here. - */ - pd->pam_status = PAM_PERM_DENIED; - be_req_terminate(be_req, DP_ERR_OK, pd->pam_status, NULL); - return; - case ERR_ACCOUNT_EXPIRED: - pd->pam_status = PAM_ACCT_EXPIRED; - be_req_terminate(be_req, DP_ERR_OK, pd->pam_status, NULL); - return; - default: - /* We got an unexpected error. Return it as-is */ - pd->pam_status = PAM_SYSTEM_ERR; - be_req_terminate(be_req, DP_ERR_FATAL, pd->pam_status, - sss_strerror(ret)); - return; + req = tevent_req_create(mem_ctx, &state, + struct ipa_fetch_hbac_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } - hbac_ctx = talloc_zero(be_req, struct hbac_ctx); - if (hbac_ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n"); - ret = ENOMEM; - goto fail; - } - - hbac_ctx->be_req = be_req; - hbac_ctx->pd = pd; - ipa_access_ctx = talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data, - struct ipa_access_ctx); - hbac_ctx->access_ctx = ipa_access_ctx; - hbac_ctx->sdap_ctx = ipa_access_ctx->sdap_ctx; - hbac_ctx->ipa_options = ipa_access_ctx->ipa_options; - hbac_ctx->tr_ctx = ipa_access_ctx->tr_ctx; - hbac_ctx->search_bases = ipa_access_ctx->hbac_search_bases; - if (hbac_ctx->search_bases == NULL) { + state->ev = ev; + state->be_ctx = be_ctx; + state->access_ctx = access_ctx; + state->sdap_ctx = access_ctx->sdap_ctx; + state->ipa_options = access_ctx->ipa_options; + state->tr_ctx = access_ctx->tr_ctx; + state->search_bases = access_ctx->hbac_search_bases; + + if (state->search_bases == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "No HBAC search base found.\n"); ret = EINVAL; - goto fail; + goto immediately; } - ret = hbac_retry(hbac_ctx); - if (ret != EOK) { - goto fail; - } - - return; - -fail: - if (hbac_ctx) { - /* Return an proper error */ - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); - } else { - be_req_terminate(be_req, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL); + state->sdap_op = sdap_id_op_create(state, state->sdap_ctx->conn->conn_cache); + if (state->sdap_op == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create() failed\n"); + ret = ENOMEM; + goto immediately; } -} - -static int hbac_retry(struct hbac_ctx *hbac_ctx) -{ - struct tevent_req *subreq; - int ret; - bool offline; - time_t now, refresh_interval; - struct ipa_access_ctx *access_ctx = hbac_ctx->access_ctx; - struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req); offline = be_is_offline(be_ctx); - DEBUG(SSSDBG_TRACE_ALL, - "Connection status is [%s].\n", offline ? "offline" : "online"); - - refresh_interval = dp_opt_get_int(hbac_ctx->ipa_options, - IPA_HBAC_REFRESH); + DEBUG(SSSDBG_TRACE_ALL, "Connection status is [%s].\n", + offline ? "offline" : "online"); + refresh_interval = dp_opt_get_int(state->ipa_options, IPA_HBAC_REFRESH); now = time(NULL); - if (now < access_ctx->last_update + refresh_interval) { - /* Simulate offline mode and just go to the cache */ + + if (offline || now < access_ctx->last_update + refresh_interval) { DEBUG(SSSDBG_TRACE_FUNC, "Performing cached HBAC evaluation\n"); - offline = true; + ret = EOK; + goto immediately; } - if (!offline) { - if (hbac_ctx->sdap_op == NULL) { - hbac_ctx->sdap_op = sdap_id_op_create(hbac_ctx, - hbac_ctx->sdap_ctx->conn->conn_cache); - if (hbac_ctx->sdap_op == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_create failed.\n"); - return EIO; - } - } + ret = ipa_fetch_hbac_retry(req); + if (ret != EAGAIN) { + goto immediately; + } - subreq = sdap_id_op_connect_send(hbac_ctx->sdap_op, hbac_ctx, &ret); - if (!subreq) { - DEBUG(SSSDBG_CRIT_FAILURE, - "sdap_id_op_connect_send failed: %d(%s).\n", ret, strerror(ret)); - talloc_zfree(hbac_ctx->sdap_op); - return ret; - } + return req; - tevent_req_set_callback(subreq, hbac_connect_done, hbac_ctx); +immediately: + if (ret == EOK) { + tevent_req_done(req); } else { - /* Evaluate the rules based on what we have in the - * sysdb - */ - ipa_hbac_evaluate_rules(hbac_ctx); - return EOK; + tevent_req_error(req, ret); } - return EOK; + tevent_req_post(req, ev); + + return req; } -static void hbac_connect_done(struct tevent_req *subreq) +static errno_t ipa_fetch_hbac_retry(struct tevent_req *req) { - struct hbac_ctx *hbac_ctx = tevent_req_callback_data(subreq, struct hbac_ctx); - int ret, dp_error; - - ret = sdap_id_op_connect_recv(subreq, &dp_error); - talloc_zfree(subreq); - - if (dp_error == DP_ERR_OFFLINE) { - /* switching to offline mode */ - talloc_zfree(hbac_ctx->sdap_op); + struct ipa_fetch_hbac_state *state; + struct tevent_req *subreq; + int ret; - ipa_hbac_evaluate_rules(hbac_ctx); - return; - } else if (ret != EOK) { - goto fail; - } + state = tevent_req_data(req, struct ipa_fetch_hbac_state); - ret = hbac_get_host_info_step(hbac_ctx); - if (ret != EOK) { - goto fail; + subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_connect_send() failed: " + "%d(%s)\n", ret, strerror(ret)); + return ret; } - return; + tevent_req_set_callback(subreq, ipa_fetch_hbac_connect_done, req); -fail: - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); + return EAGAIN; } -static void hbac_clear_rule_data(struct hbac_ctx *hbac_ctx) +static void ipa_fetch_hbac_connect_done(struct tevent_req *subreq) { - hbac_ctx->host_count = 0; - talloc_zfree(hbac_ctx->hosts); - - hbac_ctx->hostgroup_count = 0; - talloc_zfree(hbac_ctx->hostgroups); - - hbac_ctx->service_count = 0; - talloc_zfree(hbac_ctx->services); - - hbac_ctx->servicegroup_count = 0; - talloc_zfree(hbac_ctx->servicegroups); - - hbac_ctx->rule_count = 0; - talloc_zfree(hbac_ctx->rules); -} + struct tevent_req *req = NULL; + int dp_error; + errno_t ret; -/* Check whether the current HBAC request is processed in off-line mode */ -static inline bool hbac_ctx_is_offline(struct hbac_ctx *ctx) -{ - return ctx == NULL || ctx->sdap_op == NULL; -} + req = tevent_req_callback_data(subreq, struct tevent_req); -/* Check the step result code and continue, retry, get offline result or abort accordingly */ -static bool hbac_check_step_result(struct hbac_ctx *hbac_ctx, int ret) -{ - int dp_error; + ret = sdap_id_op_connect_recv(subreq, &dp_error); + talloc_zfree(subreq); + if (ret != EOK) { + goto done; + } - if (ret == EOK) { - return true; + if (dp_error == DP_ERR_OFFLINE) { + ret = EOK; + goto done; } - if (hbac_ctx_is_offline(hbac_ctx)) { - /* already offline => the error is fatal */ - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); - return false; + ret = ipa_fetch_hbac_hostinfo(req); + if (ret == EAGAIN) { + return; } - ret = sdap_id_op_done(hbac_ctx->sdap_op, ret, &dp_error); +done: if (ret != EOK) { - if (dp_error == DP_ERR_OFFLINE) { - /* switching to offline mode */ - talloc_zfree(hbac_ctx->sdap_op); - - /* Free any of the results we've gotten */ - hbac_clear_rule_data(hbac_ctx); - - dp_error = DP_ERR_OK; - } - - if (dp_error == DP_ERR_OK) { - /* retry */ - ret = hbac_retry(hbac_ctx); - if (ret == EOK) { - return false; - } - } + tevent_req_error(req, ret); + return; } - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); - return false; + tevent_req_done(req); } -static void hbac_get_service_info_step(struct tevent_req *req); -static void hbac_get_rule_info_step(struct tevent_req *req); -static void hbac_sysdb_save (struct tevent_req *req); - -static int hbac_get_host_info_step(struct hbac_ctx *hbac_ctx) +static errno_t ipa_fetch_hbac_hostinfo(struct tevent_req *req) { - struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req); + struct ipa_fetch_hbac_state *state; + struct tevent_req *subreq; const char *hostname; - struct tevent_req *req; + bool srchost; + + state = tevent_req_data(req, struct ipa_fetch_hbac_state); - if (dp_opt_get_bool(hbac_ctx->ipa_options, IPA_HBAC_SUPPORT_SRCHOST)) { + srchost = dp_opt_get_bool(state->ipa_options, IPA_HBAC_SUPPORT_SRCHOST); + if (srchost) { /* Support srchost * -> we don't want any particular host, * we want all hosts @@ -392,284 +270,345 @@ static int hbac_get_host_info_step(struct hbac_ctx *hbac_ctx) sss_log(SSS_LOG_NOTICE, "WARNING: Using deprecated option " "ipa_hbac_support_srchost.\n"); } else { - hostname = dp_opt_get_string(hbac_ctx->ipa_options, IPA_HOSTNAME); + hostname = dp_opt_get_string(state->ipa_options, IPA_HOSTNAME); } - req = ipa_host_info_send(hbac_ctx, be_ctx->ev, - sdap_id_op_handle(hbac_ctx->sdap_op), - hbac_ctx->sdap_ctx->opts, - hostname, - hbac_ctx->access_ctx->host_map, - hbac_ctx->access_ctx->hostgroup_map, - hbac_ctx->access_ctx->host_search_bases); - if (req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Could not get host info\n"); + subreq = ipa_host_info_send(state, state->ev, + sdap_id_op_handle(state->sdap_op), + state->sdap_ctx->opts, hostname, + state->access_ctx->host_map, + state->access_ctx->hostgroup_map, + state->access_ctx->host_search_bases); + if (subreq == NULL) { return ENOMEM; } - tevent_req_set_callback(req, hbac_get_service_info_step, hbac_ctx); - return EOK; + tevent_req_set_callback(subreq, ipa_fetch_hbac_hostinfo_done, req); + + return EAGAIN; } -static void hbac_get_service_info_step(struct tevent_req *req) +static void ipa_fetch_hbac_hostinfo_done(struct tevent_req *subreq) { + struct ipa_fetch_hbac_state *state = NULL; + struct tevent_req *req = NULL; errno_t ret; - struct hbac_ctx *hbac_ctx = - tevent_req_callback_data(req, struct hbac_ctx); - struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req); - - ret = ipa_host_info_recv(req, hbac_ctx, - &hbac_ctx->host_count, - &hbac_ctx->hosts, - &hbac_ctx->hostgroup_count, - &hbac_ctx->hostgroups); - talloc_zfree(req); - if (!hbac_check_step_result(hbac_ctx, ret)) { - return; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_fetch_hbac_state); + + ret = ipa_host_info_recv(subreq, state, + &state->host_count, &state->hosts, + &state->hostgroup_count, &state->hostgroups); + talloc_zfree(subreq); + if (ret != EOK) { + goto done; } - /* Get services and service groups */ - req = ipa_hbac_service_info_send(hbac_ctx, be_ctx->ev, - sdap_id_op_handle(hbac_ctx->sdap_op), - hbac_ctx->sdap_ctx->opts, - hbac_ctx->search_bases); - if (req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE,"Could not get service info\n"); - goto fail; + subreq = ipa_hbac_service_info_send(state, state->ev, + sdap_id_op_handle(state->sdap_op), + state->sdap_ctx->opts, + state->search_bases); + if (subreq == NULL) { + ret = ENOMEM; + goto done; } - tevent_req_set_callback(req, hbac_get_rule_info_step, hbac_ctx); + + tevent_req_set_callback(subreq, ipa_fetch_hbac_services_done, req); + return; -fail: - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); } -static void hbac_get_rule_info_step(struct tevent_req *req) +static void ipa_fetch_hbac_services_done(struct tevent_req *subreq) { - errno_t ret; - size_t i; + struct ipa_fetch_hbac_state *state; + struct tevent_req *req; const char *ipa_hostname; const char *hostname; - struct hbac_ctx *hbac_ctx = - tevent_req_callback_data(req, struct hbac_ctx); - struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req); - - ret = ipa_hbac_service_info_recv(req, hbac_ctx, - &hbac_ctx->service_count, - &hbac_ctx->services, - &hbac_ctx->servicegroup_count, - &hbac_ctx->servicegroups); - talloc_zfree(req); - if (!hbac_check_step_result(hbac_ctx, ret)) { - return; + errno_t ret; + size_t i; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_fetch_hbac_state); + + ret = ipa_hbac_service_info_recv(subreq, state, + &state->service_count, &state->services, + &state->servicegroup_count, &state->servicegroups); + talloc_zfree(subreq); + if (ret != EOK) { + goto done; } /* Get the ipa_host attrs */ - hbac_ctx->ipa_host = NULL; - ipa_hostname = dp_opt_get_cstring(hbac_ctx->ipa_options, IPA_HOSTNAME); + state->ipa_host = NULL; + ipa_hostname = dp_opt_get_cstring(state->ipa_options, IPA_HOSTNAME); if (ipa_hostname == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Missing ipa_hostname, this should never happen.\n"); - goto fail; + ret = EINVAL; + goto done; } - for (i = 0; i < hbac_ctx->host_count; i++) { - ret = sysdb_attrs_get_string(hbac_ctx->hosts[i], - SYSDB_FQDN, - &hostname); + for (i = 0; i < state->host_count; i++) { + ret = sysdb_attrs_get_string(state->hosts[i], SYSDB_FQDN, &hostname); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n"); - goto fail; + goto done; } if (strcasecmp(hostname, ipa_hostname) == 0) { - hbac_ctx->ipa_host = hbac_ctx->hosts[i]; + state->ipa_host = state->hosts[i]; break; } } - if (hbac_ctx->ipa_host == NULL) { + + if (state->ipa_host == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n"); - goto fail; + ret = EINVAL; + goto done; } - - /* Get the list of applicable rules */ - req = ipa_hbac_rule_info_send(hbac_ctx, - be_ctx->ev, - sdap_id_op_handle(hbac_ctx->sdap_op), - hbac_ctx->sdap_ctx->opts, - hbac_ctx->search_bases, - hbac_ctx->ipa_host); - if (req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Could not get rules\n"); - goto fail; + subreq = ipa_hbac_rule_info_send(state, state->ev, + sdap_id_op_handle(state->sdap_op), + state->sdap_ctx->opts, + state->search_bases, + state->ipa_host); + if (subreq == NULL) { + ret = ENOMEM; + goto done; } - tevent_req_set_callback(req, hbac_sysdb_save, hbac_ctx); + tevent_req_set_callback(subreq, ipa_fetch_hbac_rules_done, req); + return; -fail: - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); } -static void hbac_sysdb_save(struct tevent_req *req) +static void ipa_fetch_hbac_rules_done(struct tevent_req *subreq) { + struct ipa_fetch_hbac_state *state = NULL; + struct tevent_req *req = NULL; + int dp_error; errno_t ret; - bool in_transaction = false; - struct hbac_ctx *hbac_ctx = - tevent_req_callback_data(req, struct hbac_ctx); - struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req); - struct sss_domain_info *domain = be_ctx->domain; - struct ldb_dn *base_dn; - struct ipa_access_ctx *access_ctx = - talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data, - struct ipa_access_ctx); - TALLOC_CTX *tmp_ctx; + bool found; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_fetch_hbac_state); - ret = ipa_hbac_rule_info_recv(req, hbac_ctx, - &hbac_ctx->rule_count, - &hbac_ctx->rules); - talloc_zfree(req); + ret = ipa_hbac_rule_info_recv(subreq, state, + &state->rule_count, &state->rules); + talloc_zfree(subreq); if (ret == ENOENT) { - /* No rules were found that apply to this - * host. - */ + /* Set ret to EOK so we can safely call sdap_id_op_done. */ + found = false; + ret = EOK; + } else if (ret == EOK) { + found = true; + } else { + goto done; + } - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); - return; - } - /* Delete any rules in the sysdb so offline logins - * are also denied. - */ - base_dn = sysdb_custom_subtree_dn(tmp_ctx, domain, HBAC_RULES_SUBDIR); - if (base_dn == NULL) { - talloc_free(tmp_ctx); - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); - return; + ret = sdap_id_op_done(state->sdap_op, ret, &dp_error); + if (dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + ret = ipa_fetch_hbac_retry(req); + if (ret != EAGAIN) { + tevent_req_error(req, ret); } + return; + } else if (ret != EOK) { + tevent_req_error(req, ret); + return; + } - ret = sysdb_delete_recursive(domain->sysdb, base_dn, true); - talloc_free(tmp_ctx); + if (found == false) { + /* No rules were found that apply to this host. */ + ret = ipa_purge_hbac(state->be_ctx->domain); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_delete_recursive failed.\n"); - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); - return; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to remove HBAC rules\n"); + goto done; } - /* If no rules are found, we default to DENY */ - ipa_access_reply(hbac_ctx, PAM_PERM_DENIED); - return; + ret = ENOENT; + goto done; + } + + ret = ipa_save_hbac(state->be_ctx->domain, state); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to save HBAC rules\n"); + goto done; } - if (!hbac_check_step_result(hbac_ctx, ret)) { + ret = EOK; + +done: + if (ret != EOK) { + tevent_req_error(req, ret); return; } + tevent_req_done(req); +} + +static errno_t ipa_fetch_hbac_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +static errno_t ipa_purge_hbac(struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_dn *base_dn; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + /* Delete any rules in the sysdb so offline logins are also denied. */ + base_dn = sysdb_custom_subtree_dn(tmp_ctx, domain, HBAC_RULES_SUBDIR); + if (base_dn == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_delete_recursive(domain->sysdb, base_dn, true); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_delete_recursive failed.\n"); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t ipa_save_hbac(struct sss_domain_info *domain, + struct ipa_fetch_hbac_state *state) +{ + bool in_transaction = false; + errno_t ret; + errno_t sret; + ret = sysdb_transaction_start(domain->sysdb); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Could not start transaction\n"); - goto fail; + goto done; } in_transaction = true; /* Save the hosts */ - ret = ipa_hbac_sysdb_save(domain, - HBAC_HOSTS_SUBDIR, SYSDB_FQDN, - hbac_ctx->host_count, hbac_ctx->hosts, + ret = ipa_hbac_sysdb_save(domain, HBAC_HOSTS_SUBDIR, SYSDB_FQDN, + state->host_count, state->hosts, HBAC_HOSTGROUPS_SUBDIR, SYSDB_NAME, - hbac_ctx->hostgroup_count, - hbac_ctx->hostgroups); + state->hostgroup_count, state->hostgroups); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Error saving hosts: [%d][%s]\n", - ret, strerror(ret)); - goto fail; + DEBUG(SSSDBG_CRIT_FAILURE, "Error saving hosts [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; } /* Save the services */ - ret = ipa_hbac_sysdb_save(domain, - HBAC_SERVICES_SUBDIR, IPA_CN, - hbac_ctx->service_count, hbac_ctx->services, + ret = ipa_hbac_sysdb_save(domain, HBAC_SERVICES_SUBDIR, IPA_CN, + state->service_count, state->services, HBAC_SERVICEGROUPS_SUBDIR, IPA_CN, - hbac_ctx->servicegroup_count, - hbac_ctx->servicegroups); + state->servicegroup_count, + state->servicegroups); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Error saving services: [%d][%s]\n", - ret, strerror(ret)); - goto fail; + DEBUG(SSSDBG_CRIT_FAILURE, "Error saving services [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; } /* Save the rules */ - ret = ipa_hbac_sysdb_save(domain, - HBAC_RULES_SUBDIR, IPA_UNIQUE_ID, - hbac_ctx->rule_count, - hbac_ctx->rules, + ret = ipa_hbac_sysdb_save(domain, HBAC_RULES_SUBDIR, IPA_UNIQUE_ID, + state->rule_count, state->rules, NULL, NULL, 0, NULL); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Error saving rules: [%d][%s]\n", - ret, strerror(ret)); - goto fail; + DEBUG(SSSDBG_CRIT_FAILURE, "Error saving rules [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; } ret = sysdb_transaction_commit(domain->sysdb); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n"); - goto fail; + goto done; } in_transaction = false; - /* We don't need the rule data any longer, - * the rest of the processing relies on - * sysdb lookups. - */ - hbac_clear_rule_data(hbac_ctx); - - - access_ctx->last_update = time(NULL); + state->access_ctx->last_update = time(NULL); - /* Now evaluate the request against the rules */ - ipa_hbac_evaluate_rules(hbac_ctx); - - return; + ret = EOK; -fail: +done: if (in_transaction) { - ret = sysdb_transaction_cancel(domain->sysdb); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, "Could not cancel transaction\n"); + sret = sysdb_transaction_cancel(domain->sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n"); } } - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); + + return ret; } -void ipa_hbac_evaluate_rules(struct hbac_ctx *hbac_ctx) +errno_t ipa_hbac_evaluate_rules(struct be_ctx *be_ctx, + struct dp_option *ipa_options, + struct pam_data *pd) { - struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req); - errno_t ret; + TALLOC_CTX *tmp_ctx; + struct hbac_ctx hbac_ctx; struct hbac_rule **hbac_rules; struct hbac_eval_req *eval_req; enum hbac_eval_result result; - struct hbac_info *info; + struct hbac_info *info = NULL; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + hbac_ctx.be_ctx = be_ctx; + hbac_ctx.ipa_options = ipa_options; + hbac_ctx.pd = pd; /* Get HBAC rules from the sysdb */ - ret = hbac_get_cached_rules(hbac_ctx, be_ctx->domain, - &hbac_ctx->rule_count, &hbac_ctx->rules); + ret = hbac_get_cached_rules(tmp_ctx, be_ctx->domain, + &hbac_ctx.rule_count, &hbac_ctx.rules); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not retrieve rules from the cache\n"); - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); + goto done; } - ret = hbac_ctx_to_rules(hbac_ctx, hbac_ctx, - &hbac_rules, &eval_req); + ret = hbac_ctx_to_rules(tmp_ctx, &hbac_ctx, &hbac_rules, &eval_req); if (ret == EPERM) { DEBUG(SSSDBG_CRIT_FAILURE, "DENY rules detected. Denying access to all users\n"); - ipa_access_reply(hbac_ctx, PAM_PERM_DENIED); - return; + ret = ERR_ACCESS_DENIED; + goto done; } else if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not construct HBAC rules\n"); - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); - return; + goto done; } hbac_enable_debug(hbac_debug_messages); @@ -677,26 +616,27 @@ void ipa_hbac_evaluate_rules(struct hbac_ctx *hbac_ctx) result = hbac_evaluate(hbac_rules, eval_req, &info); if (result == HBAC_EVAL_ALLOW) { DEBUG(SSSDBG_MINOR_FAILURE, "Access granted by HBAC rule [%s]\n", - info->rule_name); - hbac_free_info(info); - ipa_access_reply(hbac_ctx, PAM_SUCCESS); - return; + info->rule_name); + ret = EOK; + goto done; } else if (result == HBAC_EVAL_ERROR) { DEBUG(SSSDBG_CRIT_FAILURE, "Error [%s] occurred in rule [%s]\n", - hbac_error_string(info->code), - info->rule_name); - hbac_free_info(info); - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); - return; + hbac_error_string(info->code), info->rule_name); + ret = EIO; + goto done; } else if (result == HBAC_EVAL_OOM) { DEBUG(SSSDBG_CRIT_FAILURE, "Insufficient memory\n"); - ipa_access_reply(hbac_ctx, PAM_SYSTEM_ERR); - return; + ret = ENOMEM; + goto done; } DEBUG(SSSDBG_MINOR_FAILURE, "Access denied by HBAC rules\n"); + ret = ERR_ACCESS_DENIED; + +done: hbac_free_info(info); - ipa_access_reply(hbac_ctx, PAM_PERM_DENIED); + talloc_free(tmp_ctx); + return ret; } errno_t hbac_get_cached_rules(TALLOC_CTX *mem_ctx, @@ -761,3 +701,154 @@ done: talloc_free(tmp_ctx); return ret; } + +struct ipa_pam_access_handler_state { + struct tevent_context *ev; + struct be_ctx *be_ctx; + struct ipa_access_ctx *access_ctx; + struct pam_data *pd; +}; + +static void ipa_pam_access_handler_sdap_done(struct tevent_req *subreq); +static void ipa_pam_access_handler_done(struct tevent_req *subreq); + +struct tevent_req * +ipa_pam_access_handler_send(TALLOC_CTX *mem_ctx, + struct ipa_access_ctx *access_ctx, + struct pam_data *pd, + struct dp_req_params *params) +{ + struct ipa_pam_access_handler_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + + req = tevent_req_create(mem_ctx, &state, + struct ipa_pam_access_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->pd = pd; + state->ev = params->ev; + state->be_ctx = params->be_ctx; + state->access_ctx = access_ctx; + + subreq = sdap_access_send(state, params->ev, params->be_ctx, + params->domain, access_ctx->sdap_access_ctx, + access_ctx->sdap_ctx->conn, pd); + if (subreq == NULL) { + state->pd->pam_status = PAM_SYSTEM_ERR; + goto immediately; + } + + tevent_req_set_callback(subreq, ipa_pam_access_handler_sdap_done, req); + + return req; + +immediately: + /* 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_pam_access_handler_sdap_done(struct tevent_req *subreq) +{ + struct ipa_pam_access_handler_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_pam_access_handler_state); + + ret = sdap_access_recv(subreq); + talloc_free(subreq); + switch (ret) { + case EOK: + /* Account wasn't locked. Continue below to HBAC processing. */ + break; + case ERR_ACCESS_DENIED: + /* Account was locked. Return permission denied here. */ + state->pd->pam_status = PAM_PERM_DENIED; + goto done; + case ERR_ACCOUNT_EXPIRED: + state->pd->pam_status = PAM_ACCT_EXPIRED; + goto done; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Error retrieving access check result " + "[%d]: %s.\n", ret, sss_strerror(ret)); + state->pd->pam_status = PAM_SYSTEM_ERR; + break; + } + + subreq = ipa_fetch_hbac_send(state, state->ev, state->be_ctx, + state->access_ctx); + if (subreq == NULL) { + state->pd->pam_status = PAM_SYSTEM_ERR; + goto done; + } + + tevent_req_set_callback(subreq, ipa_pam_access_handler_done, req); + + return; + +done: + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); +} + +static void ipa_pam_access_handler_done(struct tevent_req *subreq) +{ + struct ipa_pam_access_handler_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_pam_access_handler_state); + + ret = ipa_fetch_hbac_recv(subreq); + talloc_free(subreq); + + if (ret == ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, "No HBAC rules find, denying access\n"); + state->pd->pam_status = PAM_PERM_DENIED; + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to fetch HBAC rules [%d]: %s\n", + ret, sss_strerror(ret)); + state->pd->pam_status = PAM_SYSTEM_ERR; + goto done; + } + + ret = ipa_hbac_evaluate_rules(state->be_ctx, + state->access_ctx->ipa_options, state->pd); + if (ret == EOK) { + state->pd->pam_status = PAM_SUCCESS; + } else if (ret == ERR_ACCESS_DENIED) { + state->pd->pam_status = PAM_PERM_DENIED; + } else { + state->pd->pam_status = PAM_SYSTEM_ERR; + } + +done: + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); +} + +errno_t +ipa_pam_access_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data) +{ + struct ipa_pam_access_handler_state *state = NULL; + + state = tevent_req_data(req, struct ipa_pam_access_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_data = talloc_steal(mem_ctx, state->pd); + + return EOK; +} diff --git a/src/providers/ipa/ipa_access.h b/src/providers/ipa/ipa_access.h index 024b57e7e..1e30a89a0 100644 --- a/src/providers/ipa/ipa_access.h +++ b/src/providers/ipa/ipa_access.h @@ -46,35 +46,23 @@ struct ipa_access_ctx { }; struct hbac_ctx { - struct sdap_id_ctx *sdap_ctx; - struct ipa_access_ctx *access_ctx; - struct sdap_id_op *sdap_op; + struct be_ctx *be_ctx; struct dp_option *ipa_options; - struct time_rules_ctx *tr_ctx; - struct be_req *be_req; struct pam_data *pd; - - struct sdap_search_base **search_bases; - - /* Hosts */ - size_t host_count; - struct sysdb_attrs **hosts; - size_t hostgroup_count; - struct sysdb_attrs **hostgroups; - struct sysdb_attrs *ipa_host; - - /* Rules */ size_t rule_count; struct sysdb_attrs **rules; - - /* Services */ - size_t service_count; - struct sysdb_attrs **services; - size_t servicegroup_count; - struct sysdb_attrs **servicegroups; }; -void ipa_access_handler(struct be_req *be_req); +struct tevent_req * +ipa_pam_access_handler_send(TALLOC_CTX *mem_ctx, + struct ipa_access_ctx *access_ctx, + struct pam_data *pd, + struct dp_req_params *params); + +errno_t +ipa_pam_access_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data); errno_t hbac_get_cached_rules(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain, diff --git a/src/providers/ipa/ipa_auth.c b/src/providers/ipa/ipa_auth.c index b1bfa3ffe..ad0a89bfe 100644 --- a/src/providers/ipa/ipa_auth.c +++ b/src/providers/ipa/ipa_auth.c @@ -168,92 +168,79 @@ static int get_password_migration_flag_recv(struct tevent_req *req, return EOK; } - -struct ipa_auth_state { - struct be_req *be_req; +struct ipa_pam_auth_handler_state { struct tevent_context *ev; - struct ipa_auth_ctx *ipa_auth_ctx; + struct ipa_auth_ctx *auth_ctx; + struct be_ctx *be_ctx; struct pam_data *pd; - bool password_migration; - struct sdap_handle *sh; }; -static void ipa_auth_handler_done(struct tevent_req *req); -static void ipa_get_migration_flag_done(struct tevent_req *req); -static void ipa_migration_flag_connect_done(struct tevent_req *req); -static void ipa_auth_ldap_done(struct tevent_req *req); -static void ipa_auth_handler_retry_done(struct tevent_req *req); - -void ipa_auth(struct be_req *be_req) +static void ipa_pam_auth_handler_krb5_done(struct tevent_req *subreq); +static void ipa_pam_auth_handler_flag_done(struct tevent_req *subreq); +static void ipa_pam_auth_handler_connect_done(struct tevent_req *subreq); +static void ipa_pam_auth_handler_auth_done(struct tevent_req *subreq); +static void ipa_pam_auth_handler_retry_done(struct tevent_req *subreq); + +struct tevent_req * +ipa_pam_auth_handler_send(TALLOC_CTX *mem_ctx, + struct ipa_auth_ctx *auth_ctx, + struct pam_data *pd, + struct dp_req_params *params) { + struct ipa_pam_auth_handler_state *state; + struct tevent_req *subreq; struct tevent_req *req; - struct ipa_auth_state *state; - struct pam_data *pd = - talloc_get_type(be_req_get_data(be_req), struct pam_data); - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - - state = talloc_zero(be_req, struct ipa_auth_state); - if (state == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n"); - goto fail; - } - - state->password_migration = false; - state->sh = NULL; - state->be_req = be_req; - state->ev = be_ctx->ev; + req = tevent_req_create(mem_ctx, &state, + struct ipa_pam_auth_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } state->pd = pd; + state->ev = params->ev; + state->auth_ctx = auth_ctx; + state->be_ctx = params->be_ctx; - switch (state->pd->cmd) { - case SSS_PAM_AUTHENTICATE: - case SSS_PAM_PREAUTH: - state->ipa_auth_ctx = talloc_get_type( - be_ctx->bet_info[BET_AUTH].pvt_bet_data, - struct ipa_auth_ctx); - break; - case SSS_PAM_CHAUTHTOK: - case SSS_PAM_CHAUTHTOK_PRELIM: - state->ipa_auth_ctx = talloc_get_type( - be_ctx->bet_info[BET_CHPASS].pvt_bet_data, - struct ipa_auth_ctx); - break; - default: - DEBUG(SSSDBG_OP_FAILURE, "Unsupported PAM task.\n"); - goto fail; - } + pd->pam_status = PAM_SYSTEM_ERR; - req = krb5_auth_queue_send(state, state->ev, be_ctx, state->pd, - state->ipa_auth_ctx->krb5_auth_ctx); - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "krb5_auth_send failed.\n"); - goto fail; + subreq = krb5_auth_queue_send(state, params->ev, params->be_ctx, + pd, auth_ctx->krb5_auth_ctx); + if (subreq == NULL) { + pd->pam_status = PAM_SYSTEM_ERR; + goto immediately; } - tevent_req_set_callback(req, ipa_auth_handler_done, state); - return; + tevent_req_set_callback(subreq, ipa_pam_auth_handler_krb5_done, req); -fail: - talloc_free(state); - pd->pam_status = PAM_SYSTEM_ERR; - be_req_terminate(be_req, DP_ERR_FATAL, pd->pam_status, NULL); + return req; + +immediately: + /* 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_auth_handler_done(struct tevent_req *req) +static void ipa_pam_auth_handler_krb5_done(struct tevent_req *subreq) { - struct ipa_auth_state *state = tevent_req_callback_data(req, - struct ipa_auth_state); - int ret; - int pam_status = PAM_SYSTEM_ERR; + struct ipa_pam_auth_handler_state *state; + struct tevent_req *req; int dp_err; + char *realm; + errno_t ret; - ret = krb5_auth_queue_recv(req, &pam_status, &dp_err); - talloc_zfree(req); - state->pd->pam_status = pam_status; - if (ret != EOK && pam_status != PAM_CRED_ERR) { - DEBUG(SSSDBG_OP_FAILURE, "krb5_auth_recv request failed.\n"); - dp_err = DP_ERR_OK; + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_pam_auth_handler_state); + + state->pd->pam_status = PAM_SYSTEM_ERR; + ret = krb5_auth_queue_recv(subreq, &state->pd->pam_status, &dp_err); + talloc_free(subreq); + if (ret != EOK && state->pd->pam_status != PAM_CRED_ERR) { + DEBUG(SSSDBG_OP_FAILURE, "KRB5 auth failed [%d]: %s\n", + ret, sss_strerror(ret)); goto done; } @@ -261,201 +248,203 @@ static void ipa_auth_handler_done(struct tevent_req *req) goto done; } - if (state->pd->cmd == SSS_PAM_AUTHENTICATE && - state->pd->pam_status == PAM_CRED_ERR) { - - req = get_password_migration_flag_send(state, state->ev, - state->ipa_auth_ctx->sdap_id_ctx, - dp_opt_get_string( - state->ipa_auth_ctx->ipa_options, - IPA_KRB5_REALM)); - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, - "get_password_migration_flag failed.\n"); + if (state->pd->cmd == SSS_PAM_AUTHENTICATE + && state->pd->pam_status == PAM_CRED_ERR) { + realm = dp_opt_get_string(state->auth_ctx->ipa_options, IPA_KRB5_REALM); + subreq = get_password_migration_flag_send(state, state->ev, + state->auth_ctx->sdap_id_ctx, + realm); + if (subreq == NULL) { goto done; } - tevent_req_set_callback(req, ipa_get_migration_flag_done, state); + tevent_req_set_callback(subreq, ipa_pam_auth_handler_flag_done, req); return; } done: - be_req_terminate(state->be_req, dp_err, state->pd->pam_status, NULL); + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); } -static void ipa_get_migration_flag_done(struct tevent_req *req) +static void ipa_pam_auth_handler_flag_done(struct tevent_req *subreq) { - struct ipa_auth_state *state = tevent_req_callback_data(req, - struct ipa_auth_state); - int ret; - int dp_err = DP_ERR_FATAL; + struct ipa_pam_auth_handler_state *state; + struct sdap_auth_ctx *sdap_auth_ctx; + bool password_migration = false; + struct tevent_req *req; + errno_t ret; - ret = get_password_migration_flag_recv(req, &state->password_migration); - talloc_zfree(req); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_pam_auth_handler_state); + + ret = get_password_migration_flag_recv(subreq, &password_migration); + talloc_free(subreq); if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "get_password_migration_flag " - "request failed.\n"); + DEBUG(SSSDBG_OP_FAILURE, "Unable to get password migration flag " + "[%d]: %s\n", ret, sss_strerror(ret)); state->pd->pam_status = PAM_SYSTEM_ERR; - dp_err = DP_ERR_OK; goto done; } - if (state->password_migration) { - req = sdap_cli_connect_send(state, state->ev, - state->ipa_auth_ctx->sdap_auth_ctx->opts, - state->ipa_auth_ctx->sdap_auth_ctx->be, - state->ipa_auth_ctx->sdap_auth_ctx->service, - true, CON_TLS_ON, true); - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_cli_connect_send failed.\n"); + if (password_migration) { + sdap_auth_ctx = state->auth_ctx->sdap_auth_ctx; + subreq = sdap_cli_connect_send(state, state->ev, + sdap_auth_ctx->opts, + sdap_auth_ctx->be, + sdap_auth_ctx->service, + true, CON_TLS_ON, true); + if (subreq == NULL) { + state->pd->pam_status = PAM_SYSTEM_ERR; goto done; } - tevent_req_set_callback(req, ipa_migration_flag_connect_done, state); + tevent_req_set_callback(subreq, ipa_pam_auth_handler_connect_done, req); return; } - DEBUG(SSSDBG_CONF_SETTINGS, "Password migration is not enabled.\n"); - dp_err = DP_ERR_OK; done: - be_req_terminate(state->be_req, dp_err, state->pd->pam_status, NULL); + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); } -static void ipa_migration_flag_connect_done(struct tevent_req *req) +static void ipa_pam_auth_handler_connect_done(struct tevent_req *subreq) { - struct ipa_auth_state *state = tevent_req_callback_data(req, - struct ipa_auth_state); - struct be_ctx *be_ctx = be_req_get_be_ctx(state->be_req); - const char **attrs; - struct ldb_message *user_msg; + struct ipa_pam_auth_handler_state *state; + struct tevent_req *req; + struct sdap_handle *sh; + const char *attrs[] = {SYSDB_ORIG_DN, NULL}; + struct ldb_message *msg; const char *dn; - int dp_err = DP_ERR_FATAL; - int ret; - int auth_timeout; + int timeout; + errno_t ret; - ret = sdap_cli_connect_recv(req, state, NULL, &state->sh, NULL); - talloc_zfree(req); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - "Cannot connect to LDAP server to perform migration\n"); - goto done; - } + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_pam_auth_handler_state); state->pd->pam_status = PAM_SYSTEM_ERR; - DEBUG(SSSDBG_TRACE_FUNC, "Assuming Kerberos password is missing, " - "starting password migration.\n"); - attrs = talloc_array(state, const char *, 2); - if (attrs == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_array failed.\n"); - state->pd->pam_status = PAM_SYSTEM_ERR; - dp_err = DP_ERR_OK; + ret = sdap_cli_connect_recv(subreq, state, NULL, &sh, NULL); + talloc_free(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot connect to LDAP server to perform " + "migration [%d]: %s\n", ret, sss_strerror(ret)); goto done; } - attrs[0] = SYSDB_ORIG_DN; - attrs[1] = NULL; - ret = sysdb_search_user_by_name(state, be_ctx->domain, state->pd->user, - attrs, &user_msg); + DEBUG(SSSDBG_TRACE_FUNC, "Assuming Kerberos password is missing, " + "starting password migration.\n"); + + ret = sysdb_search_user_by_name(state, state->be_ctx->domain, + state->pd->user, attrs, &msg); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_name failed.\n"); goto done; } - dn = ldb_msg_find_attr_as_string(user_msg, SYSDB_ORIG_DN, NULL); + dn = ldb_msg_find_attr_as_string(msg, SYSDB_ORIG_DN, NULL); if (dn == NULL) { DEBUG(SSSDBG_MINOR_FAILURE, "Missing original DN for user [%s].\n", - state->pd->user); - state->pd->pam_status = PAM_SYSTEM_ERR; - dp_err = DP_ERR_OK; + state->pd->user); goto done; } - auth_timeout = dp_opt_get_int( - state->ipa_auth_ctx->sdap_auth_ctx->opts->basic, - SDAP_OPT_TIMEOUT); + timeout = dp_opt_get_int(state->auth_ctx->sdap_auth_ctx->opts->basic, + SDAP_OPT_TIMEOUT); - req = sdap_auth_send(state, state->ev, state->sh, NULL, NULL, dn, - state->pd->authtok, auth_timeout); - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_auth_send failed.\n"); + subreq = sdap_auth_send(state, state->ev, sh, NULL, NULL, dn, + state->pd->authtok, timeout); + if (subreq == NULL) { goto done; } - tevent_req_set_callback(req, ipa_auth_ldap_done, state); + tevent_req_set_callback(subreq, ipa_pam_auth_handler_auth_done, req); return; done: - be_req_terminate(state->be_req, dp_err, state->pd->pam_status, NULL); + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); } -static void ipa_auth_ldap_done(struct tevent_req *req) +static void ipa_pam_auth_handler_auth_done(struct tevent_req *subreq) { - struct ipa_auth_state *state = tevent_req_callback_data(req, - struct ipa_auth_state); - struct be_ctx *be_ctx = be_req_get_be_ctx(state->be_req); - int ret; - int dp_err = DP_ERR_FATAL; + struct ipa_pam_auth_handler_state *state; + struct tevent_req *req; + errno_t ret; - ret = sdap_auth_recv(req, state, NULL); - talloc_zfree(req); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_pam_auth_handler_state); + + ret = sdap_auth_recv(subreq, state, NULL); + + talloc_free(subreq); switch (ret) { case EOK: break; - case ERR_AUTH_DENIED: case ERR_AUTH_FAILED: case ERR_PASSWORD_EXPIRED: -/* TODO: do we need to handle expired passwords? */ + /* TODO: do we need to handle expired passwords? */ DEBUG(SSSDBG_MINOR_FAILURE, "LDAP authentication failed, " - "Password migration not possible.\n"); + "password migration not possible.\n"); state->pd->pam_status = PAM_CRED_INSUFFICIENT; - dp_err = DP_ERR_OK; goto done; default: DEBUG(SSSDBG_OP_FAILURE, "auth_send request failed.\n"); state->pd->pam_status = PAM_SYSTEM_ERR; - dp_err = DP_ERR_OK; goto done; } - DEBUG(SSSDBG_TRACE_FUNC, "LDAP authentication succeded, " - "trying Kerberos authentication again.\n"); + "trying Kerberos authentication again.\n"); - req = krb5_auth_queue_send(state, state->ev, be_ctx, state->pd, - state->ipa_auth_ctx->krb5_auth_ctx); - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "krb5_auth_send failed.\n"); + subreq = krb5_auth_queue_send(state, state->ev, state->be_ctx, state->pd, + state->auth_ctx->krb5_auth_ctx); + if (subreq == NULL) { goto done; } - tevent_req_set_callback(req, ipa_auth_handler_retry_done, state); + tevent_req_set_callback(subreq, ipa_pam_auth_handler_retry_done, req); return; done: - be_req_terminate(state->be_req, dp_err, state->pd->pam_status, NULL); + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); } -static void ipa_auth_handler_retry_done(struct tevent_req *req) +static void ipa_pam_auth_handler_retry_done(struct tevent_req *subreq) { - struct ipa_auth_state *state = tevent_req_callback_data(req, - struct ipa_auth_state); - int ret; - int pam_status; + struct ipa_pam_auth_handler_state *state; + struct tevent_req *req; int dp_err; + errno_t ret; - ret = krb5_auth_queue_recv(req, &pam_status, &dp_err); - talloc_zfree(req); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_pam_auth_handler_state); + + ret = krb5_auth_queue_recv(subreq, &state->pd->pam_status, &dp_err); + talloc_free(subreq); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "krb5_auth_recv request failed.\n"); state->pd->pam_status = PAM_SYSTEM_ERR; - dp_err = DP_ERR_OK; - goto done; } - state->pd->pam_status = pam_status; + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); +} -done: - be_req_terminate(state->be_req, dp_err, state->pd->pam_status, NULL); +errno_t +ipa_pam_auth_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data) +{ + struct ipa_pam_auth_handler_state *state = NULL; + + state = tevent_req_data(req, struct ipa_pam_auth_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_data = talloc_steal(mem_ctx, state->pd); + + return EOK; } diff --git a/src/providers/ipa/ipa_auth.h b/src/providers/ipa/ipa_auth.h index 5cd318bae..53666eb44 100644 --- a/src/providers/ipa/ipa_auth.h +++ b/src/providers/ipa/ipa_auth.h @@ -26,7 +26,17 @@ #define _IPA_AUTH_H_ #include "providers/backend.h" - -void ipa_auth(struct be_req *be_req); +#include "providers/ipa/ipa_common.h" + +struct tevent_req * +ipa_pam_auth_handler_send(TALLOC_CTX *mem_ctx, + struct ipa_auth_ctx *auth_ctx, + struct pam_data *pd, + struct dp_req_params *params); + +errno_t +ipa_pam_auth_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data); #endif /* _IPA_AUTH_H_ */ diff --git a/src/providers/ipa/ipa_autofs.c b/src/providers/ipa/ipa_autofs.c index 504735d3e..b2e4cbc06 100644 --- a/src/providers/ipa/ipa_autofs.c +++ b/src/providers/ipa/ipa_autofs.c @@ -31,23 +31,14 @@ #include "providers/ipa/ipa_dyndns.h" #include "providers/ipa/ipa_selinux.h" -struct bet_ops ipa_autofs_ops = { - .handler = sdap_autofs_handler, - .finalize = NULL, - .check_online = sdap_check_online -}; - -int ipa_autofs_init(struct be_ctx *be_ctx, - struct ipa_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data) +errno_t ipa_autofs_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_id_ctx *id_ctx, + struct dp_method *dp_methods) { int ret; - DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing autofs LDAP back end\n"); - - *ops = &ipa_autofs_ops; - *pvt_data = id_ctx->sdap_id_ctx; + DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing autofs IPA back end\n"); ret = ipa_get_autofs_options(id_ctx->ipa_options, be_ctx->cdb, be_ctx->conf_path, &id_ctx->sdap_id_ctx->opts); @@ -56,5 +47,9 @@ int ipa_autofs_init(struct be_ctx *be_ctx, return ret; } + dp_set_method(dp_methods, DPM_AUTOFS_HANDLER, + sdap_autofs_handler_send, sdap_autofs_handler_recv, id_ctx->sdap_id_ctx, + struct sdap_id_ctx, struct dp_autofs_data, struct dp_reply_std); + return ret; } diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h index 51de819c8..939c898e1 100644 --- a/src/providers/ipa/ipa_common.h +++ b/src/providers/ipa/ipa_common.h @@ -258,10 +258,10 @@ int ipa_get_autofs_options(struct ipa_options *ipa_opts, errno_t ipa_get_dyndns_options(struct be_ctx *be_ctx, struct ipa_options *ctx); -int ipa_autofs_init(struct be_ctx *be_ctx, - struct ipa_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data); +errno_t ipa_autofs_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_id_ctx *id_ctx, + struct dp_method *dp_methods); int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, const char *primary_servers, @@ -269,10 +269,10 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, struct ipa_options *options, struct ipa_service **_service); -int ipa_sudo_init(struct be_ctx *be_ctx, +int ipa_sudo_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, struct ipa_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data); + struct dp_method *dp_methods); errno_t get_idmap_data_from_range(struct range_info *r, char *domain_name, char **_name, char **_sid, uint32_t *_rid, diff --git a/src/providers/ipa/ipa_hbac_common.c b/src/providers/ipa/ipa_hbac_common.c index 82c531f15..7edaf576e 100644 --- a/src/providers/ipa/ipa_hbac_common.c +++ b/src/providers/ipa/ipa_hbac_common.c @@ -261,7 +261,6 @@ hbac_attrs_to_rule(TALLOC_CTX *mem_ctx, size_t idx, struct hbac_rule **rule) { - struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req); errno_t ret; struct hbac_rule *new_rule; struct ldb_message_element *el; @@ -305,7 +304,7 @@ hbac_attrs_to_rule(TALLOC_CTX *mem_ctx, } /* Get the users */ - ret = hbac_user_attrs_to_rule(new_rule, be_ctx->domain, + ret = hbac_user_attrs_to_rule(new_rule, hbac_ctx->be_ctx->domain, new_rule->name, hbac_ctx->rules[idx], &new_rule->users); @@ -316,7 +315,7 @@ hbac_attrs_to_rule(TALLOC_CTX *mem_ctx, } /* Get the services */ - ret = hbac_service_attrs_to_rule(new_rule, be_ctx->domain, + ret = hbac_service_attrs_to_rule(new_rule, hbac_ctx->be_ctx->domain, new_rule->name, hbac_ctx->rules[idx], &new_rule->services); @@ -327,7 +326,7 @@ hbac_attrs_to_rule(TALLOC_CTX *mem_ctx, } /* Get the target hosts */ - ret = hbac_thost_attrs_to_rule(new_rule, be_ctx->domain, + ret = hbac_thost_attrs_to_rule(new_rule, hbac_ctx->be_ctx->domain, new_rule->name, hbac_ctx->rules[idx], &new_rule->targethosts); @@ -340,7 +339,7 @@ hbac_attrs_to_rule(TALLOC_CTX *mem_ctx, /* Get the source hosts */ - ret = hbac_shost_attrs_to_rule(new_rule, be_ctx->domain, + ret = hbac_shost_attrs_to_rule(new_rule, hbac_ctx->be_ctx->domain, new_rule->name, hbac_ctx->rules[idx], dp_opt_get_bool(hbac_ctx->ipa_options, @@ -425,8 +424,7 @@ hbac_ctx_to_eval_request(TALLOC_CTX *mem_ctx, struct pam_data *pd = hbac_ctx->pd; TALLOC_CTX *tmp_ctx; struct hbac_eval_req *eval_req; - struct be_ctx *be_ctx = be_req_get_be_ctx(hbac_ctx->be_req); - struct sss_domain_info *domain = be_ctx->domain; + struct sss_domain_info *domain = hbac_ctx->be_ctx->domain; const char *rhost; const char *thost; struct sss_domain_info *user_dom; diff --git a/src/providers/ipa/ipa_hostid.c b/src/providers/ipa/ipa_hostid.c index 31fa29f91..87a36167b 100644 --- a/src/providers/ipa/ipa_hostid.c +++ b/src/providers/ipa/ipa_hostid.c @@ -39,104 +39,6 @@ struct hosts_get_state { int dp_error; }; -struct tevent_req * -hosts_get_send(TALLOC_CTX *memctx, - struct tevent_context *ev, - struct ipa_hostid_ctx *hostid_ctx, - const char *name, - const char *alias); -static errno_t -hosts_get_recv(struct tevent_req *req, - int *dp_error_out); - -static void -ipa_host_info_hosts_done(struct tevent_req *req); - -void -ipa_host_info_handler(struct be_req *breq) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(breq); - struct ipa_hostid_ctx *hostid_ctx; - struct sdap_id_ctx *ctx; - struct be_host_req *hr; - struct tevent_req *req; - int dp_error = DP_ERR_FATAL; - errno_t ret = EOK; - const char *err = "Unknown Error"; - - hostid_ctx = talloc_get_type(be_ctx->bet_info[BET_HOSTID].pvt_bet_data, - struct ipa_hostid_ctx); - ctx = hostid_ctx->sdap_id_ctx; - - if (be_is_offline(ctx->be)) { - dp_error = DP_ERR_OFFLINE; - ret = EAGAIN; - err = "Offline"; - goto done; - } - - hr = talloc_get_type(be_req_get_data(breq), struct be_host_req); - - if (hr->filter_type != BE_FILTER_NAME) { - ret = EINVAL; - err = "Invalid filter type"; - goto done; - } - - req = hosts_get_send(breq, be_ctx->ev, hostid_ctx, - hr->name, hr->alias); - if (!req) { - ret = ENOMEM; - err = "Out of memory"; - goto done; - } - - tevent_req_set_callback(req, ipa_host_info_hosts_done, breq); - - ret = EOK; - -done: - if (ret != EOK) return sdap_handler_done(breq, dp_error, ret, err); -} - -static void -ipa_host_info_complete(struct be_req *breq, int dp_error, - errno_t ret, const char *default_error_text) -{ - const char* error_text; - - if (dp_error == DP_ERR_OK) { - if (ret == EOK) { - error_text = NULL; - } else { - DEBUG(SSSDBG_CRIT_FAILURE, - "Bug: dp_error is OK on failed request\n"); - dp_error = DP_ERR_FATAL; - error_text = default_error_text; - } - } else if (dp_error == DP_ERR_OFFLINE) { - error_text = "Offline"; - } else if (dp_error == DP_ERR_FATAL && ret == ENOMEM) { - error_text = "Out of memory"; - } else { - error_text = default_error_text; - } - - sdap_handler_done(breq, dp_error, ret, error_text); -} - -static void -ipa_host_info_hosts_done(struct tevent_req *req) -{ - struct be_req *breq = tevent_req_callback_data(req, struct be_req); - int ret, dp_error; - - ret = hosts_get_recv(req, &dp_error); - talloc_zfree(req); - - ipa_host_info_complete(breq, dp_error, ret, "Host lookup failed"); -} - static errno_t hosts_get_retry(struct tevent_req *req); static void @@ -332,3 +234,82 @@ hosts_get_recv(struct tevent_req *req, return EOK; } + +struct ipa_hostid_handler_state { + struct dp_reply_std reply; +}; + +static void ipa_hostid_handler_done(struct tevent_req *subreq); + +struct tevent_req * +ipa_hostid_handler_send(TALLOC_CTX *mem_ctx, + struct ipa_hostid_ctx *hostid_ctx, + struct dp_hostid_data *data, + struct dp_req_params *params) +{ + struct ipa_hostid_handler_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ipa_hostid_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + subreq = hosts_get_send(state, params->ev, hostid_ctx, + data->name, data->alias); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send request\n"); + ret = ENOMEM; + goto immediately; + } + + tevent_req_set_callback(subreq, ipa_hostid_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_hostid_handler_done(struct tevent_req *subreq) +{ + struct ipa_hostid_handler_state *state; + struct tevent_req *req; + int dp_error; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_hostid_handler_state); + + ret = hosts_get_recv(subreq, &dp_error); + 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_hostid_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data) +{ + struct ipa_hostid_handler_state *state = NULL; + + state = tevent_req_data(req, struct ipa_hostid_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *data = state->reply; + + return EOK; +} diff --git a/src/providers/ipa/ipa_hostid.h b/src/providers/ipa/ipa_hostid.h index f88c2a21d..2611e455e 100644 --- a/src/providers/ipa/ipa_hostid.h +++ b/src/providers/ipa/ipa_hostid.h @@ -28,6 +28,15 @@ struct ipa_hostid_ctx { struct sdap_search_base **host_search_bases; }; -void ipa_host_info_handler(struct be_req *be_req); +struct tevent_req * +ipa_hostid_handler_send(TALLOC_CTX *mem_ctx, + struct ipa_hostid_ctx *hostid_ctx, + struct dp_hostid_data *data, + struct dp_req_params *params); + +errno_t +ipa_hostid_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data); #endif /* _IPA_HOSTID_H_ */ diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c index 29e22982c..e092cd2f8 100644 --- a/src/providers/ipa/ipa_id.c +++ b/src/providers/ipa/ipa_id.c @@ -30,6 +30,13 @@ #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 be_acct_req *ar); + +static int ipa_id_get_account_info_recv(struct tevent_req *req, int *dp_error); + static bool is_object_overridable(struct be_acct_req *ar) { bool ret = false; @@ -51,129 +58,9 @@ static bool is_object_overridable(struct be_acct_req *ar) return ret; } -static const char *ipa_account_info_error_text(int ret, int *dp_error, - const char *default_text) -{ - switch (*dp_error) { - case DP_ERR_OK: - if (ret == EOK) { - return NULL; - } - DEBUG(SSSDBG_CRIT_FAILURE, - "Bug: dp_error is OK on failed request\n"); - *dp_error = DP_ERR_FATAL; - break; - case DP_ERR_OFFLINE: - return "Offline"; - case DP_ERR_FATAL: - if (ret == ENOMEM) { - return "Out of memory"; - } - break; - default: - break; - } - - return default_text; -} - -static struct tevent_req * -ipa_id_get_account_info_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct ipa_id_ctx *ipa_ctx, struct be_req *be_req, - struct be_acct_req *ar); - -static int ipa_id_get_account_info_recv(struct tevent_req *req, int *dp_error); - -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); -static int ipa_id_get_netgroup_recv(struct tevent_req *req, int *dp_error); - -static void ipa_account_info_done(struct tevent_req *req); - -void ipa_account_info_handler(struct be_req *breq) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(breq); - struct ipa_id_ctx *ipa_ctx; - struct sdap_id_ctx *ctx; - struct be_acct_req *ar; - struct tevent_req *req = NULL; - - ipa_ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data, - struct ipa_id_ctx); - ctx = ipa_ctx->sdap_id_ctx; - - if (be_is_offline(ctx->be)) { - return sdap_handler_done(breq, DP_ERR_OFFLINE, EAGAIN, "Offline"); - } - - ar = talloc_get_type(be_req_get_data(breq), struct be_acct_req); - - if (sdap_is_enum_request(ar)) { - DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n"); - return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success"); - } - - if (strcasecmp(ar->domain, be_ctx->domain->name) != 0) { - /* if domain names do not match, this is a subdomain case - * subdomain lookups are handled differently on the server - * and the client - */ - req = ipa_subdomain_account_send(breq, be_ctx->ev, ipa_ctx, breq, ar); - - } else if ((ar->entry_type & BE_REQ_TYPE_MASK) == BE_REQ_NETGROUP) { - /* netgroups are handled by a separate request function */ - if (ar->filter_type != BE_FILTER_NAME) { - return sdap_handler_done(breq, DP_ERR_FATAL, - EINVAL, "Invalid filter type"); - } - req = ipa_id_get_netgroup_send(breq, be_ctx->ev, - ipa_ctx, ar->filter_value); - } else { - /* any account request is handled by sdap, - * any invalid request is caught there. */ - - req = ipa_id_get_account_info_send(breq, be_ctx->ev, ipa_ctx, breq, - ar); - } - - if (!req) { - return sdap_handler_done(breq, DP_ERR_FATAL, - ENOMEM, "Out of memory"); - } - tevent_req_set_callback(req, ipa_account_info_done, breq); -} - -static void ipa_account_info_done(struct tevent_req *req) -{ - struct be_req *breq = tevent_req_callback_data(req, struct be_req); - struct be_acct_req *ar = talloc_get_type(be_req_get_data(breq), - struct be_acct_req); - struct be_ctx *be_ctx = be_req_get_be_ctx(breq); - const char *error_text; - int ret, dp_error; - - if ((ar->entry_type & BE_REQ_TYPE_MASK) == BE_REQ_NETGROUP) { - ret = ipa_id_get_netgroup_recv(req, &dp_error); - } else { - if (strcasecmp(ar->domain, be_ctx->domain->name) != 0) { - ret = ipa_subdomain_account_recv(req, &dp_error); - } else { - ret = ipa_id_get_account_info_recv(req, &dp_error); - } - } - talloc_zfree(req); - - error_text = ipa_account_info_error_text(ret, &dp_error, - "Account info lookup failed"); - sdap_handler_done(breq, dp_error, ret, error_text); -} - struct ipa_resolve_user_list_state { struct tevent_context *ev; struct ipa_id_ctx *ipa_ctx; - struct be_req *be_req; struct ldb_message_element *users; const char *domain_name; size_t user_idx; @@ -186,7 +73,6 @@ static void ipa_resolve_user_list_get_user_done(struct tevent_req *subreq); static struct tevent_req * ipa_resolve_user_list_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct be_req *be_req, struct ipa_id_ctx *ipa_ctx, const char *domain_name, struct ldb_message_element *users) @@ -204,7 +90,6 @@ ipa_resolve_user_list_send(TALLOC_CTX *memctx, struct tevent_context *ev, state->ev = ev; state->ipa_ctx = ipa_ctx; - state->be_req = be_req; state->domain_name = domain_name; state->users = users; state->user_idx = 0; @@ -247,8 +132,7 @@ static errno_t ipa_resolve_user_list_get_user_step(struct tevent_req *req) DEBUG(SSSDBG_TRACE_ALL, "Trying to resolve user [%s].\n", ar->filter_value); - subreq = ipa_id_get_account_info_send(state, state->ev, state->ipa_ctx, - state->be_req, ar); + 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; @@ -581,7 +465,6 @@ struct ipa_id_get_account_info_state { struct sdap_id_op *op; struct sysdb_ctx *sysdb; struct sss_domain_info *domain; - struct be_req *be_req; struct be_acct_req *ar; struct be_acct_req *orig_ar; const char *realm; @@ -607,7 +490,7 @@ 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 be_req *be_req, + struct ipa_id_ctx *ipa_ctx, struct be_acct_req *ar) { int ret; @@ -642,7 +525,6 @@ ipa_id_get_account_info_send(TALLOC_CTX *memctx, struct tevent_context *ev, goto fail; } state->sysdb = state->domain->sysdb; - state->be_req = be_req; state->ar = ar; state->realm = dp_opt_get_string(state->ipa_ctx->ipa_options->basic, IPA_KRB5_REALM); @@ -855,6 +737,7 @@ static void ipa_id_get_account_info_orig_done(struct tevent_req *subreq) &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) { @@ -939,7 +822,7 @@ static void ipa_id_get_account_info_orig_done(struct tevent_req *subreq) if (state->ghosts != NULL) { /* Resolve ghost members */ - subreq = ipa_resolve_user_list_send(state, state->ev, state->be_req, + subreq = ipa_resolve_user_list_send(state, state->ev, state->ipa_ctx, state->domain->name, state->ghosts); @@ -1019,7 +902,7 @@ static void ipa_id_get_account_info_done(struct tevent_req *subreq) if (state->ghosts != NULL) { /* Resolve ghost members */ - subreq = ipa_resolve_user_list_send(state, state->ev, state->be_req, + subreq = ipa_resolve_user_list_send(state, state->ev, state->ipa_ctx, state->domain->name, state->ghosts); @@ -1315,14 +1198,134 @@ static int ipa_id_get_netgroup_recv(struct tevent_req *req, int *dp_error) return EOK; } +enum ipa_account_info_type { + IPA_ACCOUNT_INFO_SUBDOMAIN, + IPA_ACCOUNT_INFO_NETGROUP, + IPA_ACCOUNT_INFO_OTHER +}; -void ipa_check_online(struct be_req *be_req) +static enum ipa_account_info_type +ipa_decide_account_info_type(struct be_acct_req *data, struct be_ctx *be_ctx) { - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - struct ipa_id_ctx *ipa_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 be_acct_req *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; + } - ipa_ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data, - struct ipa_id_ctx); + 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; + } - return sdap_do_online_check(be_req, ipa_ctx->sdap_id_ctx); + 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; } diff --git a/src/providers/ipa/ipa_id.h b/src/providers/ipa/ipa_id.h index f59bc6a8e..17db5226a 100644 --- a/src/providers/ipa/ipa_id.h +++ b/src/providers/ipa/ipa_id.h @@ -33,7 +33,16 @@ #define IPA_DEFAULT_VIEW_NAME "Default Trust View" -void ipa_account_info_handler(struct be_req *breq); +struct tevent_req * +ipa_account_info_handler_send(TALLOC_CTX *mem_ctx, + struct ipa_id_ctx *id_ctx, + struct be_acct_req *data, + struct dp_req_params *params); + +errno_t ipa_account_info_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data); + struct tevent_req *ipa_get_netgroups_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct sysdb_ctx *sysdb, @@ -50,8 +59,6 @@ int ipa_get_netgroups_recv(struct tevent_req *req, size_t *reply_count, struct sysdb_attrs ***reply); -void ipa_check_online(struct be_req *be_req); - struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ipa_id_ctx *ipa_ctx, @@ -98,7 +105,6 @@ errno_t ipa_get_ad_override_recv(struct tevent_req *req, int *dp_error_out, struct tevent_req *ipa_subdomain_account_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct ipa_id_ctx *ipa_ctx, - struct be_req *be_req, struct be_acct_req *ar); errno_t ipa_subdomain_account_recv(struct tevent_req *req, int *dp_error_out); diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c index 6ef8ecafc..d3093b3b5 100644 --- a/src/providers/ipa/ipa_init.c +++ b/src/providers/ipa/ipa_init.c @@ -43,41 +43,20 @@ #include "providers/ipa/ipa_srv.h" #include "providers/be_dyndns.h" -struct ipa_options *ipa_options = NULL; +#define DNS_SRV_MISCONFIGURATION "SRV discovery is enabled on the IPA " \ + "server while using custom dns_discovery_domain. DNS discovery of " \ + "trusted AD domain will likely fail. It is recommended not to use " \ + "SRV discovery or the dns_discovery_domain option for the IPA " \ + "domain while running on the server itself\n" -/* Id Handler */ -struct bet_ops ipa_id_ops = { - .handler = ipa_account_info_handler, - .finalize = NULL, - .check_online = ipa_check_online -}; - -struct bet_ops ipa_auth_ops = { - .handler = ipa_auth, - .finalize = NULL, -}; - -struct bet_ops ipa_chpass_ops = { - .handler = ipa_auth, - .finalize = NULL, -}; - -struct bet_ops ipa_access_ops = { - .handler = ipa_access_handler, - .finalize = NULL -}; - -struct bet_ops ipa_selinux_ops = { - .handler = ipa_selinux_handler, - .finalize = NULL -}; +#define PREAUTH_INDICATOR_ERROR "Failed to create preauth indicator file, " \ + "special password prompting might not be available.\n" -#ifdef BUILD_SSH -struct bet_ops ipa_hostid_ops = { - .handler = ipa_host_info_handler, - .finalize = NULL +struct ipa_init_ctx { + struct ipa_options *options; + struct ipa_id_ctx *id_ctx; + struct ipa_auth_ctx *auth_ctx; }; -#endif static bool srv_in_server_list(const char *servers) { @@ -112,15 +91,17 @@ done: return has_srv; } -int common_ipa_init(struct be_ctx *bectx) +static errno_t ipa_init_options(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_options **_ipa_options) { + struct ipa_options *ipa_options; const char *ipa_servers; const char *ipa_backup_servers; - int ret; + errno_t ret; - ret = ipa_get_options(bectx, bectx->cdb, - bectx->conf_path, - bectx->domain, &ipa_options); + ret = ipa_get_options(mem_ctx, be_ctx->cdb, be_ctx->conf_path, + be_ctx->domain, &ipa_options); if (ret != EOK) { return ret; } @@ -128,278 +109,319 @@ int common_ipa_init(struct be_ctx *bectx) ipa_servers = dp_opt_get_string(ipa_options->basic, IPA_SERVER); ipa_backup_servers = dp_opt_get_string(ipa_options->basic, IPA_BACKUP_SERVER); - ret = ipa_service_init(ipa_options, bectx, ipa_servers, + ret = ipa_service_init(ipa_options, be_ctx, ipa_servers, ipa_backup_servers, ipa_options, &ipa_options->service); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init IPA failover service!\n"); + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init IPA service [%d]: %s\n", + ret, sss_strerror(ret)); + talloc_free(ipa_options); return ret; } + *_ipa_options = ipa_options; return EOK; } -static struct sdap_ext_member_ctx * -ipa_create_ext_members_ctx(TALLOC_CTX *mem_ctx, - struct ipa_id_ctx *id_ctx) +static errno_t ipa_init_id_ctx(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_options *ipa_options, + struct ipa_id_ctx **_ipa_id_ctx) { - struct sdap_ext_member_ctx *ext_ctx = NULL; + struct ipa_id_ctx *ipa_id_ctx = NULL; + struct sdap_id_ctx *sdap_id_ctx = NULL; + errno_t ret; - ext_ctx = talloc_zero(mem_ctx, struct sdap_ext_member_ctx); - if (ext_ctx == NULL) { - return NULL; + ipa_id_ctx = talloc_zero(mem_ctx, struct ipa_id_ctx); + if (ipa_id_ctx == NULL) { + ret = ENOMEM; + goto done; } - ext_ctx->pvt = id_ctx; - ext_ctx->ext_member_resolve_send = ipa_ext_group_member_send; - ext_ctx->ext_member_resolve_recv = ipa_ext_group_member_recv; + sdap_id_ctx = sdap_id_ctx_new(mem_ctx, be_ctx, ipa_options->service->sdap); + if (sdap_id_ctx == NULL) { + ret = ENOMEM; + goto done; + } - return ext_ctx; -} + ipa_id_ctx->ipa_options = ipa_options; + ipa_id_ctx->sdap_id_ctx = sdap_id_ctx; + ipa_options->id_ctx = ipa_id_ctx; -int sssm_ipa_id_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) -{ - struct ipa_id_ctx *ipa_ctx; - struct sdap_id_ctx *sdap_ctx; - const char *hostname; - const char *ipa_domain; - const char *ipa_servers; - struct ipa_srv_plugin_ctx *srv_ctx; - bool server_mode; - int ret; - - if (!ipa_options) { - ret = common_ipa_init(bectx); - if (ret != EOK) { - return ret; - } + ret = ipa_get_id_options(ipa_options, be_ctx->cdb, be_ctx->conf_path, + &sdap_id_ctx->opts); + if (ret != EOK) { + goto done; } - if (ipa_options->id_ctx) { - /* already initialized */ - *ops = &ipa_id_ops; - *pvt_data = ipa_options->id_ctx; - return EOK; - } + *_ipa_id_ctx = ipa_id_ctx; - ipa_ctx = talloc_zero(ipa_options, struct ipa_id_ctx); - if (!ipa_ctx) { - return ENOMEM; - } - ipa_options->id_ctx = ipa_ctx; - ipa_ctx->ipa_options = ipa_options; + ret = EOK; - sdap_ctx = sdap_id_ctx_new(ipa_options, bectx, ipa_options->service->sdap); - if (sdap_ctx == NULL) { - return ENOMEM; +done: + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init id context [%d]: %s\n", + ret, sss_strerror(ret)); + + talloc_free(ipa_id_ctx); + talloc_free(sdap_id_ctx); } - ipa_ctx->sdap_id_ctx = sdap_ctx; - ret = ipa_get_id_options(ipa_options, bectx->cdb, - bectx->conf_path, - &sdap_ctx->opts); + return ret; +} + + +static errno_t ipa_init_dyndns(struct be_ctx *be_ctx, + struct ipa_options *ipa_options) +{ + bool enabled; + errno_t ret; + + ret = ipa_get_dyndns_options(be_ctx, ipa_options); if (ret != EOK) { - goto done; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get dyndns options [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; } - ret = ipa_get_dyndns_options(bectx, ipa_options); - if (ret != EOK) { - goto done; + enabled = dp_opt_get_bool(ipa_options->dyndns_ctx->opts, + DP_OPT_DYNDNS_UPDATE); + if (!enabled) { + DEBUG(SSSDBG_CONF_SETTINGS, "Dynamic DNS updates are of.\n"); + return EOK; } - if (dp_opt_get_bool(ipa_options->dyndns_ctx->opts, DP_OPT_DYNDNS_UPDATE)) { - /* Perform automatic DNS updates when the - * IP address changes. - * Register a callback for successful LDAP - * reconnections. This is the easiest way to - * identify that we have gone online. - */ + /* Perform automatic DNS updates when the IP address changes. + * Register a callback for successful LDAP reconnections. + * This is the easiest way to identify that we have gone online. + */ - DEBUG(SSSDBG_CONF_SETTINGS, - "Dynamic DNS updates are on. Checking for nsupdate..\n"); - ret = be_nsupdate_check(); - if (ret == EOK) { - /* nsupdate is available. Dynamic updates - * are supported - */ - ret = ipa_dyndns_init(sdap_ctx->be, ipa_options); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Failure setting up automatic DNS update\n"); - /* We will continue without DNS updating */ - } - } - } + DEBUG(SSSDBG_CONF_SETTINGS, + "Dynamic DNS updates are on. Checking for nsupdate...\n"); - ret = setup_tls_config(sdap_ctx->opts->basic); + ret = be_nsupdate_check(); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "setup_tls_config failed [%d][%s].\n", - ret, strerror(ret)); - goto done; + DEBUG(SSSDBG_CONF_SETTINGS, "nsupdate is not availabe, " + "dynamic DNS updates will not work\n"); + return EOK; } + DEBUG(SSSDBG_CONF_SETTINGS, "nsupdate is available\n"); - /* Set up the ID mapping object */ - ret = ipa_idmap_init(sdap_ctx, sdap_ctx, &sdap_ctx->opts->idmap_ctx); + ret = ipa_dyndns_init(be_ctx, ipa_options); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Could not initialize ID mapping. In case ID mapping properties " - "changed on the server, please remove the SSSD database\n"); - goto done; + DEBUG(SSSDBG_MINOR_FAILURE, + "Failure setting up automatic DNS update\n"); + /* We will continue without DNS updating */ } + return EOK; +} - ret = ldap_id_setup_tasks(sdap_ctx); - if (ret != EOK) { - goto done; +static errno_t ipa_init_server_mode(struct be_ctx *be_ctx, + struct ipa_options *ipa_options, + struct ipa_id_ctx *ipa_id_ctx) +{ + const char *ipa_servers; + const char *dnsdomain; + const char *hostname; + bool sites_enabled; + errno_t ret; + + ipa_id_ctx->view_name = talloc_strdup(ipa_id_ctx, SYSDB_DEFAULT_VIEW_NAME); + if (ipa_id_ctx->view_name == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup() failed.\n"); + return ENOMEM; } - ret = sdap_setup_child(); + ret = sysdb_update_view_name(be_ctx->domain->sysdb, ipa_id_ctx->view_name); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "setup_child failed [%d][%s].\n", - ret, strerror(ret)); - goto done; + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add/update view name to sysdb.\n"); + return ret; } - /* setup SRV lookup plugin */ hostname = dp_opt_get_string(ipa_options->basic, IPA_HOSTNAME); - server_mode = dp_opt_get_bool(ipa_options->basic, IPA_SERVER_MODE); - - if (server_mode == true) { - ipa_ctx->view_name = talloc_strdup(ipa_ctx, SYSDB_DEFAULT_VIEW_NAME); - if (ipa_ctx->view_name == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); - ret = ENOMEM; - goto done; + ipa_servers = dp_opt_get_string(ipa_options->basic, IPA_SERVER); + sites_enabled = dp_opt_get_bool(ipa_options->basic, IPA_ENABLE_DNS_SITES); + dnsdomain = dp_opt_get_string(be_ctx->be_res->opts, DP_RES_OPT_DNS_DOMAIN); + + if (srv_in_server_list(ipa_servers) || sites_enabled) { + DEBUG(SSSDBG_MINOR_FAILURE, "SRV resolution or IPA sites enabled " + "on the IPA server. Site discovery of trusted AD servers " + "might not work.\n"); + + /* If SRV discovery is enabled on the server and + * dns_discovery_domain is set explicitly, then + * the current failover code would use the dns_discovery + * domain to try to find AD servers and fail. + */ + if (dnsdomain != NULL) { + sss_log(SSS_LOG_ERR, DNS_SRV_MISCONFIGURATION); + DEBUG(SSSDBG_CRIT_FAILURE, DNS_SRV_MISCONFIGURATION); } - ret = sysdb_update_view_name(bectx->domain->sysdb, ipa_ctx->view_name); + ret = be_fo_set_dns_srv_lookup_plugin(be_ctx, hostname); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Cannot add/update view name to sysdb.\n"); - goto done; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set SRV lookup plugin " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; } - ipa_servers = dp_opt_get_string(ipa_options->basic, IPA_SERVER); - if (srv_in_server_list(ipa_servers) == true - || dp_opt_get_bool(ipa_options->basic, - IPA_ENABLE_DNS_SITES) == true) { - DEBUG(SSSDBG_MINOR_FAILURE, "SRV resolution or IPA sites enabled " - "on the IPA server. Site discovery of trusted AD servers " - "might not work\n"); - - /* If SRV discovery is enabled on the server and - * dns_discovery_domain is set explicitly, then - * the current failover code would use the dns_discovery - * domain to try to find AD servers and fail - */ - if (dp_opt_get_string(bectx->be_res->opts, - DP_RES_OPT_DNS_DOMAIN)) { - sss_log(SSS_LOG_ERR, ("SRV discovery is enabled on the IPA " - "server while using custom dns_discovery_domain. " - "DNS discovery of trusted AD domain will likely fail. " - "It is recommended not to use SRV discovery or the " - "dns_discovery_domain option for the IPA domain while " - "running on the server itself\n")); - DEBUG(SSSDBG_CRIT_FAILURE, "SRV discovery is enabled on IPA " - "server while using custom dns_discovery_domain. " - "DNS discovery of trusted AD domain will likely fail. " - "It is recommended not to use SRV discovery or the " - "dns_discovery_domain option for the IPA domain while " - "running on the server itself\n"); - } - - ret = be_fo_set_dns_srv_lookup_plugin(bectx, hostname); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set SRV lookup plugin " - "[%d]: %s\n", ret, strerror(ret)); - goto done; - } + return EOK; + } else { + /* In server mode we need to ignore the dns_discovery_domain if set + * and only discover servers based on AD domains. */ + ret = dp_opt_set_string(be_ctx->be_res->opts, DP_RES_OPT_DNS_DOMAIN, + NULL); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Could not reset the " + "dns_discovery_domain, trusted AD domains discovery " + "might fail. Please remove dns_discovery_domain " + "from the config file and restart the SSSD\n"); } else { - /* In server mode we need to ignore the dns_discovery_domain if set - * and only discover servers based on AD domains - */ - ret = dp_opt_set_string(bectx->be_res->opts, DP_RES_OPT_DNS_DOMAIN, - NULL); - if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, "Could not reset the " - "dns_discovery_domain, trusted AD domains discovery " - "might fail. Please remove dns_discovery_domain " - "from the config file and restart the SSSD\n"); - } else { - DEBUG(SSSDBG_CONF_SETTINGS, "The value of dns_discovery_domain " - "will be ignored in ipa_server_mode\n"); - } + DEBUG(SSSDBG_CONF_SETTINGS, "The value of dns_discovery_domain " + "will be ignored in ipa_server_mode\n"); + } + } + + return EOK; +} + +static errno_t ipa_init_client_mode(struct be_ctx *be_ctx, + struct ipa_options *ipa_options, + struct ipa_id_ctx *ipa_id_ctx) +{ + struct ipa_srv_plugin_ctx *srv_ctx; + const char *ipa_domain; + const char *hostname; + bool sites_enabled; + errno_t ret; + + ret = sysdb_get_view_name(ipa_id_ctx, be_ctx->domain->sysdb, + &ipa_id_ctx->view_name); + if (ret == ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot find view name in the cache. " + "Will do online lookup later.\n"); + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_view_name() failed [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + hostname = dp_opt_get_string(ipa_options->basic, IPA_HOSTNAME); + sites_enabled = dp_opt_get_bool(ipa_options->basic, IPA_ENABLE_DNS_SITES); + + if (sites_enabled) { + /* use IPA plugin */ + ipa_domain = dp_opt_get_string(ipa_options->basic, IPA_DOMAIN); + srv_ctx = ipa_srv_plugin_ctx_init(be_ctx, be_ctx->be_res->resolv, + hostname, ipa_domain); + if (srv_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n"); + return ENOMEM; } + + be_fo_set_srv_lookup_plugin(be_ctx, ipa_srv_plugin_send, + ipa_srv_plugin_recv, srv_ctx, "IPA"); } else { - ret = sysdb_get_view_name(ipa_ctx, bectx->domain->sysdb, - &ipa_ctx->view_name); + /* fall back to standard plugin on clients. */ + ret = be_fo_set_dns_srv_lookup_plugin(be_ctx, hostname); if (ret != EOK) { - if (ret == ENOENT) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Cannot find view name in the cache. " \ - "Will do online lookup later.\n"); - } else { - DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_view_name failed.\n"); - goto done; - } + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set SRV lookup plugin " + "[%d]: %s\n", ret, strerror(ret)); + return ret; } + } - if (dp_opt_get_bool(ipa_options->basic, IPA_ENABLE_DNS_SITES)) { - /* use IPA plugin */ - ipa_domain = dp_opt_get_string(ipa_options->basic, IPA_DOMAIN); - srv_ctx = ipa_srv_plugin_ctx_init(bectx, bectx->be_res->resolv, - hostname, ipa_domain); - if (srv_ctx == NULL) { - DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n"); - ret = ENOMEM; - goto done; - } - - be_fo_set_srv_lookup_plugin(bectx, ipa_srv_plugin_send, - ipa_srv_plugin_recv, srv_ctx, "IPA"); - } else { - /* fall back to standard plugin on clients. */ - ret = be_fo_set_dns_srv_lookup_plugin(bectx, hostname); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set SRV lookup plugin " - "[%d]: %s\n", ret, strerror(ret)); - goto done; - } - } + return EOK; +} + +static errno_t ipa_init_ipa_auth_ctx(TALLOC_CTX *mem_ctx, + struct ipa_options *ipa_options, + struct ipa_id_ctx *ipa_id_ctx, + struct ipa_auth_ctx **_ipa_auth_ctx) +{ + struct ipa_auth_ctx *ipa_auth_ctx; + errno_t ret; + + ipa_auth_ctx = talloc_zero(mem_ctx, struct ipa_auth_ctx); + if (ipa_auth_ctx == NULL) { + return ENOMEM; } - /* setup periodical refresh of expired records */ - ret = sdap_refresh_init(bectx->refresh_ctx, sdap_ctx); - if (ret != EOK && ret != EEXIST) { - DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh " - "will not work [%d]: %s\n", ret, strerror(ret)); + ipa_auth_ctx->sdap_id_ctx = ipa_id_ctx->sdap_id_ctx; + + ret = dp_copy_options(ipa_auth_ctx, ipa_options->basic, + IPA_OPTS_BASIC, &ipa_auth_ctx->ipa_options); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "dp_copy_options failed.\n"); + talloc_free(ipa_auth_ctx); + return ret; } - ipa_ctx->sdap_id_ctx->opts->ext_ctx = ipa_create_ext_members_ctx( - ipa_ctx->sdap_id_ctx->opts, - ipa_ctx); - if (ipa_ctx->sdap_id_ctx->opts->ext_ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Unable to set SRV the extrernal group ctx\n"); - ret = ENOMEM; - goto done; + *_ipa_auth_ctx = ipa_auth_ctx; + + return EOK; +} + +static errno_t ipa_init_krb5_auth_ctx(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_options *ipa_options, + struct krb5_ctx **_krb5_auth_ctx) +{ + struct krb5_ctx *krb5_auth_ctx; + bool server_mode; + errno_t ret; + + krb5_auth_ctx = talloc_zero(mem_ctx, struct krb5_ctx); + if (krb5_auth_ctx == NULL) { + return ENOMEM; } - *ops = &ipa_id_ops; - *pvt_data = ipa_ctx; - ret = EOK; + krb5_auth_ctx->service = ipa_options->service->krb5_service; -done: + server_mode = dp_opt_get_bool(ipa_options->basic, IPA_SERVER_MODE); + krb5_auth_ctx->config_type = server_mode ? K5C_IPA_SERVER : K5C_IPA_CLIENT; + + ret = ipa_get_auth_options(ipa_options, be_ctx->cdb, be_ctx->conf_path, + &krb5_auth_ctx->opts); if (ret != EOK) { - talloc_zfree(ipa_options->id_ctx); + talloc_free(krb5_auth_ctx); + return ret; } - return ret; + + *_krb5_auth_ctx = krb5_auth_ctx; + return EOK; +} + +static errno_t ipa_init_sdap_auth_ctx(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_options *ipa_options, + struct sdap_auth_ctx **_sdap_auth_ctx) +{ + struct sdap_auth_ctx *sdap_auth_ctx; + + sdap_auth_ctx = talloc_zero(mem_ctx, struct sdap_auth_ctx); + if (sdap_auth_ctx == NULL) { + return ENOMEM; + } + + sdap_auth_ctx->be = be_ctx; + sdap_auth_ctx->service = ipa_options->service->sdap; + + if (ipa_options->id == NULL) { + talloc_free(sdap_auth_ctx); + return EINVAL; + } + + sdap_auth_ctx->opts = ipa_options->id; + + *_sdap_auth_ctx = sdap_auth_ctx; + + return EOK; } -void cleanup_ipa_preauth_indicator(void) +static void cleanup_ipa_preauth_indicator(void) { int ret; @@ -413,8 +435,8 @@ void cleanup_ipa_preauth_indicator(void) static errno_t create_ipa_preauth_indicator(void) { - int ret; - TALLOC_CTX *tmp_ctx = NULL; + TALLOC_CTX *tmp_ctx; + errno_t ret; int fd; tmp_ctx = talloc_new(NULL); @@ -455,324 +477,452 @@ done: return ret; } -int sssm_ipa_auth_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +static struct sdap_ext_member_ctx * +ipa_create_ext_members_ctx(TALLOC_CTX *mem_ctx, + struct ipa_id_ctx *id_ctx) +{ + struct sdap_ext_member_ctx *ext_ctx = NULL; + + ext_ctx = talloc_zero(mem_ctx, struct sdap_ext_member_ctx); + if (ext_ctx == NULL) { + return NULL; + } + + ext_ctx->pvt = id_ctx; + ext_ctx->ext_member_resolve_send = ipa_ext_group_member_send; + ext_ctx->ext_member_resolve_recv = ipa_ext_group_member_recv; + + return ext_ctx; +} + +static errno_t ipa_init_auth_ctx(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_options *ipa_options, + struct ipa_id_ctx *id_ctx, + struct ipa_auth_ctx **_auth_ctx) { + struct sdap_auth_ctx *sdap_auth_ctx; struct ipa_auth_ctx *ipa_auth_ctx; - struct ipa_id_ctx *id_ctx; struct krb5_ctx *krb5_auth_ctx; - struct sdap_auth_ctx *sdap_auth_ctx; - struct bet_ops *id_ops; - int ret; + errno_t ret; - if (!ipa_options) { - ret = common_ipa_init(bectx); - if (ret != EOK) { - return ret; - } + ret = ipa_init_ipa_auth_ctx(mem_ctx, ipa_options, id_ctx, &ipa_auth_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init IPA auth context\n"); + return ret; } - if (ipa_options->auth_ctx) { - /* already initialized */ - *ops = &ipa_auth_ops; - *pvt_data = ipa_options->auth_ctx; - return EOK; - } + ipa_options->auth_ctx = ipa_auth_ctx; - ipa_auth_ctx = talloc_zero(ipa_options, struct ipa_auth_ctx); - if (!ipa_auth_ctx) { - return ENOMEM; + ret = ipa_init_krb5_auth_ctx(ipa_auth_ctx, be_ctx, ipa_options, + &krb5_auth_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init KRB5 auth context\n"); + goto done; } - ipa_options->auth_ctx = ipa_auth_ctx; + ipa_options->auth_ctx->krb5_auth_ctx = krb5_auth_ctx; - ret = sssm_ipa_id_init(bectx, &id_ops, (void **) &id_ctx); + ret = ipa_init_sdap_auth_ctx(ipa_auth_ctx, be_ctx, ipa_options, + &sdap_auth_ctx); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ipa_id_init failed.\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init SDAP auth context\n"); goto done; } - ipa_auth_ctx->sdap_id_ctx = id_ctx->sdap_id_ctx; + ipa_options->auth_ctx->sdap_auth_ctx = sdap_auth_ctx; - ret = dp_copy_options(ipa_auth_ctx, ipa_options->basic, - IPA_OPTS_BASIC, &ipa_auth_ctx->ipa_options); + ret = setup_tls_config(sdap_auth_ctx->opts->basic); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "dp_copy_options failed.\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "setup_tls_config failed [%d]: %s\n", + ret, sss_strerror(ret)); goto done; } - krb5_auth_ctx = talloc_zero(ipa_auth_ctx, struct krb5_ctx); - if (!krb5_auth_ctx) { - ret = ENOMEM; + /* Initialize features needed by the krb5_child */ + ret = krb5_child_init(krb5_auth_ctx, be_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Could not initialize krb5_child " + "settings [%d]: %s\n", ret, sss_strerror(ret)); goto done; } - krb5_auth_ctx->service = ipa_options->service->krb5_service; - if (dp_opt_get_bool(id_ctx->ipa_options->basic, - IPA_SERVER_MODE) == true) { - krb5_auth_ctx->config_type = K5C_IPA_SERVER; - } else { - krb5_auth_ctx->config_type = K5C_IPA_CLIENT; + ret = create_ipa_preauth_indicator(); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, PREAUTH_INDICATOR_ERROR); + sss_log(SSSDBG_CRIT_FAILURE, PREAUTH_INDICATOR_ERROR); } - ipa_options->auth_ctx->krb5_auth_ctx = krb5_auth_ctx; - ret = ipa_get_auth_options(ipa_options, bectx->cdb, bectx->conf_path, - &krb5_auth_ctx->opts); + *_auth_ctx = ipa_auth_ctx; + ret = EOK; + +done: if (ret != EOK) { - goto done; + talloc_free(ipa_auth_ctx); } - sdap_auth_ctx = talloc_zero(ipa_auth_ctx, struct sdap_auth_ctx); - if (!sdap_auth_ctx) { - ret = ENOMEM; - goto done; + return ret; +} + +static errno_t ipa_init_misc(struct be_ctx *be_ctx, + struct ipa_options *ipa_options, + struct ipa_id_ctx *ipa_id_ctx, + struct sdap_id_ctx *sdap_id_ctx) +{ + errno_t ret; + + ret = ipa_init_dyndns(be_ctx, ipa_options); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init dyndns [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; } - sdap_auth_ctx->be = bectx; - sdap_auth_ctx->service = ipa_options->service->sdap; - if (ipa_options->id == NULL) { - ret = EINVAL; - goto done; + ret = setup_tls_config(sdap_id_ctx->opts->basic); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get TLS options [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; } - sdap_auth_ctx->opts = ipa_options->id; - ipa_options->auth_ctx->sdap_auth_ctx = sdap_auth_ctx; + ret = ipa_idmap_init(sdap_id_ctx, sdap_id_ctx, + &sdap_id_ctx->opts->idmap_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Could not initialize ID mapping. In case ID mapping properties " + "changed on the server, please remove the SSSD database\n"); + return ret; + } - ret = setup_tls_config(sdap_auth_ctx->opts->basic); + ret = ldap_id_setup_tasks(sdap_id_ctx); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "setup_tls_config failed [%d][%s].\n", - ret, strerror(ret)); + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup background tasks " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + + ret = sdap_setup_child(); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup sdap child [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + if (dp_opt_get_bool(ipa_options->basic, IPA_SERVER_MODE)) { + ret = ipa_init_server_mode(be_ctx, ipa_options, ipa_id_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init server mode " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + } else { + ret = ipa_init_client_mode(be_ctx, ipa_options, ipa_id_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init client mode " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + } + + ret = sdap_refresh_init(be_ctx->refresh_ctx, sdap_id_ctx); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh " + "will not work [%d]: %s\n", ret, sss_strerror(ret)); + } + + ipa_id_ctx->sdap_id_ctx->opts->ext_ctx = ipa_create_ext_members_ctx( + ipa_id_ctx->sdap_id_ctx->opts, ipa_id_ctx); + if (ipa_id_ctx->sdap_id_ctx->opts->ext_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set the extrernal group ctx\n"); + return ENOMEM; + } + + return EOK; +} + +errno_t sssm_ipa_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct data_provider *provider, + const char *module_name, + void **_module_data) +{ + struct ipa_init_ctx *init_ctx; + errno_t ret; + + init_ctx = talloc_zero(mem_ctx, struct ipa_init_ctx); + if (init_ctx == NULL) { + return ENOMEM; + } + + /* Always initialize options since it is needed everywhere. */ + ret = ipa_init_options(init_ctx, be_ctx, &init_ctx->options); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init IPA options " + "[%d]: %s\n", ret, sss_strerror(ret)); goto done; } - /* Initialize features needed by the krb5_child */ - ret = krb5_child_init(krb5_auth_ctx, bectx); + /* Always initialize id_ctx since it is needed everywhere. */ + ret = ipa_init_id_ctx(init_ctx, be_ctx, init_ctx->options, + &init_ctx->id_ctx); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Could not initialize krb5_child settings: [%s]\n", - strerror(ret)); + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init IPA ID context " + "[%d]: %s\n", ret, sss_strerror(ret)); goto done; } - ret = create_ipa_preauth_indicator(); + /* Setup miscellaneous things. */ + ret = ipa_init_misc(be_ctx, init_ctx->options, init_ctx->id_ctx, + init_ctx->id_ctx->sdap_id_ctx); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Failed to create preauth indicator file, special password " - "prompting might not be available.\n"); - sss_log(SSSDBG_CRIT_FAILURE, - "Failed to create preauth indicator file, special password " - "prompting might not be available.\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init IPA module " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + /* Initialize auth_ctx only if one of the target is enabled. */ + if (dp_target_enabled(provider, module_name, DPT_AUTH, DPT_CHPASS)) { + ret = ipa_init_auth_ctx(init_ctx, be_ctx, init_ctx->options, + init_ctx->id_ctx, &init_ctx->auth_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init IPA auth context " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } } - *ops = &ipa_auth_ops; - *pvt_data = ipa_auth_ctx; + *_module_data = init_ctx; + ret = EOK; done: if (ret != EOK) { - talloc_zfree(ipa_options->auth_ctx); + talloc_free(init_ctx); } + return ret; } -int sssm_ipa_chpass_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +errno_t sssm_ipa_id_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - int ret; - ret = sssm_ipa_auth_init(bectx, ops, pvt_data); - *ops = &ipa_chpass_ops; - return ret; + struct ipa_init_ctx *init_ctx; + struct ipa_id_ctx *id_ctx; + + init_ctx = talloc_get_type(module_data, struct ipa_init_ctx); + id_ctx = init_ctx->id_ctx; + + dp_set_method(dp_methods, DPM_ACCOUNT_HANDLER, + ipa_account_info_handler_send, ipa_account_info_handler_recv, id_ctx, + struct ipa_id_ctx, struct be_acct_req, struct dp_reply_std); + + dp_set_method(dp_methods, DPM_CHECK_ONLINE, + sdap_online_check_handler_send, sdap_online_check_handler_recv, id_ctx->sdap_id_ctx, + struct sdap_id_ctx, void, struct dp_reply_std); + + return EOK; } -int sssm_ipa_access_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +errno_t sssm_ipa_auth_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - int ret; - struct ipa_access_ctx *ipa_access_ctx; + struct ipa_init_ctx *init_ctx; + struct ipa_auth_ctx *auth_ctx; + + init_ctx = talloc_get_type(module_data, struct ipa_init_ctx); + auth_ctx = init_ctx->auth_ctx; + + dp_set_method(dp_methods, DPM_AUTH_HANDLER, + ipa_pam_auth_handler_send, ipa_pam_auth_handler_recv, auth_ctx, + struct ipa_auth_ctx, struct pam_data, struct pam_data *); + + return EOK; +} + +errno_t sssm_ipa_chpass_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) +{ + return sssm_ipa_auth_init(mem_ctx, be_ctx, module_data, dp_methods); +} + +errno_t sssm_ipa_access_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) +{ + struct ipa_access_ctx *access_ctx; + struct ipa_init_ctx *init_ctx; struct ipa_id_ctx *id_ctx; + errno_t ret; - ipa_access_ctx = talloc_zero(bectx, struct ipa_access_ctx); - if (ipa_access_ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); + init_ctx = talloc_get_type(module_data, struct ipa_init_ctx); + id_ctx = init_ctx->id_ctx; + + access_ctx = talloc_zero(mem_ctx, struct ipa_access_ctx); + if (access_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed.\n"); return ENOMEM; } - ret = sssm_ipa_id_init(bectx, ops, (void **) &id_ctx); + access_ctx->sdap_ctx = id_ctx->sdap_id_ctx; + access_ctx->host_map = id_ctx->ipa_options->host_map; + access_ctx->hostgroup_map = id_ctx->ipa_options->hostgroup_map; + access_ctx->host_search_bases = id_ctx->ipa_options->host_search_bases; + access_ctx->hbac_search_bases = id_ctx->ipa_options->hbac_search_bases; + + ret = dp_copy_options(access_ctx, id_ctx->ipa_options->basic, + IPA_OPTS_BASIC, &access_ctx->ipa_options); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ipa_id_init failed.\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "dp_copy_options() failed.\n"); goto done; } - ipa_access_ctx->sdap_ctx = id_ctx->sdap_id_ctx; - ipa_access_ctx->host_map = id_ctx->ipa_options->host_map; - ipa_access_ctx->hostgroup_map = id_ctx->ipa_options->hostgroup_map; - ipa_access_ctx->host_search_bases = id_ctx->ipa_options->host_search_bases; - ipa_access_ctx->hbac_search_bases = id_ctx->ipa_options->hbac_search_bases; - ret = dp_copy_options(ipa_access_ctx, ipa_options->basic, - IPA_OPTS_BASIC, &ipa_access_ctx->ipa_options); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "dp_copy_options failed.\n"); + /* Set up an sdap_access_ctx for checking expired/locked accounts. */ + access_ctx->sdap_access_ctx = talloc_zero(access_ctx, struct sdap_access_ctx); + if (access_ctx->sdap_access_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n"); + ret = ENOMEM; goto done; } - /* Set up an sdap_access_ctx for checking expired/locked - * accounts. - */ - ipa_access_ctx->sdap_access_ctx = - talloc_zero(ipa_access_ctx, struct sdap_access_ctx); + access_ctx->sdap_access_ctx->id_ctx = access_ctx->sdap_ctx; + access_ctx->sdap_access_ctx->access_rule[0] = LDAP_ACCESS_EXPIRE; + access_ctx->sdap_access_ctx->access_rule[1] = LDAP_ACCESS_EMPTY; - ipa_access_ctx->sdap_access_ctx->id_ctx = ipa_access_ctx->sdap_ctx; - ipa_access_ctx->sdap_access_ctx->access_rule[0] = LDAP_ACCESS_EXPIRE; - ipa_access_ctx->sdap_access_ctx->access_rule[1] = LDAP_ACCESS_EMPTY; + dp_set_method(dp_methods, DPM_ACCESS_HANDLER, + ipa_pam_access_handler_send, ipa_pam_access_handler_recv, access_ctx, + struct ipa_access_ctx, struct pam_data, struct pam_data *); - *ops = &ipa_access_ops; - *pvt_data = ipa_access_ctx; + ret = EOK; done: if (ret != EOK) { - talloc_free(ipa_access_ctx); + talloc_free(access_ctx); } + return ret; } -int sssm_ipa_selinux_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +errno_t sssm_ipa_selinux_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - int ret; +#if defined HAVE_SELINUX && defined HAVE_SELINUX_LOGIN_DIR struct ipa_selinux_ctx *selinux_ctx; + struct ipa_init_ctx *init_ctx; struct ipa_options *opts; - selinux_ctx = talloc_zero(bectx, struct ipa_selinux_ctx); + init_ctx = talloc_get_type(module_data, struct ipa_init_ctx); + opts = init_ctx->options; + + selinux_ctx = talloc_zero(mem_ctx, struct ipa_selinux_ctx); if (selinux_ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed.\n"); return ENOMEM; } - ret = sssm_ipa_id_init(bectx, ops, (void **) &selinux_ctx->id_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ipa_id_init failed.\n"); - goto done; - } - - opts = selinux_ctx->id_ctx->ipa_options; - + selinux_ctx->id_ctx = init_ctx->id_ctx; selinux_ctx->hbac_search_bases = opts->hbac_search_bases; selinux_ctx->host_search_bases = opts->host_search_bases; selinux_ctx->selinux_search_bases = opts->selinux_search_bases; - *ops = &ipa_selinux_ops; - *pvt_data = selinux_ctx; + dp_set_method(dp_methods, DPM_SELINUX_HANDLER, + ipa_selinux_handler_send, ipa_selinux_handler_recv, selinux_ctx, + struct ipa_selinux_ctx, struct pam_data, struct pam_data *); -done: - if (ret != EOK) { - talloc_free(selinux_ctx); - } - return ret; + return EOK; +#else + DEBUG(SSSDBG_MINOR_FAILURE, "SELinux init handler called but SSSD is " + "built without SSH support, ignoring\n"); + return EOK; +#endif } -#ifdef BUILD_SSH -int sssm_ipa_hostid_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +errno_t sssm_ipa_hostid_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - int ret; +#ifdef BUILD_SSH struct ipa_hostid_ctx *hostid_ctx; - struct ipa_id_ctx *id_ctx; + struct ipa_init_ctx *init_ctx; + + init_ctx = talloc_get_type(module_data, struct ipa_init_ctx); - hostid_ctx = talloc_zero(bectx, struct ipa_hostid_ctx); + hostid_ctx = talloc_zero(mem_ctx, struct ipa_hostid_ctx); if (hostid_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); return ENOMEM; } - ret = sssm_ipa_id_init(bectx, ops, (void **) &id_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ipa_id_init failed.\n"); - goto done; - } - hostid_ctx->sdap_id_ctx = id_ctx->sdap_id_ctx; - hostid_ctx->host_search_bases = id_ctx->ipa_options->host_search_bases; - hostid_ctx->ipa_opts = ipa_options; + hostid_ctx->sdap_id_ctx = init_ctx->id_ctx->sdap_id_ctx; + hostid_ctx->host_search_bases = init_ctx->options->host_search_bases; + hostid_ctx->ipa_opts = init_ctx->options; - *ops = &ipa_hostid_ops; - *pvt_data = hostid_ctx; + dp_set_method(dp_methods, DPM_HOSTID_HANDLER, + ipa_hostid_handler_send, ipa_hostid_handler_recv, hostid_ctx, + struct ipa_hostid_ctx, struct dp_hostid_data, struct dp_reply_std); -done: - if (ret != EOK) { - talloc_free(hostid_ctx); - } - return ret; -} + return EOK; +#else + DEBUG(SSSDBG_MINOR_FAILURE, "HostID init handler called but SSSD is " + "built without SSH support, ignoring\n"); + return EOK; #endif +} -int sssm_ipa_autofs_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +errno_t sssm_ipa_autofs_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { #ifdef BUILD_AUTOFS - struct ipa_id_ctx *id_ctx; - int ret; + struct ipa_init_ctx *init_ctx; DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing IPA autofs handler\n"); + init_ctx = talloc_get_type(module_data, struct ipa_init_ctx); - ret = sssm_ipa_id_init(bectx, ops, (void **) &id_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ipa_id_init failed.\n"); - return ret; - } - - return ipa_autofs_init(bectx, id_ctx, ops, pvt_data); + return ipa_autofs_init(mem_ctx, be_ctx, init_ctx->id_ctx, dp_methods); #else DEBUG(SSSDBG_MINOR_FAILURE, "Autofs init handler called but SSSD is " - "built without autofs support, ignoring\n"); + "built without autofs support, ignoring\n"); return EOK; #endif } -int sssm_ipa_subdomains_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +errno_t sssm_ipa_subdomains_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - int ret; - struct ipa_id_ctx *id_ctx; + struct ipa_init_ctx *init_ctx; - ret = sssm_ipa_id_init(bectx, ops, (void **) &id_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ipa_id_init failed.\n"); - return ret; - } + DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing IPA subdomains handler\n"); + init_ctx = talloc_get_type(module_data, struct ipa_init_ctx); - ret = ipa_subdom_init(bectx, id_ctx, ops, pvt_data); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "ipa_subdom_init failed.\n"); - return ret; - } - - return EOK; + return ipa_subdomains_init(mem_ctx, be_ctx, init_ctx->id_ctx, dp_methods); } -int sssm_ipa_sudo_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +errno_t sssm_ipa_sudo_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { #ifdef BUILD_SUDO - struct ipa_id_ctx *id_ctx; - int ret; + struct ipa_init_ctx *init_ctx; DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing IPA sudo handler\n"); + init_ctx = talloc_get_type(module_data, struct ipa_init_ctx); - ret = sssm_ipa_id_init(bectx, ops, (void **) &id_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ipa_id_init failed.\n"); - return ret; - } - - return ipa_sudo_init(bectx, id_ctx, ops, pvt_data); + return ipa_sudo_init(mem_ctx, be_ctx, init_ctx->id_ctx, dp_methods); #else DEBUG(SSSDBG_MINOR_FAILURE, "Sudo init handler called but SSSD is " - "built without sudo support, ignoring\n"); + "built without sudo support, ignoring\n"); return EOK; #endif } diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c index 13eabc00b..78fe9e3d2 100644 --- a/src/providers/ipa/ipa_selinux.c +++ b/src/providers/ipa/ipa_selinux.c @@ -37,8 +37,6 @@ #include "providers/ipa/ipa_selinux_maps.h" #include "providers/ipa/ipa_subdomains.h" -#if defined HAVE_SELINUX && defined HAVE_SELINUX_LOGIN_DIR - #ifndef SELINUX_CHILD_DIR #ifndef SSSD_LIBEXEC_PATH #error "SSSD_LIBEXEC_PATH not defined" @@ -70,15 +68,6 @@ static errno_t ipa_get_selinux_recv(struct tevent_req *req, char **default_user, char **map_order); -static struct ipa_selinux_op_ctx * -ipa_selinux_create_op_ctx(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, - struct sss_domain_info *ipa_domain, - struct sss_domain_info *user_domain, - struct be_req *be_req, const char *username, - const char *hostname, - struct ipa_selinux_ctx *selinux_ctx); -static void ipa_selinux_handler_done(struct tevent_req *subreq); - static void ipa_get_selinux_connect_done(struct tevent_req *subreq); static void ipa_get_selinux_hosts_done(struct tevent_req *subreq); static void ipa_get_config_step(struct tevent_req *req); @@ -94,83 +83,6 @@ static errno_t ipa_selinux_process_maps(TALLOC_CTX *mem_ctx, size_t hbac_rule_count, struct sysdb_attrs ***usermaps); -struct ipa_selinux_op_ctx { - struct be_req *be_req; - struct sss_domain_info *user_domain; - struct sss_domain_info *ipa_domain; - struct ipa_selinux_ctx *selinux_ctx; - - struct sysdb_attrs *user; - struct sysdb_attrs *host; -}; - -void ipa_selinux_handler(struct be_req *be_req) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - struct ipa_selinux_ctx *selinux_ctx; - struct ipa_selinux_op_ctx *op_ctx; - struct tevent_req *req; - struct pam_data *pd; - const char *hostname; - struct sss_domain_info *user_domain; - struct be_ctx *subdom_be_ctx; - - pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); - - selinux_ctx = talloc_get_type(be_ctx->bet_info[BET_SELINUX].pvt_bet_data, - struct ipa_selinux_ctx); - - hostname = dp_opt_get_string(selinux_ctx->id_ctx->ipa_options->basic, - IPA_HOSTNAME); - if (!hostname) { - DEBUG(SSSDBG_OP_FAILURE, "Cannot determine this machine's host name\n"); - goto fail; - } - - if (strcasecmp(pd->domain, be_ctx->domain->name) != 0) { - subdom_be_ctx = ipa_get_subdomains_be_ctx(be_ctx); - if (subdom_be_ctx == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "Subdomains are not configured, " \ - "cannot lookup domain [%s].\n", - pd->domain); - goto fail; - } else { - user_domain = find_domain_by_name(subdom_be_ctx->domain, - pd->domain, true); - if (user_domain == NULL) { - DEBUG(SSSDBG_MINOR_FAILURE, "No domain entry found " \ - "for [%s].\n", pd->domain); - goto fail; - } - } - } else { - user_domain = be_ctx->domain; - } - - op_ctx = ipa_selinux_create_op_ctx(be_req, user_domain->sysdb, - be_ctx->domain, - user_domain, - be_req, pd->user, hostname, - selinux_ctx); - if (op_ctx == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "Cannot create op context\n"); - goto fail; - } - - req = ipa_get_selinux_send(be_req, be_ctx, - op_ctx->user, op_ctx->host, selinux_ctx); - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "Cannot initiate the search\n"); - goto fail; - } - - tevent_req_set_callback(req, ipa_selinux_handler_done, op_ctx); - return; - -fail: - be_req_terminate(be_req, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL); -} - static errno_t ipa_save_user_maps(struct sysdb_ctx *sysdb, struct sss_domain_info *domain, @@ -217,246 +129,18 @@ done: return ret; } -static struct ipa_selinux_op_ctx * -ipa_selinux_create_op_ctx(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, - struct sss_domain_info *ipa_domain, - struct sss_domain_info *user_domain, - struct be_req *be_req, const char *username, - const char *hostname, - struct ipa_selinux_ctx *selinux_ctx) -{ - struct ipa_selinux_op_ctx *op_ctx; - struct ldb_dn *host_dn; - const char *attrs[] = { SYSDB_ORIG_DN, - SYSDB_ORIG_MEMBEROF, - NULL }; - size_t count; - struct ldb_message **msgs; - struct sysdb_attrs **hosts; - errno_t ret; - - op_ctx = talloc_zero(mem_ctx, struct ipa_selinux_op_ctx); - if (op_ctx == NULL) { - return NULL; - } - op_ctx->be_req = be_req; - op_ctx->ipa_domain = ipa_domain; - op_ctx->user_domain = user_domain; - op_ctx->selinux_ctx = selinux_ctx; - - ret = sss_selinux_extract_user(op_ctx, user_domain, username, &op_ctx->user); - if (ret != EOK) { - goto fail; - } - - host_dn = sysdb_custom_dn(op_ctx, ipa_domain, hostname, HBAC_HOSTS_SUBDIR); - if (host_dn == NULL) { - goto fail; - } - - /* Look up the host to get its originalMemberOf entries */ - ret = sysdb_search_entry(op_ctx, sysdb, host_dn, - LDB_SCOPE_BASE, NULL, - attrs, &count, &msgs); - if (ret == ENOENT || count == 0) { - op_ctx->host = NULL; - return op_ctx; - } else if (ret != EOK) { - goto fail; - } else if (count > 1) { - DEBUG(SSSDBG_OP_FAILURE, "More than one result for a BASE search!\n"); - goto fail; - } - - ret = sysdb_msg2attrs(op_ctx, count, msgs, &hosts); - talloc_free(msgs); - if (ret != EOK) { - goto fail; - } - - op_ctx->host = hosts[0]; - return op_ctx; - -fail: - talloc_free(op_ctx); - return NULL; -} - struct map_order_ctx { char *order; char **order_array; size_t order_count; }; -static errno_t init_map_order_ctx(TALLOC_CTX *mem_ctx, const char *map_order, - struct map_order_ctx **_mo_ctx); - struct selinux_child_input { const char *seuser; const char *mls_range; const char *username; }; -static errno_t choose_best_seuser(TALLOC_CTX *mem_ctx, - struct sysdb_attrs **usermaps, - struct pam_data *pd, - struct sss_domain_info *user_domain, - struct map_order_ctx *mo_ctx, - const char *default_user, - struct selinux_child_input **_sci); - -static struct tevent_req *selinux_child_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct selinux_child_input *sci); -static errno_t selinux_child_recv(struct tevent_req *req); - -static void ipa_selinux_child_done(struct tevent_req *child_req); - -static void ipa_selinux_handler_done(struct tevent_req *req) -{ - struct ipa_selinux_op_ctx *op_ctx = tevent_req_callback_data(req, struct ipa_selinux_op_ctx); - struct be_req *breq = op_ctx->be_req; - struct be_ctx *be_ctx = be_req_get_be_ctx(breq); - struct sysdb_ctx *sysdb = op_ctx->ipa_domain->sysdb; - errno_t ret, sret; - size_t map_count = 0; - struct sysdb_attrs **maps = NULL; - bool in_transaction = false; - char *default_user = NULL; - struct pam_data *pd = - talloc_get_type(be_req_get_data(breq), struct pam_data); - char *map_order = NULL; - size_t hbac_count = 0; - struct sysdb_attrs **hbac_rules = 0; - struct sysdb_attrs **best_match_maps; - struct map_order_ctx *map_order_ctx; - struct selinux_child_input *sci = NULL; - struct tevent_req *child_req; - - ret = ipa_get_selinux_recv(req, breq, &map_count, &maps, - &hbac_count, &hbac_rules, - &default_user, &map_order); - if (ret != EOK) { - goto fail; - } - - ret = sysdb_transaction_start(sysdb); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n"); - goto fail; - } - in_transaction = true; - - ret = sysdb_delete_usermaps(op_ctx->ipa_domain); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Cannot delete existing maps from sysdb\n"); - goto fail; - } - - ret = sysdb_store_selinux_config(op_ctx->ipa_domain, - default_user, map_order); - if (ret != EOK) { - goto fail; - } - - if (map_count > 0) { - ret = ipa_save_user_maps(sysdb, op_ctx->ipa_domain, map_count, maps); - if (ret != EOK) { - goto fail; - } - } - - ret = sysdb_transaction_commit(sysdb); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Could not commit transaction\n"); - goto fail; - } - in_transaction = false; - - /* Process the maps and return list of best matches (maps with - * highest priority). The input maps are also parent memory - * context for the output list of best matches. The best match - * maps should never be freed explicitly but always through - * their parent (or any indirect parent) */ - ret = ipa_selinux_process_maps(maps, op_ctx->user, op_ctx->host, - maps, map_count, - hbac_rules, hbac_count, &best_match_maps); - if (ret != EOK) { - goto fail; - } - - ret = init_map_order_ctx(op_ctx, map_order, &map_order_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Failed to create ordered SELinux users array.\n"); - goto fail; - } - - ret = choose_best_seuser(breq, - best_match_maps, pd, op_ctx->user_domain, - map_order_ctx, default_user, &sci); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Failed to evaluate ordered SELinux users array.\n"); - goto fail; - } - - /* Update the SELinux context in a privileged child as the back end is - * running unprivileged - */ - child_req = selinux_child_send(breq, be_ctx->ev, sci); - if (child_req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "selinux_child_send() failed\n"); - ret = ENOMEM; - goto fail; - } - tevent_req_set_callback(child_req, ipa_selinux_child_done, op_ctx); - return; - -fail: - if (in_transaction) { - sret = sysdb_transaction_cancel(sysdb); - if (sret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n"); - } - } - if (ret == EAGAIN) { - be_req_terminate(breq, DP_ERR_OFFLINE, EAGAIN, "Offline"); - } else { - be_req_terminate(breq, DP_ERR_FATAL, ret, NULL); - } -} - -static void ipa_selinux_child_done(struct tevent_req *child_req) -{ - errno_t ret; - struct ipa_selinux_op_ctx *op_ctx; - struct be_req *breq; - struct pam_data *pd; - struct be_ctx *be_ctx; - - op_ctx = tevent_req_callback_data(child_req, struct ipa_selinux_op_ctx); - breq = op_ctx->be_req; - pd = talloc_get_type(be_req_get_data(breq), struct pam_data); - be_ctx = be_req_get_be_ctx(breq); - - ret = selinux_child_recv(child_req); - talloc_free(child_req); - if (ret != EOK) { - be_req_terminate(breq, DP_ERR_FATAL, ret, NULL); - return; - } - - /* If we got here in online mode, set last_update to current time */ - if (!be_is_offline(be_ctx)) { - op_ctx->selinux_ctx->last_update = time(NULL); - } - - pd->pam_status = PAM_SUCCESS; - be_req_terminate(breq, DP_ERR_OK, EOK, "Success"); -} - static errno_t ipa_selinux_process_seealso_maps(struct sysdb_attrs *user, struct sysdb_attrs *host, @@ -1023,8 +707,8 @@ static errno_t selinux_child_create_buffer(struct selinux_child_state *state) static errno_t selinux_fork_child(struct selinux_child_state *state) { - int pipefd_to_child[2] = PIPE_INIT; - int pipefd_from_child[2] = PIPE_INIT; + int pipefd_to_child[2]; + int pipefd_from_child[2]; pid_t pid; errno_t ret; @@ -1033,7 +717,7 @@ static errno_t selinux_fork_child(struct selinux_child_state *state) ret = errno; DEBUG(SSSDBG_CRIT_FAILURE, "pipe failed [%d][%s].\n", errno, sss_strerror(errno)); - goto fail; + return ret; } ret = pipe(pipefd_to_child); @@ -1041,23 +725,22 @@ static errno_t selinux_fork_child(struct selinux_child_state *state) ret = errno; DEBUG(SSSDBG_CRIT_FAILURE, "pipe failed [%d][%s].\n", errno, sss_strerror(errno)); - goto fail; + return ret; } pid = fork(); if (pid == 0) { /* child */ - exec_child(state, - pipefd_to_child, pipefd_from_child, + exec_child(state, pipefd_to_child, pipefd_from_child, SELINUX_CHILD, selinux_child_debug_fd); - - /* We should never get here */ - DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Could not exec selinux_child\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "Could not exec selinux_child: [%d][%s].\n", + ret, sss_strerror(ret)); + return ret; } else if (pid > 0) { /* parent */ state->io->read_from_child_fd = pipefd_from_child[0]; - PIPE_FD_CLOSE(pipefd_from_child[1]); + close(pipefd_from_child[1]); state->io->write_to_child_fd = pipefd_to_child[1]; - PIPE_FD_CLOSE(pipefd_to_child[0]); + close(pipefd_to_child[0]); sss_fd_nonblocking(state->io->read_from_child_fd); sss_fd_nonblocking(state->io->write_to_child_fd); @@ -1065,21 +748,16 @@ static errno_t selinux_fork_child(struct selinux_child_state *state) if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up child signal handler\n"); - goto fail; + return ret; } } else { /* error */ ret = errno; DEBUG(SSSDBG_CRIT_FAILURE, "fork failed [%d][%s].\n", errno, sss_strerror(errno)); - goto fail; + return ret; } return EOK; - -fail: - PIPE_CLOSE(pipefd_from_child); - PIPE_CLOSE(pipefd_to_child); - return ret; } static void selinux_child_step(struct tevent_req *subreq) @@ -1099,7 +777,8 @@ static void selinux_child_step(struct tevent_req *subreq) return; } - PIPE_FD_CLOSE(state->io->write_to_child_fd); + close(state->io->write_to_child_fd); + state->io->write_to_child_fd = -1; subreq = read_pipe_send(state, state->ev, state->io->read_from_child_fd); if (subreq == NULL) { @@ -1128,7 +807,8 @@ static void selinux_child_done(struct tevent_req *subreq) return; } - PIPE_FD_CLOSE(state->io->read_from_child_fd); + close(state->io->read_from_child_fd); + state->io->read_from_child_fd = -1; ret = selinux_child_parse_response(buf, len, &child_result); if (ret != EOK) { @@ -1278,9 +958,8 @@ static void ipa_get_selinux_connect_done(struct tevent_req *subreq) int dp_error = DP_ERR_FATAL; int ret; struct ipa_id_ctx *id_ctx = state->selinux_ctx->id_ctx; - - const char *access_name; - const char *selinux_name; + struct dp_module *access_mod; + struct dp_module *selinux_mod; const char *hostname; ret = sdap_id_op_connect_recv(subreq, &dp_error); @@ -1300,9 +979,9 @@ static void ipa_get_selinux_connect_done(struct tevent_req *subreq) goto fail; } - access_name = state->be_ctx->bet_info[BET_ACCESS].mod_name; - selinux_name = state->be_ctx->bet_info[BET_SELINUX].mod_name; - if (strcasecmp(access_name, selinux_name) == 0 && state->host != NULL) { + access_mod = dp_target_module(state->be_ctx->provider, DPT_ACCESS); + selinux_mod = dp_target_module(state->be_ctx->provider, DPT_SELINUX); + if (access_mod == selinux_mod && state->host != NULL) { /* If the access control module is the same as the selinux module * and the access control had already discovered the host */ @@ -1506,11 +1185,9 @@ static void ipa_get_selinux_maps_done(struct tevent_req *subreq) { struct tevent_req *req; struct ipa_get_selinux_state *state; - struct ipa_id_ctx *id_ctx; - - char *selinux_name; - char *access_name; + struct dp_module *access_mod; + struct dp_module *selinux_mod; const char *tmp_str; bool check_hbac; @@ -1548,9 +1225,9 @@ static void ipa_get_selinux_maps_done(struct tevent_req *subreq) } if (check_hbac) { - access_name = state->be_ctx->bet_info[BET_ACCESS].mod_name; - selinux_name = state->be_ctx->bet_info[BET_SELINUX].mod_name; - if (strcasecmp(access_name, selinux_name) == 0) { + access_mod = dp_target_module(state->be_ctx->provider, DPT_ACCESS); + selinux_mod = dp_target_module(state->be_ctx->provider, DPT_SELINUX); + if (access_mod == selinux_mod) { ret = hbac_get_cached_rules(state, state->be_ctx->domain, &state->hbac_rule_count, &state->hbac_rules); @@ -1656,16 +1333,367 @@ ipa_get_selinux_recv(struct tevent_req *req, return EOK; } -/*end of #if defined HAVE_SELINUX && defined HAVE_SELINUX_LOGIN_DIR */ -#else -/* Simply return success if HAVE_SELINUX_LOGIN_DIR is not defined. */ -void ipa_selinux_handler(struct be_req *be_req) +static errno_t +ipa_selinux_init_attrs(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *ipa_domain, + struct sss_domain_info *user_domain, + const char *username, + const char *hostname, + struct sysdb_attrs **_user, + struct sysdb_attrs **_host) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_dn *host_dn; + const char *attrs[] = { SYSDB_ORIG_DN, + SYSDB_ORIG_MEMBEROF, + NULL }; + size_t count; + struct ldb_message **msgs; + struct sysdb_attrs **hosts; + struct sysdb_attrs *user = NULL; + struct sysdb_attrs *host = NULL; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sss_selinux_extract_user(tmp_ctx, user_domain, username, &user); + if (ret != EOK) { + goto done; + } + + host_dn = sysdb_custom_dn(tmp_ctx, ipa_domain, hostname, HBAC_HOSTS_SUBDIR); + if (host_dn == NULL) { + goto done; + } + + /* Look up the host to get its originalMemberOf entries */ + ret = sysdb_search_entry(tmp_ctx, sysdb, host_dn, LDB_SCOPE_BASE, NULL, + attrs, &count, &msgs); + if (ret == ENOENT || count == 0) { + host = NULL; + ret = EOK; + goto done; + } else if (ret != EOK) { + goto done; + } else if (count > 1) { + DEBUG(SSSDBG_OP_FAILURE, "More than one result for a BASE search!\n"); + goto done; + } + + ret = sysdb_msg2attrs(tmp_ctx, count, msgs, &hosts); + talloc_free(msgs); + if (ret != EOK) { + goto done; + } + + host = hosts[0]; + + ret = EOK; + +done: + if (ret == EOK) { + *_user = talloc_steal(mem_ctx, user); + *_host = talloc_steal(mem_ctx, host); + } + + talloc_free(tmp_ctx); + + return ret; +} + +static errno_t +ipa_selinux_store_config(struct sysdb_ctx *sysdb, + struct sss_domain_info *ipa_domain, + const char *default_user, + const char *map_order, + size_t map_count, + struct sysdb_attrs **maps) +{ + bool in_transaction = false; + errno_t sret; + errno_t ret; + + ret = sysdb_transaction_start(sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n"); + goto done; + } + in_transaction = true; + + ret = sysdb_delete_usermaps(ipa_domain); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot delete existing maps from sysdb\n"); + goto done; + } + + ret = sysdb_store_selinux_config(ipa_domain, default_user, map_order); + if (ret != EOK) { + goto done; + } + + if (map_count > 0) { + ret = ipa_save_user_maps(sysdb, ipa_domain, map_count, maps); + if (ret != EOK) { + goto done; + } + } + + ret = sysdb_transaction_commit(sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not commit transaction\n"); + goto done; + } + in_transaction = false; + + ret = EOK; + +done: + if (in_transaction) { + sret = sysdb_transaction_cancel(sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n"); + } + } + + return ret; +} + +static errno_t +ipa_selinux_create_child_input(TALLOC_CTX *mem_ctx, + struct sysdb_attrs *user, + struct sysdb_attrs *host, + struct sysdb_attrs **maps, + size_t map_count, + struct sysdb_attrs **hbac_rules, + size_t hbac_count, + const char *map_order, + struct pam_data *pd, + struct sss_domain_info *user_domain, + const char *default_user, + struct selinux_child_input **_sci) { + struct sysdb_attrs **best_match_maps = NULL; + struct map_order_ctx *map_order_ctx = NULL; + struct selinux_child_input *sci = NULL; + errno_t ret; + + /* Process the maps and return list of best matches + * (maps with highest priority). */ + ret = ipa_selinux_process_maps(mem_ctx, user, host, maps, map_count, + hbac_rules, hbac_count, &best_match_maps); + if (ret != EOK) { + goto done; + } + + ret = init_map_order_ctx(mem_ctx, map_order, &map_order_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to create ordered SELinux users array.\n"); + goto done; + } + + ret = choose_best_seuser(mem_ctx, best_match_maps, pd, user_domain, + map_order_ctx, default_user, &sci); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to evaluate ordered SELinux users array.\n"); + goto done; + } + + *_sci = sci; + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(best_match_maps); + talloc_free(map_order_ctx); + talloc_free(sci); + } + + return ret; +} + +struct ipa_selinux_handler_state { + struct be_ctx *be_ctx; + struct tevent_context *ev; struct pam_data *pd; - pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); + struct sss_domain_info *user_domain; + struct sss_domain_info *ipa_domain; + struct ipa_selinux_ctx *selinux_ctx; + + struct sysdb_attrs *user; + struct sysdb_attrs *host; +}; - pd->pam_status = PAM_SUCCESS; - be_req_terminate(be_req, DP_ERR_OK, EOK, "Success"); +static void ipa_selinux_handler_get_done(struct tevent_req *subreq); +static void ipa_selinux_handler_done(struct tevent_req *subreq); + +struct tevent_req * +ipa_selinux_handler_send(TALLOC_CTX *mem_ctx, + struct ipa_selinux_ctx *selinux_ctx, + struct pam_data *pd, + struct dp_req_params *params) +{ + struct ipa_selinux_handler_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + const char *hostname; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ipa_selinux_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->be_ctx = params->be_ctx; + state->ev = params->ev; + state->pd = pd; + state->user_domain = params->domain; + state->ipa_domain = params->be_ctx->domain; + state->selinux_ctx = selinux_ctx; + + pd->pam_status = PAM_SYSTEM_ERR; + + hostname = dp_opt_get_string(selinux_ctx->id_ctx->ipa_options->basic, + IPA_HOSTNAME); + if (hostname == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot determine this machine's host name\n"); + ret = EINVAL; + goto immediately; + } + + ret = ipa_selinux_init_attrs(state, state->user_domain->sysdb, + state->ipa_domain, state->user_domain, + pd->user, hostname, + &state->user, &state->host); + if (ret != EOK) { + goto immediately; + } + + subreq = ipa_get_selinux_send(state, params->be_ctx, state->user, + state->host, selinux_ctx); + if (subreq == NULL) { + goto immediately; + } + + tevent_req_set_callback(subreq, ipa_selinux_handler_get_done, req); + + return req; + +immediately: + /* 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_selinux_handler_get_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct ipa_selinux_handler_state *state; + struct selinux_child_input *sci; + struct sysdb_attrs **hbac_rules = NULL; + struct sysdb_attrs **maps = NULL; + size_t map_count = 0; + size_t hbac_count = 0; + char *default_user = NULL; + char *map_order = NULL; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_selinux_handler_state); + + ret = ipa_get_selinux_recv(subreq, state, &map_count, &maps, + &hbac_count, &hbac_rules, + &default_user, &map_order); + talloc_free(subreq); + if (ret != EOK) { + goto done; + } + + ret = ipa_selinux_store_config(state->ipa_domain->sysdb, state->ipa_domain, + default_user, map_order, map_count, maps); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to store SELinux config [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = ipa_selinux_create_child_input(state, state->user, state->host, + maps, map_count, hbac_rules, + hbac_count, map_order, state->pd, + state->user_domain, default_user, + &sci); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create child input [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + /* Update the SELinux context in a privileged child as the back end is + * running unprivileged + */ + subreq = selinux_child_send(state, state->ev, sci); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + tevent_req_set_callback(subreq, ipa_selinux_handler_done, req); + return; + +done: + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); +} + +static void ipa_selinux_handler_done(struct tevent_req *subreq) +{ + struct ipa_selinux_handler_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_selinux_handler_state); + + ret = selinux_child_recv(subreq); + talloc_free(subreq); + if (ret != EOK) { + state->pd->pam_status = PAM_SYSTEM_ERR; + goto done; + } + + if (!be_is_offline(state->be_ctx)) { + state->selinux_ctx->last_update = time(NULL); + } + + state->pd->pam_status = PAM_SUCCESS; + +done: + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); +} + +errno_t +ipa_selinux_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data) +{ + struct ipa_selinux_handler_state *state = NULL; + + state = tevent_req_data(req, struct ipa_selinux_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_data = talloc_steal(mem_ctx, state->pd); + + return EOK; } -#endif diff --git a/src/providers/ipa/ipa_selinux.h b/src/providers/ipa/ipa_selinux.h index 08cdaef7e..65ca037ee 100644 --- a/src/providers/ipa/ipa_selinux.h +++ b/src/providers/ipa/ipa_selinux.h @@ -43,6 +43,15 @@ struct ipa_selinux_ctx { struct sdap_search_base **hbac_search_bases; }; -void ipa_selinux_handler(struct be_req *be_req); +struct tevent_req * +ipa_selinux_handler_send(TALLOC_CTX *mem_ctx, + struct ipa_selinux_ctx *selinux_ctx, + struct pam_data *pd, + struct dp_req_params *params); + +errno_t +ipa_selinux_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data); #endif diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c index bfe770be6..cb443db9c 100644 --- a/src/providers/ipa/ipa_subdomains.c +++ b/src/providers/ipa/ipa_subdomains.c @@ -24,6 +24,7 @@ #include "providers/ldap/sdap_async.h" #include "providers/ldap/sdap_idmap.h" +#include "providers/ldap/sdap_ops.h" #include "providers/ipa/ipa_subdomains.h" #include "providers/ipa/ipa_common.h" #include "providers/ipa/ipa_id.h" @@ -53,23 +54,9 @@ #define IPA_SUBDOMAIN_DISABLED_PERIOD 3600 -enum ipa_subdomains_req_type { - IPA_SUBDOMAINS_MASTER, - IPA_SUBDOMAINS_SLAVE, - IPA_SUBDOMAINS_RANGES, - - IPA_SUBDOMAINS_MAX /* Counter */ -}; - -struct ipa_subdomains_req_params { - const char *filter; - tevent_req_fn cb; - const char *attrs[9]; -}; - struct ipa_subdomains_ctx { struct be_ctx *be_ctx; - struct ipa_id_ctx *id_ctx; + struct ipa_id_ctx *ipa_id_ctx; struct sdap_id_ctx *sdap_id_ctx; struct sdap_search_base **search_bases; struct sdap_search_base **master_search_bases; @@ -77,34 +64,9 @@ struct ipa_subdomains_ctx { struct sdap_search_base **host_search_bases; time_t last_refreshed; - struct tevent_timer *timer_event; - bool configured_explicit; - time_t disabled_until; bool view_read_at_init; }; -static void ipa_subdomains_done(struct ipa_subdomains_ctx *sd_ctx, - struct be_req *req, int dp_err, - int error, const char *errstr) -{ - sd_ctx->view_read_at_init = true; - return be_req_terminate(req, dp_err, error, errstr); -} - -struct be_ctx *ipa_get_subdomains_be_ctx(struct be_ctx *be_ctx) -{ - struct ipa_subdomains_ctx *subdom_ctx; - - subdom_ctx = talloc_get_type(be_ctx->bet_info[BET_SUBDOMAINS].pvt_bet_data, - struct ipa_subdomains_ctx); - if (subdom_ctx == NULL) { - DEBUG(SSSDBG_TRACE_ALL, "Subdomains are not configured.\n"); - return NULL; - } - - return subdom_ctx->be_ctx; -} - static errno_t ipa_subdom_reinit(struct ipa_subdomains_ctx *ctx) { @@ -114,7 +76,7 @@ ipa_subdom_reinit(struct ipa_subdomains_ctx *ctx) "Re-initializing domain %s\n", ctx->be_ctx->domain->name); ret = sss_write_krb5_conf_snippet( - dp_opt_get_string(ctx->id_ctx->ipa_options->basic, + dp_opt_get_string(ctx->ipa_id_ctx->ipa_options->basic, IPA_KRB5_CONFD_PATH)); if (ret != EOK) { DEBUG(SSSDBG_MINOR_FAILURE, "sss_write_krb5_conf_snippet failed.\n"); @@ -535,10 +497,10 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx, } /* Remove the AD ID ctx from the list of LDAP domains */ - ipa_ad_subdom_remove(ctx->be_ctx, ctx->id_ctx, dom); + ipa_ad_subdom_remove(ctx->be_ctx, ctx->ipa_id_ctx, dom); } else { /* ok let's try to update it */ - ipa_subdom_store_step(parent, ctx->id_ctx, + ipa_subdom_store_step(parent, ctx->ipa_id_ctx, ctx->sdap_id_ctx->opts->idmap_ctx, reply[c]); handled[c] = true; @@ -560,7 +522,7 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx, continue; } - ipa_subdom_store_step(parent, ctx->id_ctx, + ipa_subdom_store_step(parent, ctx->ipa_id_ctx, ctx->sdap_id_ctx->opts->idmap_ctx, reply[c]); } @@ -576,948 +538,1101 @@ done: return ret; } -struct ipa_subdomains_req_ctx { - struct be_req *be_req; - struct ipa_subdomains_ctx *sd_ctx; - struct sdap_id_op *sdap_op; - - char *current_filter; +static errno_t ipa_apply_view(struct sss_domain_info *domain, + struct ipa_id_ctx *ipa_id_ctx, + const char *view_name, + bool read_at_init) +{ + const char *current = ipa_id_ctx->view_name; + struct sysdb_ctx *sysdb = domain->sysdb; + bool in_transaction = false; + errno_t sret; + errno_t ret; - struct sdap_search_base **search_bases; - int search_base_iter; + DEBUG(SSSDBG_TRACE_ALL, "read_at_init [%s] current view [%s]\n", + read_at_init ? "true" : "false", ipa_id_ctx->view_name); - size_t reply_count; - struct sysdb_attrs **reply; -}; + if (current != NULL && strcmp(current, view_name) != 0 && read_at_init) { + DEBUG(SSSDBG_CRIT_FAILURE, "View name changed, this is not supported " + "at runtime. Please restart SSSD to get the new view applied.\n"); + return EOK; + } -static void ipa_subdomains_get_conn_done(struct tevent_req *req); -static errno_t -ipa_subdomains_handler_get_start(struct ipa_subdomains_req_ctx *ctx, - struct sdap_search_base **search_bases, - enum ipa_subdomains_req_type type); -static errno_t -ipa_subdomains_handler_get_cont(struct ipa_subdomains_req_ctx *ctx, - enum ipa_subdomains_req_type type); -static void ipa_subdomains_handler_done(struct tevent_req *req); -static void ipa_subdomains_handler_master_done(struct tevent_req *req); -static void ipa_subdomains_handler_ranges_done(struct tevent_req *req); - -static struct ipa_subdomains_req_params subdomain_requests[] = { - { MASTER_DOMAIN_FILTER, - ipa_subdomains_handler_master_done, - { IPA_CN, IPA_FLATNAME, IPA_SID, NULL } - }, - { SUBDOMAINS_FILTER, - ipa_subdomains_handler_done, - { IPA_CN, IPA_FLATNAME, IPA_TRUSTED_DOMAIN_SID, - IPA_TRUST_DIRECTION, NULL } - }, - { RANGE_FILTER, - ipa_subdomains_handler_ranges_done, - { OBJECTCLASS, IPA_CN, - IPA_BASE_ID, IPA_BASE_RID, IPA_SECONDARY_BASE_RID, - IPA_ID_RANGE_SIZE, IPA_TRUSTED_DOMAIN_SID, IPA_RANGE_TYPE, NULL - } + if (current != NULL && strcmp(current, view_name) == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "View name did not change.\n"); + return EOK; } -}; -static void ipa_subdomains_retrieve(struct ipa_subdomains_ctx *ctx, struct be_req *be_req) -{ - struct ipa_subdomains_req_ctx *req_ctx = NULL; - struct tevent_req *req; - int dp_error = DP_ERR_FATAL; - int ret; + DEBUG(SSSDBG_TRACE_FUNC, "View name changed to [%s].\n", view_name); - req_ctx = talloc(be_req, struct ipa_subdomains_req_ctx); - if (req_ctx == NULL) { - ret = ENOMEM; - goto done; - } + /* View name changed. If there was a non-default non-local view + * was used the tree in cache containing the override values is + * removed. In all cases sysdb_invalidate_overrides() is called to + * remove the override attribute from the cached user objects. + * + * Typically ctx->sd_ctx->id_ctx->view_name == NULL means that the + * cache was empty but there was a bug in with caused that the + * view name was not written to the cache at all. In this case the + * cache must be invalidated if the new view is not the + * default-view as well. */ + + if (current != NULL || !is_default_view(view_name)) { + ret = sysdb_transaction_start(sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to start transaction " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } - req_ctx->be_req = be_req; - req_ctx->sd_ctx = ctx; - req_ctx->search_base_iter = 0; - req_ctx->search_bases = ctx->ranges_search_bases; - req_ctx->current_filter = NULL; - req_ctx->reply_count = 0; - req_ctx->reply = NULL; + in_transaction = true; - req_ctx->sdap_op = sdap_id_op_create(req_ctx, - ctx->sdap_id_ctx->conn->conn_cache); - if (req_ctx->sdap_op == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed.\n"); - ret = ENOMEM; + if (!is_default_view(current) && !is_local_view(current)) { + /* Old view was not the default view, delete view tree */ + ret = sysdb_delete_view_tree(sysdb, current); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to delete old view tree " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + } + + ret = sysdb_invalidate_overrides(sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, " Unable to invalidate overrides " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + ret = sysdb_transaction_commit(sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to commint transaction " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + in_transaction = false; + } + + ret = sysdb_update_view_name(sysdb, view_name); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot update view name " + "[%d]: %s\n", ret, sss_strerror(ret)); goto done; } - req = sdap_id_op_connect_send(req_ctx->sdap_op, req_ctx, &ret); - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed: %d(%s).\n", - ret, strerror(ret)); + talloc_free(ipa_id_ctx->view_name); + ipa_id_ctx->view_name = talloc_strdup(ipa_id_ctx, view_name); + if (ipa_id_ctx->view_name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot copy view name.\n"); + ret = ENOMEM; goto done; } - tevent_req_set_callback(req, ipa_subdomains_get_conn_done, req_ctx); + if (!read_at_init) { + /* refresh view data of all domains at startup */ + ret = sysdb_master_domain_update(domain); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_master_domain_update failed " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } - return; + ret = sysdb_update_subdomains(domain); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_subdomains failed " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + } done: - talloc_free(req_ctx); - if (ret == EOK) { - dp_error = DP_ERR_OK; + if (in_transaction) { + sret = sysdb_transaction_cancel(sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n"); + } } - ipa_subdomains_done(ctx, be_req, dp_error, ret, NULL); + + return ret; } -static void ipa_subdomains_get_conn_done(struct tevent_req *req) -{ - int ret; - int dp_error = DP_ERR_FATAL; - struct ipa_subdomains_req_ctx *ctx; +struct ipa_subdomains_ranges_state { + struct sss_domain_info *domain; +}; - ctx = tevent_req_callback_data(req, struct ipa_subdomains_req_ctx); +static void ipa_subdomains_ranges_done(struct tevent_req *subreq); - ret = sdap_id_op_connect_recv(req, &dp_error); - talloc_zfree(req); - if (ret) { - if (dp_error == DP_ERR_OFFLINE) { - DEBUG(SSSDBG_MINOR_FAILURE, - "No IPA server is available, cannot get the " - "subdomain list while offline\n"); - } else { - DEBUG(SSSDBG_OP_FAILURE, - "Failed to connect to IPA server: [%d](%s)\n", - ret, strerror(ret)); - } +static struct tevent_req * +ipa_subdomains_ranges_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ipa_subdomains_ctx *sd_ctx, + struct sdap_handle *sh) +{ + struct ipa_subdomains_ranges_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + const char *attrs[] = { OBJECTCLASS, IPA_CN, + IPA_BASE_ID, IPA_BASE_RID, IPA_SECONDARY_BASE_RID, + IPA_ID_RANGE_SIZE, IPA_TRUSTED_DOMAIN_SID, + IPA_RANGE_TYPE, NULL }; - goto fail; + req = tevent_req_create(mem_ctx, &state, + struct ipa_subdomains_ranges_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } - ret = ipa_subdomains_handler_get_start(ctx, - ctx->sd_ctx->ranges_search_bases, - IPA_SUBDOMAINS_RANGES); - if (ret != EOK && ret != EAGAIN) { - goto fail; + if (sd_ctx->ranges_search_bases == NULL) { + DEBUG(SSSDBG_TRACE_FUNC, "No search base is set\n"); + ret = EOK; + goto immediately; } - return; + state->domain = sd_ctx->be_ctx->domain; + + subreq = sdap_search_bases_send(state, ev, sd_ctx->sdap_id_ctx->opts, sh, + sd_ctx->ranges_search_bases, NULL, false, + 0, RANGE_FILTER, attrs); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } + + tevent_req_set_callback(subreq, ipa_subdomains_ranges_done, req); + + return req; + +immediately: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); -fail: - ipa_subdomains_done(ctx->sd_ctx, ctx->be_req, dp_error, ret, NULL); + return req; } -static errno_t -ipa_subdomains_handler_get(struct ipa_subdomains_req_ctx *ctx, - enum ipa_subdomains_req_type type) +static void ipa_subdomains_ranges_done(struct tevent_req *subreq) { + struct ipa_subdomains_ranges_state *state; struct tevent_req *req; - struct sdap_search_base *base; - struct ipa_subdomains_req_params *params; - - if (type >= IPA_SUBDOMAINS_MAX) { - return EINVAL; - } + struct range_info **range_list; + struct sysdb_attrs **reply; + size_t reply_count; + errno_t ret; - params = &subdomain_requests[type]; + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_subdomains_ranges_state); - base = ctx->search_bases[ctx->search_base_iter]; - if (base == NULL) { - return EOK; + ret = sdap_search_bases_recv(subreq, state, &reply_count, &reply); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get data from LDAP [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; } - talloc_free(ctx->current_filter); - ctx->current_filter = sdap_combine_filters(ctx, params->filter, - base->filter); - if (ctx->current_filter == NULL) { - return ENOMEM; + ret = ipa_ranges_parse_results(state, state->domain->name, + reply_count, reply, &range_list); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to parse range resulg [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; } - req = sdap_get_generic_send(ctx, ctx->sd_ctx->be_ctx->ev, - ctx->sd_ctx->sdap_id_ctx->opts, - sdap_id_op_handle(ctx->sdap_op), - base->basedn, base->scope, - ctx->current_filter, params->attrs, NULL, 0, - dp_opt_get_int(ctx->sd_ctx->sdap_id_ctx->opts->basic, - SDAP_SEARCH_TIMEOUT), false); - - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n"); - return ENOMEM; + ret = sysdb_update_ranges(state->domain->sysdb, range_list); + talloc_free(range_list); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to update ranges [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; } - tevent_req_set_callback(req, params->cb, ctx); +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } - return EAGAIN; + tevent_req_done(req); } -static errno_t -ipa_subdomains_handler_get_start(struct ipa_subdomains_req_ctx *ctx, - struct sdap_search_base **search_bases, - enum ipa_subdomains_req_type type) +static errno_t ipa_subdomains_ranges_recv(struct tevent_req *req) { - ctx->search_base_iter = 0; - ctx->search_bases = search_bases; - return ipa_subdomains_handler_get(ctx, type); -} + TEVENT_REQ_RETURN_ON_ERROR(req); -static errno_t -ipa_subdomains_handler_get_cont(struct ipa_subdomains_req_ctx *ctx, - enum ipa_subdomains_req_type type) -{ - ctx->search_base_iter++; - return ipa_subdomains_handler_get(ctx, type); + return EOK; } -static void ipa_get_view_name_done(struct tevent_req *req); -static void ipa_server_create_trusts_done(struct tevent_req *trust_req); -static errno_t ipa_check_master(struct ipa_subdomains_req_ctx *ctx); +struct ipa_subdomains_master_state { + struct sss_domain_info *domain; + struct ipa_options *ipa_options; +}; + +static void ipa_subdomains_master_done(struct tevent_req *subreq); -static errno_t ipa_get_view_name(struct ipa_subdomains_req_ctx *ctx) +static struct tevent_req * +ipa_subdomains_master_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ipa_subdomains_ctx *sd_ctx, + struct sdap_handle *sh) { + struct ipa_subdomains_master_state *state; + struct sss_domain_info *domain; + struct tevent_req *subreq; struct tevent_req *req; - struct sdap_search_base *base; - const char *attrs[] = {IPA_CN, OBJECTCLASS, NULL}; - struct sdap_attr_map_info *maps; + errno_t ret; + const char *attrs[] = { IPA_CN, IPA_FLATNAME, IPA_SID, NULL }; - maps = talloc_zero(ctx, struct sdap_attr_map_info); - if (maps == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n"); - return ENOMEM; + req = tevent_req_create(mem_ctx, &state, + struct ipa_subdomains_master_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } - maps->map = ctx->sd_ctx->id_ctx->ipa_options->view_map; - maps->num_attrs = IPA_OPTS_VIEW; - base = ctx->search_bases[ctx->search_base_iter]; - if (base == NULL) { - return EOK; + if (sd_ctx->master_search_bases == NULL) { + DEBUG(SSSDBG_TRACE_FUNC, "No search base is set\n"); + ret = EOK; + goto immediately; } - /* We add SDAP_DEREF_FLG_SILENT because old IPA servers don't have - * the attribute we dereference, causing the deref call to fail - */ - req = sdap_deref_search_with_filter_send(ctx, ctx->sd_ctx->be_ctx->ev, - ctx->sd_ctx->sdap_id_ctx->opts, - sdap_id_op_handle(ctx->sdap_op), - base->basedn, - ctx->current_filter, IPA_ASSIGNED_ID_VIEW, attrs, - 1, maps, - dp_opt_get_int(ctx->sd_ctx->sdap_id_ctx->opts->basic, - SDAP_SEARCH_TIMEOUT), - SDAP_DEREF_FLG_SILENT); + state->domain = domain = sd_ctx->be_ctx->domain; + state->ipa_options = sd_ctx->ipa_id_ctx->ipa_options; - if (req == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n"); - return ENOMEM; + ret = sysdb_master_domain_update(domain); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to update master domain [%d]: %s\n", + ret, sss_strerror(ret)); + goto immediately; } - tevent_req_set_callback(req, ipa_get_view_name_done, ctx); + if (domain->flat_name != NULL && domain->domain_id != NULL + && domain->realm != NULL) { + DEBUG(SSSDBG_TRACE_FUNC, "Master record is up to date.\n"); + ret = EOK; + goto immediately; + } - return EAGAIN; + subreq = sdap_search_bases_return_first_send(state, ev, + sd_ctx->sdap_id_ctx->opts, sh, + sd_ctx->master_search_bases, NULL, false, + 0, MASTER_DOMAIN_FILTER, attrs); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } + + tevent_req_set_callback(subreq, ipa_subdomains_master_done, req); + + return req; + +immediately: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); + + return req; } -static void ipa_get_view_name_done(struct tevent_req *req) +static void ipa_subdomains_master_done(struct tevent_req *subreq) { - int ret; - int sret; - struct ipa_subdomains_req_ctx *ctx; + struct ipa_subdomains_master_state *state; + struct tevent_req *req; + struct sysdb_attrs **reply; size_t reply_count; - struct sdap_deref_attrs **reply = NULL; - const char *view_name; - int dp_error = DP_ERR_FATAL; + const char *flat = NULL; + const char *id = NULL; + const char *realm = NULL; + errno_t ret; - ctx = tevent_req_callback_data(req, struct ipa_subdomains_req_ctx); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_subdomains_master_state); - ret = sdap_deref_search_with_filter_recv(req, ctx, &reply_count, &reply); - talloc_zfree(req); + ret = sdap_search_bases_return_first_recv(subreq, state, + &reply_count, &reply); + talloc_zfree(subreq); if (ret != EOK) { - /* Depending on the version 389ds return a different error code if the - * search for the view name failed because our dereference attribute - * ipaAssignedIDView is not known. Newer version return - * LDAP_UNAVAILABLE_CRITICAL_EXTENSION(12) which is translated to - * EOPNOTSUPP and older versions return LDAP_PROTOCOL_ERROR(2) which - * is returned as EIO. In both cases we have to assume that the server - * is not view aware and keep the view name unset. */ - if (ret == EOPNOTSUPP || ret == EIO) { - DEBUG(SSSDBG_TRACE_FUNC, "get_view_name request failed, looks " \ - "like server does not support views.\n"); - ret = EOK; - } else { - DEBUG(SSSDBG_OP_FAILURE, "get_view_name request failed.\n"); - } + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get data from LDAP [%d]: %s\n", + ret, sss_strerror(ret)); goto done; } - if (reply_count == 0) { - ctx->search_base_iter++; - ret = ipa_get_view_name(ctx); - if (ret == EAGAIN) { - return; - } else if (ret == EOK) { - DEBUG(SSSDBG_TRACE_FUNC, "No view found, using default.\n"); - view_name = SYSDB_DEFAULT_VIEW_NAME; - } else { + if (reply_count > 0) { + ret = sysdb_attrs_get_string(reply[0], IPA_FLATNAME, &flat); + if (ret != EOK) { goto done; } - } else if (reply_count > 1) { - DEBUG(SSSDBG_CRIT_FAILURE, - "get_view_name request returned more than one object.\n"); - ret = EINVAL; - goto done; - } else { - ret = sysdb_attrs_get_string(reply[0]->attrs, SYSDB_VIEW_NAME, - &view_name); + + ret = sysdb_attrs_get_string(reply[0], IPA_SID, &id); if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); goto done; } + } else { + /* All search paths are searched and no master domain record was + * found. + * + * A default IPA installation will not have a master domain record, + * this is only created by ipa-adtrust-install. Nevertheless we should + * continue to read other data like the idview on IPA clients. */ + DEBUG(SSSDBG_TRACE_INTERNAL, "Master domain record not found!\n"); } - DEBUG(SSSDBG_TRACE_FUNC, "Found view name [%s].\n", view_name); - if (is_default_view(view_name)) { - DEBUG(SSSDBG_TRACE_ALL, - "Found IPA default view name, replacing with sysdb default.\n"); - view_name = SYSDB_DEFAULT_VIEW_NAME; + realm = dp_opt_get_string(state->ipa_options->basic, IPA_KRB5_REALM); + if (realm == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "No Kerberos realm for IPA?\n"); + ret = EINVAL; + goto done; } - DEBUG(SSSDBG_TRACE_ALL, "read_at_init [%s] current view [%s].\n", - ctx->sd_ctx->view_read_at_init ? "true" : "false", - ctx->sd_ctx->id_ctx->view_name); - - if (ctx->sd_ctx->id_ctx->view_name != NULL - && strcmp(ctx->sd_ctx->id_ctx->view_name, view_name) != 0 - && ctx->sd_ctx->view_read_at_init) { - DEBUG(SSSDBG_CRIT_FAILURE, - "View name changed, this is not supported at runtime. " \ - "Please restart SSSD to get the new view applied.\n"); - } else { - if (ctx->sd_ctx->id_ctx->view_name == NULL - || strcmp(ctx->sd_ctx->id_ctx->view_name, view_name) != 0) { - /* View name changed. If there was a non-default non-local view - * was used the tree in cache containing the override values is - * removed. In all cases sysdb_invalidate_overrides() is called to - * remove the override attribute from the cached user objects. - * - * Typically ctx->sd_ctx->id_ctx->view_name == NULL means that the - * cache was empty but there was a bug in with caused that the - * view name was not written to the cache at all. In this case the - * cache must be invalidated if the new view is not the - * default-view as well. */ - - if (ctx->sd_ctx->id_ctx->view_name != NULL - || !is_default_view(view_name)) { - ret = sysdb_transaction_start( - ctx->sd_ctx->be_ctx->domain->sysdb); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - "sysdb_transaction_start failed.\n"); - goto done; - } - - if (!is_default_view(ctx->sd_ctx->id_ctx->view_name) - && !is_local_view(ctx->sd_ctx->id_ctx->view_name)) { - /* Old view was not the default view, delete view tree */ - ret = sysdb_delete_view_tree( - ctx->sd_ctx->be_ctx->domain->sysdb, - ctx->sd_ctx->id_ctx->view_name); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - "sysdb_delete_view_tree failed.\n"); - sret = sysdb_transaction_cancel( - ctx->sd_ctx->be_ctx->domain->sysdb); - if (sret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - "sysdb_transaction_cancel failed.\n"); - goto done; - } - goto done; - } - } - - ret = sysdb_invalidate_overrides( - ctx->sd_ctx->be_ctx->domain->sysdb); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - "sysdb_invalidate_overrides failed.\n"); - sret = sysdb_transaction_cancel( - ctx->sd_ctx->be_ctx->domain->sysdb); - if (sret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - "sysdb_transaction_cancel failed.\n"); - goto done; - } - goto done; - } - - ret = sysdb_transaction_commit( - ctx->sd_ctx->be_ctx->domain->sysdb); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - "sysdb_transaction_commit failed.\n"); - goto done; - } - - /* TODO: start referesh task */ - } - - ret = sysdb_update_view_name(ctx->sd_ctx->be_ctx->domain->sysdb, - view_name); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Cannot add/update view name to sysdb.\n"); - } else { - talloc_free(ctx->sd_ctx->id_ctx->view_name); - ctx->sd_ctx->id_ctx->view_name = talloc_strdup( - ctx->sd_ctx->id_ctx, - view_name); - if (ctx->sd_ctx->id_ctx->view_name == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Cannot copy view name.\n"); - } - } - } - - if (!ctx->sd_ctx->view_read_at_init) { - /* refresh view data of all domains at startup */ - ret = sysdb_master_domain_update(ctx->sd_ctx->be_ctx->domain); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - "sysdb_master_domain_update failed.\n"); - goto done; - } - - ret = sysdb_update_subdomains(ctx->sd_ctx->be_ctx->domain); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_subdomains failed.\n"); - goto done; - } - } - - ctx->sd_ctx->view_read_at_init = true; - + ret = sysdb_master_domain_add_info(state->domain, realm, flat, id, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add master domain info " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; } ret = EOK; + done: - if (ret == EOK) { - dp_error = DP_ERR_OK; + if (ret != EOK) { + tevent_req_error(req, ret); + return; } - ipa_subdomains_done(ctx->sd_ctx, ctx->be_req, dp_error, ret, NULL); + + tevent_req_done(req); } -static void ipa_subdomains_handler_done(struct tevent_req *req) +static errno_t ipa_subdomains_master_recv(struct tevent_req *req) { - int ret; - size_t reply_count; - struct sysdb_attrs **reply = NULL; - struct ipa_subdomains_req_ctx *ctx; - struct sss_domain_info *domain; - bool refresh_has_changes = false; - int dp_error = DP_ERR_FATAL; - struct tevent_req *trust_req; + TEVENT_REQ_RETURN_ON_ERROR(req); - ctx = tevent_req_callback_data(req, struct ipa_subdomains_req_ctx); - domain = ctx->sd_ctx->be_ctx->domain; + return EOK; +} - ret = sdap_get_generic_recv(req, ctx, &reply_count, &reply); - talloc_zfree(req); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send request failed.\n"); - goto done; - } +struct ipa_subdomains_slave_state { + struct ipa_subdomains_ctx *sd_ctx; + struct be_ctx *be_ctx; + struct ipa_id_ctx *ipa_id_ctx; +}; - if (reply_count) { - ctx->reply = talloc_realloc(ctx, ctx->reply, struct sysdb_attrs *, - ctx->reply_count + reply_count); - if (ctx->reply == NULL) { - ret = ENOMEM; - goto done; - } - memcpy(ctx->reply+ctx->reply_count, reply, - reply_count * sizeof(struct sysdb_attrs *)); - ctx->reply_count += reply_count; +static void ipa_subdomains_slave_search_done(struct tevent_req *subreq); +static void ipa_subdomains_slave_trusts_done(struct tevent_req *subreq); + +static struct tevent_req * +ipa_subdomains_slave_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ipa_subdomains_ctx *sd_ctx, + struct sdap_handle *sh) +{ + struct ipa_subdomains_slave_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + const char *attrs[] = { IPA_CN, IPA_FLATNAME, IPA_TRUSTED_DOMAIN_SID, + IPA_TRUST_DIRECTION, NULL }; + + req = tevent_req_create(mem_ctx, &state, + struct ipa_subdomains_slave_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } - ret = ipa_subdomains_handler_get_cont(ctx, IPA_SUBDOMAINS_SLAVE); - if (ret == EAGAIN) { - return; - } else if (ret != EOK) { - goto done; + if (sd_ctx->search_bases == NULL) { + DEBUG(SSSDBG_TRACE_FUNC, "No search base is set\n"); + ret = EOK; + goto immediately; } - ret = ipa_subdomains_refresh(ctx->sd_ctx, ctx->reply_count, ctx->reply, - &refresh_has_changes); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Failed to refresh subdomains.\n"); - goto done; + state->sd_ctx = sd_ctx; + state->be_ctx = sd_ctx->be_ctx; + state->ipa_id_ctx = sd_ctx->ipa_id_ctx; + + subreq = sdap_search_bases_send(state, ev, sd_ctx->sdap_id_ctx->opts, sh, + sd_ctx->search_bases, NULL, false, + 0, SUBDOMAINS_FILTER, attrs); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; } - if (refresh_has_changes) { - ret = ipa_subdom_reinit(ctx->sd_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Could not reinitialize subdomains\n"); - goto done; - } + tevent_req_set_callback(subreq, ipa_subdomains_slave_search_done, req); - if (ctx->sd_ctx->id_ctx->server_mode != NULL) { - trust_req = ipa_server_create_trusts_send(ctx, ctx->sd_ctx->be_ctx->ev, - ctx->sd_ctx->be_ctx, - ctx->sd_ctx->id_ctx, - domain); - if (trust_req == NULL) { - ret = ENOMEM; - goto done; - } - tevent_req_set_callback(trust_req, ipa_server_create_trusts_done, ctx); - return; - } + return req; + +immediately: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); } + tevent_req_post(req, ev); - ctx->search_base_iter = 0; - ctx->search_bases = ctx->sd_ctx->host_search_bases; - talloc_zfree(ctx->current_filter); - ctx->current_filter = talloc_asprintf(ctx, "(&(objectClass=%s)(%s=%s))", - ctx->sd_ctx->id_ctx->ipa_options->host_map[IPA_OC_HOST].name, - ctx->sd_ctx->id_ctx->ipa_options->host_map[IPA_AT_HOST_FQDN].name, - dp_opt_get_string(ctx->sd_ctx->id_ctx->ipa_options->basic, - IPA_HOSTNAME)); - if (ctx->current_filter == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); - ret = ENOMEM; + return req; +} + +static void ipa_subdomains_slave_search_done(struct tevent_req *subreq) +{ + struct ipa_subdomains_slave_state *state; + struct tevent_req *req; + struct sysdb_attrs **reply; + size_t reply_count; + bool has_changes; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_subdomains_slave_state); + + ret = sdap_search_bases_recv(subreq, state, &reply_count, &reply); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get data from LDAP [%d]: %s\n", + ret, sss_strerror(ret)); goto done; } - if (ctx->sd_ctx->id_ctx->server_mode == NULL) { - /* Only get view on clients, on servers it is always 'default' */ - ret = ipa_get_view_name(ctx); - if (ret == EAGAIN) { - return; - } else if (ret != EOK) { - goto done; - } + ret = ipa_subdomains_refresh(state->sd_ctx, reply_count, reply, + &has_changes); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to refresh subdomains.\n"); + goto done; } - ret = EOK; -done: - if (ret == EOK) { - dp_error = DP_ERR_OK; + if (!has_changes) { + ret = EOK; + goto done; } - ipa_subdomains_done(ctx->sd_ctx, ctx->be_req, dp_error, ret, NULL); -} -static void ipa_server_create_trusts_done(struct tevent_req *trust_req) -{ - errno_t ret; - int dp_error = DP_ERR_FATAL; - struct ipa_subdomains_req_ctx *ctx; + ret = ipa_subdom_reinit(state->sd_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not reinitialize subdomains\n"); + goto done; + } - ctx = tevent_req_callback_data(trust_req, struct ipa_subdomains_req_ctx); + if (state->sd_ctx->ipa_id_ctx->server_mode == NULL) { + ret = EOK; + goto done; + } - ret = ipa_server_create_trusts_recv(trust_req); - talloc_zfree(trust_req); - if (ret == EOK) { - dp_error = DP_ERR_OK; + subreq = ipa_server_create_trusts_send(state, state->be_ctx->ev, + state->be_ctx, state->ipa_id_ctx, + state->be_ctx->domain); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + tevent_req_set_callback(subreq, ipa_subdomains_slave_trusts_done, req); + return; + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; } - ipa_subdomains_done(ctx->sd_ctx, ctx->be_req, dp_error, ret, NULL); + tevent_req_done(req); } -static errno_t ipa_check_master(struct ipa_subdomains_req_ctx *ctx) +static void ipa_subdomains_slave_trusts_done(struct tevent_req *subreq) { - int ret; - struct sss_domain_info *domain; + struct tevent_req *req; + errno_t ret; - domain = ctx->sd_ctx->be_ctx->domain; + req = tevent_req_callback_data(subreq, struct tevent_req); - ret = sysdb_master_domain_update(domain); + ret = ipa_server_create_trusts_recv(subreq); + talloc_zfree(subreq); if (ret != EOK) { - return ret; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create trusts [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; } - if (domain->flat_name == NULL || - domain->domain_id == NULL || - domain->realm == NULL) { + tevent_req_done(req); +} - ret = ipa_subdomains_handler_get_start(ctx, - ctx->sd_ctx->master_search_bases, - IPA_SUBDOMAINS_MASTER); - if (ret == EAGAIN) { - return EAGAIN; - } else if (ret != EOK) { - return ret; - } - } +static errno_t ipa_subdomains_slave_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); return EOK; } +struct ipa_subdomains_view_name_state { + struct ipa_subdomains_ctx *sd_ctx; +}; + +static void ipa_subdomains_view_name_done(struct tevent_req *subreq); -static void ipa_subdomains_handler_ranges_done(struct tevent_req *req) +static struct tevent_req * +ipa_subdomains_view_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ipa_subdomains_ctx *sd_ctx, + struct sdap_handle *sh) { + struct ipa_subdomains_view_name_state *state; + struct sdap_attr_map_info *maps; + struct tevent_req *subreq; + struct tevent_req *req; + struct ipa_options *ipa_options; + const char *filter; + const char *attrs[] = {IPA_CN, OBJECTCLASS, NULL}; errno_t ret; - int dp_error = DP_ERR_FATAL; - size_t reply_count; - struct sysdb_attrs **reply = NULL; - struct ipa_subdomains_req_ctx *ctx; - struct range_info **range_list = NULL; - struct sysdb_ctx *sysdb; - struct sss_domain_info *domain; - - ctx = tevent_req_callback_data(req, struct ipa_subdomains_req_ctx); - domain = ctx->sd_ctx->be_ctx->domain; - sysdb = domain->sysdb; - ret = sdap_get_generic_recv(req, ctx, &reply_count, &reply); - talloc_zfree(req); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send request failed.\n"); - goto done; + req = tevent_req_create(mem_ctx, &state, + struct ipa_subdomains_view_name_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } - ret = ipa_ranges_parse_results(ctx, domain->name, - reply_count, reply, &range_list); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - "ipa_ranges_parse_results request failed.\n"); - goto done; + if (sd_ctx->ipa_id_ctx->server_mode != NULL) { + /* Only get view on clients, on servers it is always 'default'. */ + ret = EOK; + goto immediately; } - ret = sysdb_update_ranges(sysdb, range_list); - talloc_free(range_list); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_ranges failed.\n"); - goto done; + state->sd_ctx = sd_ctx; + + ipa_options = sd_ctx->ipa_id_ctx->ipa_options; + + maps = talloc_zero_array(state, struct sdap_attr_map_info, 2); + if (maps == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_zero() failed\n"); + ret = ENOMEM; + goto immediately; } + maps[0].map = ipa_options->view_map; + maps->num_attrs = IPA_OPTS_VIEW; - ret = ipa_check_master(ctx); - if (ret == EAGAIN) { - DEBUG(SSSDBG_TRACE_ALL, "Checking master record..\n"); - return; - } else if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "ipa_check_master failed.\n"); - goto done; + filter = talloc_asprintf(state, "(&(objectClass=%s)(%s=%s))", + ipa_options->host_map[IPA_OC_HOST].name, + ipa_options->host_map[IPA_AT_HOST_FQDN].name, + dp_opt_get_string(ipa_options->basic, IPA_HOSTNAME)); + if (filter == NULL) { + ret = ENOMEM; + goto immediately; } - /* Master domain is up-to-date. Continue checking subdomains */ - DEBUG(SSSDBG_TRACE_ALL, "Master record up2date, checking subdomains\n"); - ret = ipa_subdomains_handler_get_start(ctx, ctx->sd_ctx->search_bases, - IPA_SUBDOMAINS_SLAVE); - if (ret == EAGAIN) { - return; - } else if (ret != EOK) { - goto done; + /* We add SDAP_DEREF_FLG_SILENT because old IPA servers don't have + * the attribute we dereference, causing the deref call to fail. */ + subreq = sdap_deref_bases_return_first_send(state, ev, + sd_ctx->sdap_id_ctx->opts, sh, sd_ctx->host_search_bases, + maps, filter, attrs, IPA_ASSIGNED_ID_VIEW, + SDAP_DEREF_FLG_SILENT, 0); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; } - DEBUG(SSSDBG_OP_FAILURE, "No search base for ranges available.\n"); - ret = EINVAL; + tevent_req_set_callback(subreq, ipa_subdomains_view_name_done, req); -done: + return req; + +immediately: if (ret == EOK) { - dp_error = DP_ERR_OK; + tevent_req_done(req); + } else { + tevent_req_error(req, ret); } - ipa_subdomains_done(ctx->sd_ctx, ctx->be_req, dp_error, ret, NULL); + tevent_req_post(req, ev); + + return req; } -static void ipa_subdomains_handler_master_done(struct tevent_req *req) +static void ipa_subdomains_view_name_done(struct tevent_req *subreq) { + struct ipa_subdomains_view_name_state *state; + struct tevent_req *req; + size_t reply_count; + struct sdap_deref_attrs **reply; + const char *view_name; errno_t ret; - int dp_error = DP_ERR_FATAL; - size_t reply_count = 0; - struct sysdb_attrs **reply = NULL; - struct ipa_subdomains_req_ctx *ctx; - const char *flat = NULL; - const char *id = NULL; - const char *realm = NULL; - ctx = tevent_req_callback_data(req, struct ipa_subdomains_req_ctx); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_subdomains_view_name_state); - ret = sdap_get_generic_recv(req, ctx, &reply_count, &reply); - talloc_zfree(req); + ret = sdap_deref_bases_return_first_recv(subreq, state, + &reply_count, &reply); + talloc_zfree(subreq); if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send request failed.\n"); - goto done; - } - - if (reply_count) { - ret = sysdb_attrs_get_string(reply[0], IPA_FLATNAME, &flat); - if (ret != EOK) { + /* Depending on the version 389ds return a different error code if the + * search for the view name failed because our dereference attribute + * ipaAssignedIDView is not known. Newer version return + * LDAP_UNAVAILABLE_CRITICAL_EXTENSION(12) which is translated to + * EOPNOTSUPP and older versions return LDAP_PROTOCOL_ERROR(2) which + * is returned as EIO. In both cases we have to assume that the server + * is not view aware and keep the view name unset. */ + if (ret == EOPNOTSUPP || ret == EIO) { + DEBUG(SSSDBG_TRACE_FUNC, "Unable to get view name, looks " \ + "like server does not support views.\n"); + ret = EOK; goto done; } - ret = sysdb_attrs_get_string(reply[0], IPA_SID, &id); + DEBUG(SSSDBG_OP_FAILURE, "Unable to get view name [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + if (reply_count == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "No view found, using default.\n"); + view_name = SYSDB_DEFAULT_VIEW_NAME; + } else if (reply_count == 1) { + ret = sysdb_attrs_get_string(reply[0]->attrs, SYSDB_VIEW_NAME, + &view_name); if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); goto done; } - - /* There is only one master record. Don't bother checking other IPA - * search bases; move to checking subdomains instead - */ } else { - ret = ipa_subdomains_handler_get_cont(ctx, IPA_SUBDOMAINS_MASTER); - if (ret == EAGAIN) { - return; - } else if (ret != EOK) { - goto done; - } - - /* All search paths are searched and no master domain record was - * found. - * - * A default IPA installation will not have a master domain record, - * this is only created by ipa-adtrust-install. Nevertheless we should - * continue to read other data like the idview on IPA clients. */ - - DEBUG(SSSDBG_TRACE_INTERNAL, "Master domain record not found!\n"); - - } - - realm = dp_opt_get_string(ctx->sd_ctx->id_ctx->ipa_options->basic, - IPA_KRB5_REALM); - if (realm == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "No Kerberos realm for IPA?\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "More than one object returned.\n"); ret = EINVAL; goto done; } - ret = sysdb_master_domain_add_info(ctx->sd_ctx->be_ctx->domain, - realm, flat, id, NULL); + ret = ipa_apply_view(state->sd_ctx->be_ctx->domain, + state->sd_ctx->ipa_id_ctx, view_name, + state->sd_ctx->view_read_at_init); if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set view [%d]: %s\n", + ret, sss_strerror(ret)); goto done; } - ret = ipa_subdomains_handler_get_start(ctx, - ctx->sd_ctx->search_bases, - IPA_SUBDOMAINS_SLAVE); - if (ret == EAGAIN) { - return; - } else if (ret == EOK) { - /* If there are no search bases defined for subdomains try to get the - * idview before ending the request */ - if (ctx->sd_ctx->id_ctx->server_mode == NULL) { - /* Only get view on clients, on servers it is always 'default' */ - ret = ipa_get_view_name(ctx); - if (ret == EAGAIN) { - return; - } else if (ret != EOK) { - goto done; - } - } - } + state->sd_ctx->view_read_at_init = true; done: - if (ret == EOK) { - dp_error = DP_ERR_OK; + if (ret != EOK) { + tevent_req_error(req, ret); + return; } - ipa_subdomains_done(ctx->sd_ctx, ctx->be_req, dp_error, ret, NULL); -} -static void ipa_subdom_online_cb(void *pvt); + tevent_req_done(req); +} -static void ipa_subdom_timer_refresh(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval current_time, - void *pvt) +static errno_t ipa_subdomains_view_name_recv(struct tevent_req *req) { - ipa_subdom_online_cb(pvt); + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; } -static void ipa_subdom_be_req_callback(struct be_req *be_req, - int dp_err, int dp_ret, - const char *errstr) + +struct ipa_subdomains_refresh_state { + struct tevent_context *ev; + struct ipa_subdomains_ctx *sd_ctx; + struct sdap_id_op *sdap_op; +}; + +static errno_t ipa_subdomains_refresh_retry(struct tevent_req *req); +static void ipa_subdomains_refresh_connect_done(struct tevent_req *subreq); +static void ipa_subdomains_refresh_ranges_done(struct tevent_req *subreq); +static void ipa_subdomains_refresh_master_done(struct tevent_req *subreq); +static void ipa_subdomains_refresh_slave_done(struct tevent_req *subreq); +static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq); + +static struct tevent_req * +ipa_subdomains_refresh_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ipa_subdomains_ctx *sd_ctx) { - talloc_free(be_req); + struct ipa_subdomains_refresh_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ipa_subdomains_refresh_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->ev = ev; + state->sd_ctx = sd_ctx; + + state->sdap_op = sdap_id_op_create(state, + sd_ctx->sdap_id_ctx->conn->conn_cache); + if (state->sdap_op == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create() failed\n"); + ret = ENOMEM; + goto immediately; + } + + ret = ipa_subdomains_refresh_retry(req); + if (ret == EAGAIN) { + /* asynchronous processing */ + return req; + } + +immediately: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); + + return req; } -static void ipa_subdom_reset_timeouts_cb(void *pvt) +static errno_t ipa_subdomains_refresh_retry(struct tevent_req *req) { - struct ipa_subdomains_ctx *ctx; + struct ipa_subdomains_refresh_state *state; + struct tevent_req *subreq; + int ret; - ctx = talloc_get_type(pvt, struct ipa_subdomains_ctx); - if (ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Bad private pointer\n"); - return; + state = tevent_req_data(req, struct ipa_subdomains_refresh_state); + + subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_connect_send() failed " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; } - DEBUG(SSSDBG_TRACE_ALL, "Resetting last_refreshed and disabled_until.\n"); - ctx->last_refreshed = 0; - ctx->disabled_until = 0; + tevent_req_set_callback(subreq, ipa_subdomains_refresh_connect_done, req); + + return EAGAIN; } -static void ipa_subdom_online_cb(void *pvt) +static void ipa_subdomains_refresh_connect_done(struct tevent_req *subreq) { - struct ipa_subdomains_ctx *ctx; - struct be_req *be_req; - struct timeval tv; - uint32_t refresh_interval; - - ctx = talloc_get_type(pvt, struct ipa_subdomains_ctx); - if (!ctx) { - DEBUG(SSSDBG_CRIT_FAILURE, "Bad private pointer\n"); - return; - } + struct ipa_subdomains_refresh_state *state; + struct tevent_req *req; + int dp_error; + errno_t ret; - ctx->disabled_until = 0; + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_subdomains_refresh_state); - refresh_interval = ctx->be_ctx->domain->subdomain_refresh_interval; + ret = sdap_id_op_connect_recv(subreq, &dp_error); + talloc_zfree(subreq); - be_req = be_req_create(ctx, NULL, ctx->be_ctx, "IPA subdomains", - ipa_subdom_be_req_callback, NULL); - if (be_req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "be_req_create() failed.\n"); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to connect to LDAP " + "[%d]: %s\n", ret, sss_strerror(ret)); + if (dp_error == DP_ERR_OFFLINE) { + DEBUG(SSSDBG_MINOR_FAILURE, "No IPA server is available, " + "cannot get the subdomain list while offline\n"); + ret = ERR_OFFLINE; + } + tevent_req_error(req, ret); return; } - ipa_subdomains_retrieve(ctx, be_req); - - tv = tevent_timeval_current_ofs(refresh_interval, 0); - ctx->timer_event = tevent_add_timer(ctx->be_ctx->ev, ctx, tv, - ipa_subdom_timer_refresh, ctx); - if (!ctx->timer_event) { - DEBUG(SSSDBG_MINOR_FAILURE, "Failed to add subdom timer event\n"); + subreq = ipa_subdomains_ranges_send(state, state->ev, state->sd_ctx, + sdap_id_op_handle(state->sdap_op)); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; } + + tevent_req_set_callback(subreq, ipa_subdomains_refresh_ranges_done, req); + return; } -static void ipa_subdom_offline_cb(void *pvt) +static void ipa_subdomains_refresh_ranges_done(struct tevent_req *subreq) { - struct ipa_subdomains_ctx *ctx; + struct ipa_subdomains_refresh_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_subdomains_refresh_state); - ctx = talloc_get_type(pvt, struct ipa_subdomains_ctx); + ret = ipa_subdomains_ranges_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get IPA ranges " + "[%d]: %s\n", ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } - if (ctx) { - talloc_zfree(ctx->timer_event); + subreq = ipa_subdomains_master_send(state, state->ev, state->sd_ctx, + sdap_id_op_handle(state->sdap_op)); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; } + + tevent_req_set_callback(subreq, ipa_subdomains_refresh_master_done, req); + return; } -static errno_t get_config_status(struct be_ctx *be_ctx, - bool *configured_explicit) +static void ipa_subdomains_refresh_master_done(struct tevent_req *subreq) { - int ret; - TALLOC_CTX *tmp_ctx = NULL; - char *tmp_str; + struct ipa_subdomains_refresh_state *state; + struct tevent_req *req; + errno_t ret; - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); - return ENOMEM; - } + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_subdomains_refresh_state); - ret = confdb_get_string(be_ctx->cdb, tmp_ctx, be_ctx->conf_path, - CONFDB_DOMAIN_SUBDOMAINS_PROVIDER, NULL, - &tmp_str); + ret = ipa_subdomains_master_recv(subreq); + talloc_zfree(subreq); if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "confdb_get_string failed.\n"); - goto done; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get master domain " + "[%d]: %s\n", ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; } - if (tmp_str == NULL) { - *configured_explicit = false; - } else { - *configured_explicit = true; + subreq = ipa_subdomains_slave_send(state, state->ev, state->sd_ctx, + sdap_id_op_handle(state->sdap_op)); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; } - DEBUG(SSSDBG_TRACE_ALL, "IPA subdomain provider is configured %s.\n", - *configured_explicit ? "explicit" : "implicit"); + tevent_req_set_callback(subreq, ipa_subdomains_refresh_slave_done, req); + return; +} - ret = EOK; +static void ipa_subdomains_refresh_slave_done(struct tevent_req *subreq) +{ + struct ipa_subdomains_refresh_state *state; + struct tevent_req *req; + errno_t ret; -done: - talloc_free(tmp_ctx); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_subdomains_refresh_state); - return ret; -} + ret = ipa_subdomains_slave_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get subdomains " + "[%d]: %s\n", ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } -void ipa_subdomains_handler(struct be_req *be_req) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - struct ipa_subdomains_ctx *ctx; - time_t now; - - ctx = talloc_get_type(be_ctx->bet_info[BET_SUBDOMAINS].pvt_bet_data, - struct ipa_subdomains_ctx); - if (!ctx) { - be_req_terminate(be_req, DP_ERR_FATAL, EINVAL, NULL); + subreq = ipa_subdomains_view_name_send(state, state->ev, state->sd_ctx, + sdap_id_op_handle(state->sdap_op)); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); return; } - now = time(NULL); + tevent_req_set_callback(subreq, ipa_subdomains_refresh_view_done, req); + return; +} + +static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq) +{ + struct ipa_subdomains_refresh_state *state; + struct tevent_req *req; + int dp_error; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_subdomains_refresh_state); - if (ctx->disabled_until > now) { - DEBUG(SSSDBG_TRACE_ALL, "Subdomain provider disabled.\n"); - ipa_subdomains_done(ctx, be_req, DP_ERR_OK, EOK, NULL); + ret = ipa_subdomains_view_name_recv(subreq); + talloc_zfree(subreq); + ret = sdap_id_op_done(state->sdap_op, ret, &dp_error); + if (dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + ret = ipa_subdomains_refresh_retry(req); + if (ret != EOK) { + goto done; + } return; + } else if (dp_error == DP_ERR_OFFLINE) { + ret = ERR_OFFLINE; + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get view name " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; } - if (ctx->last_refreshed > now - IPA_SUBDOMAIN_REFRESH_LIMIT) { - ipa_subdomains_done(ctx, be_req, DP_ERR_OK, EOK, NULL); +done: + if (ret != EOK) { + DEBUG(SSSDBG_TRACE_FUNC, "Unable to refresh subdomains [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); return; } - ipa_subdomains_retrieve(ctx, be_req); + DEBUG(SSSDBG_TRACE_FUNC, "Subdomains refreshed.\n"); + tevent_req_done(req); +} + +static errno_t ipa_subdomains_refresh_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; } -struct bet_ops ipa_subdomains_ops = { - .handler = ipa_subdomains_handler, - .finalize = NULL +struct ipa_subdomains_handler_state { + struct dp_reply_std reply; }; -int ipa_subdom_init(struct be_ctx *be_ctx, - struct ipa_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data) +static void ipa_subdomains_handler_done(struct tevent_req *subreq); + +static struct tevent_req * +ipa_subdomains_handler_send(TALLOC_CTX *mem_ctx, + struct ipa_subdomains_ctx *sd_ctx, + struct dp_subdomains_data *data, + struct dp_req_params *params) { - struct ipa_subdomains_ctx *ctx; - int ret; - bool configured_explicit = false; + struct ipa_subdomains_handler_state *state; + struct tevent_req *req; + struct tevent_req *subreq; + errno_t ret; - ret = get_config_status(be_ctx, &configured_explicit); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "get_config_status failed.\n"); - return ret; + req = tevent_req_create(mem_ctx, &state, + struct ipa_subdomains_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } - ctx = talloc_zero(id_ctx, struct ipa_subdomains_ctx); - if (ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); - return ENOMEM; + + if (sd_ctx->last_refreshed > time(NULL) - IPA_SUBDOMAIN_REFRESH_LIMIT) { + DEBUG(SSSDBG_TRACE_FUNC, "Subdomains were recently refreshed, " + "nothing to do\n"); + ret = EOK; + goto immediately; } - ctx->be_ctx = be_ctx; - ctx->id_ctx = id_ctx; - ctx->sdap_id_ctx = id_ctx->sdap_id_ctx; - ctx->search_bases = id_ctx->ipa_options->subdomains_search_bases; - ctx->master_search_bases = id_ctx->ipa_options->master_domain_search_bases; - ctx->ranges_search_bases = id_ctx->ipa_options->ranges_search_bases; - ctx->host_search_bases = id_ctx->ipa_options->host_search_bases; - ctx->configured_explicit = configured_explicit; - ctx->disabled_until = 0; - *ops = &ipa_subdomains_ops; - *pvt_data = ctx; - - ret = be_add_unconditional_online_cb(ctx, be_ctx, - ipa_subdom_reset_timeouts_cb, ctx, - NULL); - if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, - "Failed to add subdom reset timeouts callback\n"); + subreq = ipa_subdomains_refresh_send(state, params->ev, sd_ctx); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; } - ret = be_add_online_cb(ctx, be_ctx, ipa_subdom_online_cb, ctx, NULL); + tevent_req_set_callback(subreq, ipa_subdomains_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_subdomains_handler_done(struct tevent_req *subreq) +{ + struct ipa_subdomains_handler_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_subdomains_handler_state); + + ret = ipa_subdomains_refresh_recv(subreq); + talloc_zfree(subreq); if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, "Failed to add subdom online callback\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh subdomains [%d]: %s\n", + ret, sss_strerror(ret)); } - ret = be_add_offline_cb(ctx, be_ctx, ipa_subdom_offline_cb, ctx, NULL); + /* TODO For backward compatibility we always return EOK to DP now. */ + dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ret, NULL); + tevent_req_done(req); +} + +static errno_t ipa_subdomains_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data) +{ + struct ipa_subdomains_handler_state *state; + + state = tevent_req_data(req, struct ipa_subdomains_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *data = state->reply; + + return EOK; +} + +static struct tevent_req * +ipa_subdomains_ptask_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct be_ptask *be_ptask, + void *pvt) +{ + struct ipa_subdomains_ctx *sd_ctx; + sd_ctx = talloc_get_type(pvt, struct ipa_subdomains_ctx); + + return ipa_subdomains_refresh_send(mem_ctx, ev, sd_ctx); +} + +static errno_t +ipa_subdomains_ptask_recv(struct tevent_req *req) +{ + return ipa_subdomains_refresh_recv(req); +} + +errno_t ipa_subdomains_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_id_ctx *ipa_id_ctx, + struct dp_method *dp_methods) +{ + struct ipa_subdomains_ctx *sd_ctx; + struct ipa_options *ipa_options; + time_t period; + errno_t ret; + + ipa_options = ipa_id_ctx->ipa_options; + + sd_ctx = talloc_zero(mem_ctx, struct ipa_subdomains_ctx); + if (sd_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); + return ENOMEM; + } + + sd_ctx->be_ctx = be_ctx; + sd_ctx->ipa_id_ctx = ipa_id_ctx; + sd_ctx->sdap_id_ctx = ipa_id_ctx->sdap_id_ctx; + sd_ctx->search_bases = ipa_options->subdomains_search_bases; + sd_ctx->master_search_bases = ipa_options->master_domain_search_bases; + sd_ctx->ranges_search_bases = ipa_options->ranges_search_bases; + sd_ctx->host_search_bases = ipa_options->host_search_bases; + + dp_set_method(dp_methods, DPM_DOMAINS_HANDLER, + ipa_subdomains_handler_send, ipa_subdomains_handler_recv, sd_ctx, + struct ipa_subdomains_ctx, struct dp_subdomains_data, struct dp_reply_std); + + period = be_ctx->domain->subdomain_refresh_interval; + ret = be_ptask_create(sd_ctx, be_ctx, period, 0, 0, 0, period, + BE_PTASK_OFFLINE_DISABLE, 0, + ipa_subdomains_ptask_send, ipa_subdomains_ptask_recv, sd_ctx, + "Subdomains Refresh", NULL); if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, "Failed to add subdom offline callback\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup ptask " + "[%d]: %s\n", ret, sss_strerror(ret)); + /* Ignore, responders will trigger refresh from time to time. */ } - ret = ipa_subdom_reinit(ctx); + ret = ipa_subdom_reinit(sd_ctx); if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, "Could not load the list of subdomains. " + DEBUG(SSSDBG_MINOR_FAILURE, "Could not reinitialize subdomains. " "Users from trusted domains might not be resolved correctly\n"); + /* Ignore this error and try to discover the subdomains later */ } - ret = ipa_ad_subdom_init(be_ctx, id_ctx); + ret = ipa_ad_subdom_init(be_ctx, ipa_id_ctx); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "ipa_ad_subdom_init failed.\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "ipa_ad_subdom_init() failed.\n"); return ret; } diff --git a/src/providers/ipa/ipa_subdomains.h b/src/providers/ipa/ipa_subdomains.h index 270867fb0..8e66ca977 100644 --- a/src/providers/ipa/ipa_subdomains.h +++ b/src/providers/ipa/ipa_subdomains.h @@ -37,12 +37,10 @@ #define EXOP_SID2NAME_OID "2.16.840.1.113730.3.8.10.4" #define EXOP_SID2NAME_V1_OID "2.16.840.1.113730.3.8.10.4.1" -struct be_ctx *ipa_get_subdomains_be_ctx(struct be_ctx *be_ctx); - -int ipa_subdom_init(struct be_ctx *be_ctx, - struct ipa_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data); +errno_t ipa_subdomains_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_id_ctx *ipa_id_ctx, + struct dp_method *dp_methods); /* The following are used in server mode only */ struct ipa_ad_server_ctx { diff --git a/src/providers/ipa/ipa_subdomains_ext_groups.c b/src/providers/ipa/ipa_subdomains_ext_groups.c index a8ba4dfe8..93ef1f4a1 100644 --- a/src/providers/ipa/ipa_subdomains_ext_groups.c +++ b/src/providers/ipa/ipa_subdomains_ext_groups.c @@ -1108,8 +1108,9 @@ struct tevent_req *ipa_ext_group_member_send(TALLOC_CTX *mem_ctx, goto immediate; } - subreq = be_get_account_info_send(state, ev, NULL, - ipa_ctx->sdap_id_ctx->be, ar); + subreq = dp_req_send(state, ipa_ctx->sdap_id_ctx->be->provider, NULL, + ar->domain, "External Member", + DPT_ID, DPM_ACCOUNT_HANDLER, 0, ar, NULL); if (subreq == NULL) { ret = ENOMEM; goto immediate; @@ -1135,20 +1136,23 @@ static void ipa_ext_group_member_done(struct tevent_req *subreq) struct ipa_ext_member_state *state = tevent_req_data(req, struct ipa_ext_member_state); errno_t ret; - int err_maj; - int err_min; - const char *err_msg; struct ldb_message *msg; struct sysdb_attrs **members; + struct dp_reply_std *reply; - ret = be_get_account_info_recv(subreq, state, - &err_maj, &err_min, &err_msg); + + ret = dp_req_recv_ptr(state, subreq, struct dp_reply_std, &reply); talloc_free(subreq); if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - "be request failed %d:%d: %s\n", err_maj, err_min, err_msg); + DEBUG(SSSDBG_OP_FAILURE, "dp_req_recv failed\n"); tevent_req_error(req, ret); return; + } else if (reply->dp_error != DP_ERR_OK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot refresh data from DP: %u,%u: %s\n", + reply->dp_error, reply->error, reply->message); + tevent_req_error(req, EIO); + return; } ret = search_user_or_group_by_sid_str(state, diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c index 665ff635b..f06eff7a7 100644 --- a/src/providers/ipa/ipa_subdomains_id.c +++ b/src/providers/ipa/ipa_subdomains_id.c @@ -40,7 +40,6 @@ static struct tevent_req * ipa_srv_ad_acct_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ipa_id_ctx *ipa_ctx, - struct be_req *be_req, struct sysdb_attrs *override_attrs, struct be_acct_req *ar); static errno_t @@ -53,7 +52,6 @@ struct ipa_subdomain_account_state { struct sdap_id_op *op; struct sysdb_ctx *sysdb; struct sss_domain_info *domain; - struct be_req *be_req; struct be_acct_req *ar; bool ipa_server_mode; @@ -75,7 +73,6 @@ static errno_t ipa_subdomain_account_get_original_step(struct tevent_req *req, struct tevent_req *ipa_subdomain_account_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct ipa_id_ctx *ipa_ctx, - struct be_req *be_req, struct be_acct_req *ar) { struct tevent_req *req; @@ -109,7 +106,6 @@ struct tevent_req *ipa_subdomain_account_send(TALLOC_CTX *memctx, goto fail; } state->sysdb = state->domain->sysdb; - state->be_req = be_req; state->ar = ar; state->ipa_server_mode = dp_opt_get_bool(state->ipa_ctx->ipa_options->basic, IPA_SERVER_MODE); @@ -277,7 +273,7 @@ static errno_t ipa_subdomain_account_get_original_step(struct tevent_req *req, if (state->ipa_server_mode) { subreq = ipa_srv_ad_acct_send(state, state->ev, state->ipa_ctx, - state->be_req, state->override_attrs, ar); + state->override_attrs, ar); } else { subreq = ipa_get_subdom_acct_send(state, state->ev, state->ipa_ctx, state->override_attrs, ar); @@ -402,7 +398,6 @@ struct tevent_req *ipa_get_subdom_acct_send(TALLOC_CTX *memctx, case BE_REQ_USER: case BE_REQ_GROUP: case BE_REQ_BY_SECID: - case BE_REQ_BY_CERT: case BE_REQ_USER_AND_GROUP: ret = EOK; break; @@ -625,7 +620,6 @@ struct ipa_get_ad_acct_state { int dp_error; struct tevent_context *ev; struct ipa_id_ctx *ipa_ctx; - struct be_req *be_req; struct be_acct_req *ar; struct sss_domain_info *obj_dom; char *object_sid; @@ -646,7 +640,6 @@ static struct tevent_req * ipa_get_ad_acct_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ipa_id_ctx *ipa_ctx, - struct be_req *be_req, struct sysdb_attrs *override_attrs, struct be_acct_req *ar) { @@ -665,7 +658,6 @@ ipa_get_ad_acct_send(TALLOC_CTX *mem_ctx, state->dp_error = -1; state->ev = ev; state->ipa_ctx = ipa_ctx; - state->be_req = be_req; state->ar = ar; state->obj_msg = NULL; state->override_attrs = override_attrs; @@ -715,7 +707,7 @@ ipa_get_ad_acct_send(TALLOC_CTX *mem_ctx, goto fail; } - subreq = ad_handle_acct_info_send(req, be_req, ar, sdap_id_ctx, + subreq = ad_handle_acct_info_send(req, ar, sdap_id_ctx, ad_id_ctx->ad_options, sdom, clist); if (subreq == NULL) { ret = ENOMEM; @@ -1404,7 +1396,6 @@ ipa_get_ad_acct_recv(struct tevent_req *req, int *dp_error_out) struct ipa_srv_ad_acct_state { struct tevent_context *ev; struct ipa_id_ctx *ipa_ctx; - struct be_req *be_req; struct sysdb_attrs *override_attrs; struct be_acct_req *ar; @@ -1423,7 +1414,6 @@ static struct tevent_req * ipa_srv_ad_acct_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct ipa_id_ctx *ipa_ctx, - struct be_req *be_req, struct sysdb_attrs *override_attrs, struct be_acct_req *ar) { @@ -1438,12 +1428,11 @@ ipa_srv_ad_acct_send(TALLOC_CTX *mem_ctx, state->ev = ev; state->ipa_ctx = ipa_ctx; - state->be_req = be_req; state->override_attrs = override_attrs; state->ar = ar; state->retry = true; state->dp_error = DP_ERR_FATAL; - state->be_ctx = be_req_get_be_ctx(state->be_req); + state->be_ctx = ipa_ctx->sdap_id_ctx->be; state->obj_dom = find_domain_by_name( state->ipa_ctx->sdap_id_ctx->be->domain, @@ -1475,7 +1464,7 @@ static int ipa_srv_ad_acct_lookup_step(struct tevent_req *req) DEBUG(SSSDBG_TRACE_FUNC, "Looking up AD account\n"); subreq = ipa_get_ad_acct_send(state, state->ev, state->ipa_ctx, - state->be_req, state->override_attrs, + state->override_attrs, state->ar); if (subreq == NULL) { return ENOMEM; diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c index b870d5552..43636098f 100644 --- a/src/providers/ipa/ipa_subdomains_server.c +++ b/src/providers/ipa/ipa_subdomains_server.c @@ -961,7 +961,7 @@ void ipa_ad_subdom_remove(struct be_ctx *be_ctx, DLIST_REMOVE(id_ctx->server_mode->trusts, iter); /* terminate all requests for this subdomain so we can free it */ - be_terminate_domain_requests(be_ctx, subdom->name); + dp_terminate_domain_requests(be_ctx->provider, subdom->name); talloc_zfree(sdom); } diff --git a/src/providers/ipa/ipa_sudo.c b/src/providers/ipa/ipa_sudo.c index b4633858f..c93d8ca29 100644 --- a/src/providers/ipa/ipa_sudo.c +++ b/src/providers/ipa/ipa_sudo.c @@ -24,13 +24,119 @@ #include "providers/ipa/ipa_sudo.h" #include "db/sysdb_sudo.h" -static void ipa_sudo_handler(struct be_req *breq); - -struct bet_ops ipa_sudo_ops = { - .handler = ipa_sudo_handler, - .finalize = NULL, +struct ipa_sudo_handler_state { + uint32_t type; + struct dp_reply_std reply; }; +static void ipa_sudo_handler_done(struct tevent_req *subreq); + +static struct tevent_req * +ipa_sudo_handler_send(TALLOC_CTX *mem_ctx, + struct ipa_sudo_ctx *sudo_ctx, + struct dp_sudo_data *data, + struct dp_req_params *params) +{ + struct ipa_sudo_handler_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ipa_sudo_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->type = data->type; + + switch (data->type) { + case BE_REQ_SUDO_FULL: + DEBUG(SSSDBG_TRACE_FUNC, "Issuing a full refresh of sudo rules\n"); + subreq = ipa_sudo_full_refresh_send(state, params->ev, sudo_ctx); + break; + case BE_REQ_SUDO_RULES: + DEBUG(SSSDBG_TRACE_FUNC, "Issuing a refresh of specific sudo rules\n"); + subreq = ipa_sudo_rules_refresh_send(state, params->ev, sudo_ctx, + data->rules); + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type: %d\n", data->type); + ret = EINVAL; + goto immediately; + } + + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send request: %d\n", data->type); + ret = ENOMEM; + goto immediately; + } + + tevent_req_set_callback(subreq, ipa_sudo_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_sudo_handler_done(struct tevent_req *subreq) +{ + struct ipa_sudo_handler_state *state; + struct tevent_req *req; + int dp_error; + bool deleted; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_sudo_handler_state); + + switch (state->type) { + case BE_REQ_SUDO_FULL: + ret = ipa_sudo_full_refresh_recv(subreq, &dp_error); + talloc_zfree(subreq); + break; + case BE_REQ_SUDO_RULES: + ret = ipa_sudo_rules_refresh_recv(subreq, &dp_error, &deleted); + talloc_zfree(subreq); + if (ret == EOK && deleted == true) { + ret = ENOENT; + } + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type: %d\n", state->type); + dp_error = DP_ERR_FATAL; + ret = ERR_INTERNAL; + break; + } + + /* 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); +} + +static errno_t +ipa_sudo_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data) +{ + struct ipa_sudo_handler_state *state = NULL; + + state = tevent_req_data(req, struct ipa_sudo_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *data = state->reply; + + return EOK; +} + enum sudo_schema { SUDO_SCHEMA_IPA, SUDO_SCHEMA_LDAP @@ -95,10 +201,10 @@ done: } static int -ipa_sudo_init_ipa_schema(struct be_ctx *be_ctx, +ipa_sudo_init_ipa_schema(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, struct ipa_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data) + struct dp_method *dp_methods) { struct ipa_sudo_ctx *sudo_ctx; errno_t ret; @@ -154,8 +260,9 @@ ipa_sudo_init_ipa_schema(struct be_ctx *be_ctx, goto done; } - *ops = &ipa_sudo_ops; - *pvt_data = sudo_ctx; + dp_set_method(dp_methods, DPM_SUDO_HANDLER, + ipa_sudo_handler_send, ipa_sudo_handler_recv, sudo_ctx, + struct ipa_sudo_ctx, struct dp_sudo_data, struct dp_reply_std); ret = EOK; @@ -167,10 +274,10 @@ done: return ret; } -int ipa_sudo_init(struct be_ctx *be_ctx, +int ipa_sudo_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, struct ipa_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data) + struct dp_method *dp_methods) { enum sudo_schema schema; errno_t ret; @@ -189,11 +296,11 @@ int ipa_sudo_init(struct be_ctx *be_ctx, switch (schema) { case SUDO_SCHEMA_IPA: DEBUG(SSSDBG_TRACE_FUNC, "Using IPA schema for sudo\n"); - ret = ipa_sudo_init_ipa_schema(be_ctx, id_ctx, ops, pvt_data); + ret = ipa_sudo_init_ipa_schema(mem_ctx, be_ctx, id_ctx, dp_methods); break; case SUDO_SCHEMA_LDAP: DEBUG(SSSDBG_TRACE_FUNC, "Using LDAP schema for sudo\n"); - ret = sdap_sudo_init(be_ctx, id_ctx->sdap_id_ctx, ops, pvt_data); + ret = sdap_sudo_init(mem_ctx, be_ctx, id_ctx->sdap_id_ctx, dp_methods); break; } @@ -205,86 +312,3 @@ int ipa_sudo_init(struct be_ctx *be_ctx, return EOK; } - -static void -ipa_sudo_reply(struct tevent_req *req) -{ - struct be_sudo_req *sudo_req; - struct be_req *be_req; - bool deleted; - int dp_error; - int ret; - - be_req = tevent_req_callback_data(req, struct be_req); - sudo_req = talloc_get_type(be_req_get_data(be_req), struct be_sudo_req); - - switch (sudo_req->type) { - case BE_REQ_SUDO_FULL: - ret = ipa_sudo_full_refresh_recv(req, &dp_error); - break; - case BE_REQ_SUDO_RULES: - ret = ipa_sudo_rules_refresh_recv(req, &dp_error, &deleted); - if (ret == EOK && deleted == true) { - ret = ENOENT; - } - break; - default: - DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type: %d\n", - sudo_req->type); - dp_error = DP_ERR_FATAL; - ret = ERR_INTERNAL; - break; - } - - talloc_zfree(req); - sdap_handler_done(be_req, dp_error, ret, sss_strerror(ret)); -} - -static void -ipa_sudo_handler(struct be_req *be_req) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - struct ipa_sudo_ctx *sudo_ctx; - struct be_sudo_req *sudo_req; - struct tevent_req *req; - int ret; - - if (be_is_offline(be_ctx)) { - sdap_handler_done(be_req, DP_ERR_OFFLINE, EAGAIN, "Offline"); - return; - } - - sudo_ctx = talloc_get_type(be_ctx->bet_info[BET_SUDO].pvt_bet_data, - struct ipa_sudo_ctx); - - sudo_req = talloc_get_type(be_req_get_data(be_req), struct be_sudo_req); - - switch (sudo_req->type) { - case BE_REQ_SUDO_FULL: - req = ipa_sudo_full_refresh_send(be_req, be_ctx->ev, sudo_ctx); - break; - case BE_REQ_SUDO_RULES: - req = ipa_sudo_rules_refresh_send(be_req, be_ctx->ev, sudo_ctx, - sudo_req->rules); - break; - default: - DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type: %d\n", - sudo_req->type); - ret = EINVAL; - goto fail; - } - - if (req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send request: %d\n", - sudo_req->type); - ret = ENOMEM; - goto fail; - } - - tevent_req_set_callback(req, ipa_sudo_reply, be_req); - - return; - -fail: - sdap_handler_done(be_req, DP_ERR_FATAL, ret, NULL); -} diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c index 7ed7bd73d..dabf55cf2 100644 --- a/src/providers/krb5/krb5_auth.c +++ b/src/providers/krb5/krb5_auth.c @@ -137,34 +137,6 @@ static int krb5_delete_ccname(TALLOC_CTX *mem_ctx, SYSDB_MOD_DEL); } -static struct krb5_ctx *get_krb5_ctx(struct be_req *be_req) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - struct pam_data *pd; - - pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); - - switch (pd->cmd) { - case SSS_PAM_AUTHENTICATE: - case SSS_CMD_RENEW: - return talloc_get_type(be_ctx->bet_info[BET_AUTH].pvt_bet_data, - struct krb5_ctx); - break; - case SSS_PAM_ACCT_MGMT: - return talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data, - struct krb5_ctx); - break; - case SSS_PAM_CHAUTHTOK: - case SSS_PAM_CHAUTHTOK_PRELIM: - return talloc_get_type(be_ctx->bet_info[BET_CHPASS].pvt_bet_data, - struct krb5_ctx); - break; - default: - DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported PAM task.\n"); - return NULL; - } -} - static int krb5_cleanup(void *ptr) { struct krb5child_req *kr = talloc_get_type(ptr, struct krb5child_req); @@ -1165,115 +1137,137 @@ int krb5_auth_recv(struct tevent_req *req, int *pam_status, int *dp_err) return EOK; } -void krb5_pam_handler_auth_done(struct tevent_req *req); -static void krb5_pam_handler_access_done(struct tevent_req *req); +struct krb5_pam_handler_state { + struct pam_data *pd; +}; + +static void krb5_pam_handler_auth_done(struct tevent_req *subreq); +static void krb5_pam_handler_access_done(struct tevent_req *subreq); -void krb5_pam_handler(struct be_req *be_req) +struct tevent_req * +krb5_pam_handler_send(TALLOC_CTX *mem_ctx, + struct krb5_ctx *krb5_ctx, + struct pam_data *pd, + struct dp_req_params *params) { - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); + struct krb5_pam_handler_state *state; + struct tevent_req *subreq; struct tevent_req *req; - struct pam_data *pd; - struct krb5_ctx *krb5_ctx; - int dp_err = DP_ERR_FATAL; - - pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); - pd->pam_status = PAM_SYSTEM_ERR; - krb5_ctx = get_krb5_ctx(be_req); - if (krb5_ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Kerberos context not available.\n"); - goto done; + req = tevent_req_create(mem_ctx, &state, + struct krb5_pam_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } + state->pd = pd; + switch (pd->cmd) { case SSS_PAM_AUTHENTICATE: case SSS_CMD_RENEW: case SSS_PAM_CHAUTHTOK_PRELIM: case SSS_PAM_CHAUTHTOK: - req = krb5_auth_queue_send(be_req, be_ctx->ev, be_ctx, pd, krb5_ctx); - if (req == NULL) { + subreq = krb5_auth_queue_send(state, params->ev, params->be_ctx, + pd, krb5_ctx); + if (subreq == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "krb5_auth_send failed.\n"); - goto done; + pd->pam_status = PAM_SYSTEM_ERR; + goto immediately; } - tevent_req_set_callback(req, krb5_pam_handler_auth_done, be_req); + tevent_req_set_callback(subreq, krb5_pam_handler_auth_done, req); break; case SSS_PAM_ACCT_MGMT: - req = krb5_access_send(be_req, be_ctx->ev, be_ctx, pd, krb5_ctx); - if (req == NULL) { + subreq = krb5_access_send(state, params->ev, params->be_ctx, + pd, krb5_ctx); + if (subreq == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "krb5_access_send failed.\n"); - goto done; + pd->pam_status = PAM_SYSTEM_ERR; + goto immediately; } - tevent_req_set_callback(req, krb5_pam_handler_access_done, be_req); + tevent_req_set_callback(subreq, krb5_pam_handler_access_done, req); break; case SSS_PAM_SETCRED: case SSS_PAM_OPEN_SESSION: case SSS_PAM_CLOSE_SESSION: pd->pam_status = PAM_SUCCESS; - dp_err = DP_ERR_OK; - goto done; + goto immediately; break; default: DEBUG(SSSDBG_CONF_SETTINGS, "krb5 does not handles pam task %d.\n", pd->cmd); pd->pam_status = PAM_MODULE_UNKNOWN; - dp_err = DP_ERR_OK; - goto done; + goto immediately; } - return; + return req; -done: - be_req_terminate(be_req, dp_err, pd->pam_status, NULL); +immediately: + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); + tevent_req_post(req, params->ev); + + return req; } -void krb5_pam_handler_auth_done(struct tevent_req *req) +static void krb5_pam_handler_auth_done(struct tevent_req *subreq) { - int ret; - struct be_req *be_req = tevent_req_callback_data(req, struct be_req); - int pam_status; - int dp_err; - struct pam_data *pd; + struct krb5_pam_handler_state *state; + struct tevent_req *req; + errno_t ret; - pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct krb5_pam_handler_state); - ret = krb5_auth_queue_recv(req, &pam_status, &dp_err); - talloc_zfree(req); - if (ret) { - pd->pam_status = PAM_SYSTEM_ERR; - dp_err = DP_ERR_OK; - } else { - pd->pam_status = pam_status; + ret = krb5_auth_queue_recv(subreq, &state->pd->pam_status, NULL); + talloc_zfree(subreq); + if (ret != EOK) { + state->pd->pam_status = PAM_SYSTEM_ERR; } - be_req_terminate(be_req, dp_err, pd->pam_status, NULL); + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); } -static void krb5_pam_handler_access_done(struct tevent_req *req) +static void krb5_pam_handler_access_done(struct tevent_req *subreq) { - int ret; - struct be_req *be_req = tevent_req_callback_data(req, struct be_req); + struct krb5_pam_handler_state *state; + struct tevent_req *req; bool access_allowed; - struct pam_data *pd; - int dp_err = DP_ERR_OK; + errno_t ret; - pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); - pd->pam_status = PAM_SYSTEM_ERR; + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct krb5_pam_handler_state); - ret = krb5_access_recv(req, &access_allowed); - talloc_zfree(req); + ret = krb5_access_recv(subreq, &access_allowed); + talloc_zfree(subreq); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "krb5_access request failed [%d][%s]\n", ret, strerror(ret)); - goto done; + state->pd->pam_status = PAM_SYSTEM_ERR; } + DEBUG(SSSDBG_TRACE_LIBS, "Access %s for user [%s].\n", - access_allowed ? "allowed" : "denied", pd->user); - pd->pam_status = access_allowed ? PAM_SUCCESS : PAM_PERM_DENIED; - dp_err = DP_ERR_OK; + access_allowed ? "allowed" : "denied", state->pd->user); + state->pd->pam_status = access_allowed ? PAM_SUCCESS : PAM_PERM_DENIED; -done: - be_req_terminate(be_req, dp_err, pd->pam_status, NULL); + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); +} + +errno_t +krb5_pam_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data) +{ + struct krb5_pam_handler_state *state = NULL; + + state = tevent_req_data(req, struct krb5_pam_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_data = talloc_steal(mem_ctx, state->pd); + + return EOK; } diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h index 1822f2b98..dbad061f0 100644 --- a/src/providers/krb5/krb5_auth.h +++ b/src/providers/krb5/krb5_auth.h @@ -63,8 +63,16 @@ errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd, struct krb5_ctx *krb5_ctx, bool case_sensitive, struct krb5child_req **krb5_req); -void krb5_pam_handler(struct be_req *be_req); -void krb5_pam_handler_auth_done(struct tevent_req *req); +struct tevent_req * +krb5_pam_handler_send(TALLOC_CTX *mem_ctx, + struct krb5_ctx *krb5_ctx, + struct pam_data *pd, + struct dp_req_params *params); + +errno_t +krb5_pam_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data); /* Please use krb5_auth_send/recv *only* if you're certain there can't * be concurrent logins happening. With some ccache back ends, the ccache diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h index 367b56e08..3bd5ed1c8 100644 --- a/src/providers/krb5/krb5_common.h +++ b/src/providers/krb5/krb5_common.h @@ -190,10 +190,6 @@ errno_t krb5_get_simple_upn(TALLOC_CTX *mem_ctx, struct krb5_ctx *krb5_ctx, errno_t compare_principal_realm(const char *upn, const char *realm, bool *different_realm); -int sssm_krb5_auth_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_auth_data); - /* from krb5_keytab.c */ /** diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c index 4f36d905b..d356491e5 100644 --- a/src/providers/krb5/krb5_init.c +++ b/src/providers/krb5/krb5_init.c @@ -30,171 +30,202 @@ #include "providers/krb5/krb5_auth.h" #include "providers/krb5/krb5_common.h" #include "providers/krb5/krb5_init_shared.h" +#include "providers/data_provider.h" -struct krb5_options { - struct dp_option *opts; - struct krb5_ctx *auth_ctx; -}; +static errno_t krb5_init_kpasswd(struct krb5_ctx *ctx, + struct be_ctx *be_ctx) +{ + const char *realm; + const char *primary_servers; + const char *backup_servers; + const char *kdc_servers; + bool use_kdcinfo; + errno_t ret; + + realm = dp_opt_get_string(ctx->opts, KRB5_REALM); + if (realm == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Missing krb5_realm option!\n"); + return EINVAL; + } -struct krb5_options *krb5_options = NULL; + kdc_servers = dp_opt_get_string(ctx->opts, KRB5_KDC); + primary_servers = dp_opt_get_string(ctx->opts, KRB5_KPASSWD); + backup_servers = dp_opt_get_string(ctx->opts, KRB5_BACKUP_KPASSWD); + use_kdcinfo = dp_opt_get_bool(ctx->opts, KRB5_USE_KDCINFO); -struct bet_ops krb5_auth_ops = { - .handler = krb5_pam_handler, - .finalize = NULL, -}; + if (primary_servers == NULL && backup_servers != NULL) { + DEBUG(SSSDBG_CONF_SETTINGS, "kpasswd server wasn't specified but " + "backup_servers kpasswd given. Using it as primary_servers\n"); + primary_servers = backup_servers; + backup_servers = NULL; + } -int krb5_ctx_re_destructor(void *memctx) + if (primary_servers == NULL && kdc_servers != NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Missing krb5_kpasswd option and KDC set " + "explicitly, will use KDC for pasword change operations!\n"); + ctx->kpasswd_service = NULL; + } else { + ret = krb5_service_init(ctx, be_ctx, SSS_KRB5KPASSWD_FO_SRV, + primary_servers, backup_servers, realm, + use_kdcinfo, &ctx->kpasswd_service); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to init KRB5KPASSWD failover service!\n"); + return ret; + } + } + + return EOK; +} + +static errno_t krb5_init_kdc(struct krb5_ctx *ctx, struct be_ctx *be_ctx) { - struct krb5_ctx *ctx = (struct krb5_ctx *) memctx; + const char *primary_servers; + const char *backup_servers; + const char *realm; + bool use_kdcinfo; + errno_t ret; + + realm = dp_opt_get_string(ctx->opts, KRB5_REALM); + if (realm == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Missing krb5_realm option!\n"); + return EINVAL; + } + + primary_servers = dp_opt_get_string(ctx->opts, KRB5_KDC); + backup_servers = dp_opt_get_string(ctx->opts, KRB5_BACKUP_KDC); + + use_kdcinfo = dp_opt_get_bool(ctx->opts, KRB5_USE_KDCINFO); + + ret = krb5_service_init(ctx, be_ctx, SSS_KRB5KDC_FO_SRV, + primary_servers, backup_servers, realm, + use_kdcinfo, &ctx->service); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init KRB5 failover service!\n"); + return ret; + } - if (ctx->illegal_path_re) { + return EOK; +} + +int krb5_ctx_re_destructor(struct krb5_ctx *ctx) +{ + if (ctx->illegal_path_re != NULL) { pcre_free(ctx->illegal_path_re); ctx->illegal_path_re = NULL; } + return 0; } -int sssm_krb5_auth_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_auth_data) +errno_t sssm_krb5_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct data_provider *provider, + const char *module_name, + void **_module_data) { - struct krb5_ctx *ctx = NULL; - int ret; - const char *krb5_servers; - const char *krb5_backup_servers; - const char *krb5_kpasswd_servers; - const char *krb5_backup_kpasswd_servers; - const char *krb5_realm; + struct krb5_ctx *ctx; const char *errstr; int errval; int errpos; + errno_t ret; - if (krb5_options == NULL) { - krb5_options = talloc_zero(bectx, struct krb5_options); - if (krb5_options == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); - return ENOMEM; - } - ret = krb5_get_options(krb5_options, bectx->cdb, bectx->conf_path, - &krb5_options->opts); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "krb5_get_options failed.\n"); - return ret; - } - } - - if (krb5_options->auth_ctx != NULL) { - *ops = &krb5_auth_ops; - *pvt_auth_data = krb5_options->auth_ctx; - return EOK; + ctx = talloc_zero(mem_ctx, struct krb5_ctx); + if (ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n"); + return ENOMEM; } - ctx = talloc_zero(bectx, struct krb5_ctx); - if (!ctx) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n"); - return ENOMEM; + ret = krb5_get_options(ctx, be_ctx->cdb, be_ctx->conf_path, &ctx->opts); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get krb5 options [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; } - krb5_options->auth_ctx = ctx; ctx->action = INIT_PW; - ctx->opts = krb5_options->opts; ctx->config_type = K5C_GENERIC; - krb5_servers = dp_opt_get_string(ctx->opts, KRB5_KDC); - krb5_backup_servers = dp_opt_get_string(ctx->opts, KRB5_BACKUP_KDC); - - krb5_realm = dp_opt_get_string(ctx->opts, KRB5_REALM); - if (krb5_realm == NULL) { - DEBUG(SSSDBG_FATAL_FAILURE, "Missing krb5_realm option!\n"); - return EINVAL; - } - - ret = krb5_service_init(ctx, bectx, - SSS_KRB5KDC_FO_SRV, krb5_servers, - krb5_backup_servers, krb5_realm, - dp_opt_get_bool(krb5_options->opts, - KRB5_USE_KDCINFO), - &ctx->service); + ret = krb5_init_kdc(ctx, be_ctx); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init KRB5 failover service!\n"); - return ret; - } - - krb5_kpasswd_servers = dp_opt_get_string(ctx->opts, KRB5_KPASSWD); - krb5_backup_kpasswd_servers = dp_opt_get_string(ctx->opts, - KRB5_BACKUP_KPASSWD); - if (krb5_kpasswd_servers == NULL && krb5_backup_kpasswd_servers != NULL) { - DEBUG(SSSDBG_CONF_SETTINGS, "kpasswd server wasn't specified but " - "backup kpasswd given. Using it as primary\n"); - krb5_kpasswd_servers = krb5_backup_kpasswd_servers; - krb5_backup_kpasswd_servers = NULL; + goto done; } - if (krb5_kpasswd_servers == NULL && krb5_servers != NULL) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Missing krb5_kpasswd option and KDC set explicitly, " - "will use KDC for pasword change operations!\n"); - ctx->kpasswd_service = NULL; - } else { - ret = krb5_service_init(ctx, bectx, - SSS_KRB5KPASSWD_FO_SRV, krb5_kpasswd_servers, - krb5_backup_kpasswd_servers, krb5_realm, - dp_opt_get_bool(krb5_options->opts, - KRB5_USE_KDCINFO), - &ctx->kpasswd_service); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to init KRB5KPASSWD failover service!\n"); - return ret; - } + ret = krb5_init_kpasswd(ctx, be_ctx); + if (ret != EOK) { + goto done; } - /* Initialize features needed by the krb5_child */ - ret = krb5_child_init(ctx, bectx); + ret = krb5_child_init(ctx, be_ctx); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Could not initialize krb5_child settings: [%s]\n", - strerror(ret)); - goto fail; + DEBUG(SSSDBG_FATAL_FAILURE, "Could not initialize krb5_child settings " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; } ctx->illegal_path_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0, &errval, &errstr, &errpos, NULL); if (ctx->illegal_path_re == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Invalid Regular Expression pattern at position %d. " - "(Error: %d [%s])\n", errpos, errval, errstr); + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid Regular Expression pattern " + "at position %d. (Error: %d [%s])\n", errpos, errval, errstr); ret = EFAULT; - goto fail; + goto done; } - talloc_set_destructor((TALLOC_CTX *) ctx, krb5_ctx_re_destructor); + talloc_set_destructor(ctx, krb5_ctx_re_destructor); - ret = be_fo_set_dns_srv_lookup_plugin(bectx, NULL); + ret = be_fo_set_dns_srv_lookup_plugin(be_ctx, NULL); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set SRV lookup plugin " - "[%d]: %s\n", ret, sss_strerror(ret)); - goto fail; + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; } - *ops = &krb5_auth_ops; - *pvt_auth_data = ctx; - return EOK; + *_module_data = ctx; + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(ctx); + } -fail: - talloc_zfree(krb5_options->auth_ctx); return ret; } -int sssm_krb5_chpass_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_auth_data) +errno_t sssm_krb5_auth_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - return sssm_krb5_auth_init(bectx, ops, pvt_auth_data); + struct krb5_ctx *ctx; + + ctx = talloc_get_type(module_data, struct krb5_ctx); + dp_set_method(dp_methods, DPM_AUTH_HANDLER, + krb5_pam_handler_send, krb5_pam_handler_recv, ctx, + struct krb5_ctx, struct pam_data, struct pam_data *); + + return EOK; } -int sssm_krb5_access_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_auth_data) +errno_t sssm_krb5_chpass_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - return sssm_krb5_auth_init(bectx, ops, pvt_auth_data); + return sssm_krb5_auth_init(mem_ctx, be_ctx, module_data, dp_methods); +} + +errno_t sssm_krb5_access_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) +{ + struct krb5_ctx *ctx; + + ctx = talloc_get_type(module_data, struct krb5_ctx); + dp_set_method(dp_methods, DPM_ACCESS_HANDLER, + krb5_pam_handler_send, krb5_pam_handler_recv, ctx, + struct krb5_ctx, struct pam_data, struct pam_data *); + + return EOK; } diff --git a/src/providers/ldap/ldap_access.c b/src/providers/ldap/ldap_access.c index eb60f720d..4ec4702f9 100644 --- a/src/providers/ldap/ldap_access.c +++ b/src/providers/ldap/ldap_access.c @@ -29,90 +29,100 @@ #include "src/providers/ldap/sdap_access.h" #include "providers/ldap/ldap_common.h" -static void sdap_access_reply(struct be_req *be_req, int pam_status) -{ +struct sdap_pam_access_handler_state { struct pam_data *pd; - pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); - pd->pam_status = pam_status; - - if (pam_status == PAM_SUCCESS || pam_status == PAM_PERM_DENIED - || pam_status == PAM_ACCT_EXPIRED) { - be_req_terminate(be_req, DP_ERR_OK, pam_status, NULL); - } else { - be_req_terminate(be_req, DP_ERR_FATAL, pam_status, NULL); - } -} +}; + +static void sdap_pam_access_handler_done(struct tevent_req *subreq); -static void sdap_access_done(struct tevent_req *req); -void sdap_pam_access_handler(struct be_req *breq) +struct tevent_req * +sdap_pam_access_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_access_ctx *access_ctx, + struct pam_data *pd, + struct dp_req_params *params) { - struct be_ctx *be_ctx = be_req_get_be_ctx(breq); - struct pam_data *pd; + struct sdap_pam_access_handler_state *state; + struct tevent_req *subreq; struct tevent_req *req; - struct sdap_access_ctx *access_ctx; - struct sss_domain_info *dom; - pd = talloc_get_type(be_req_get_data(breq), struct pam_data); + req = tevent_req_create(mem_ctx, &state, + struct sdap_pam_access_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } - access_ctx = - talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data, - struct sdap_access_ctx); + state->pd = pd; - dom = be_ctx->domain; - if (strcasecmp(pd->domain, be_ctx->domain->name) != 0) { - /* Subdomain request, verify subdomain */ - dom = find_domain_by_name(be_ctx->domain, pd->domain, true); + subreq = sdap_access_send(state, params->ev, params->be_ctx, + params->domain, access_ctx, + access_ctx->id_ctx->conn, pd); + if (subreq == NULL) { + pd->pam_status = PAM_SYSTEM_ERR; + goto immediately; } - req = sdap_access_send(breq, be_ctx->ev, be_ctx, - dom, access_ctx, - access_ctx->id_ctx->conn, - pd); - if (req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to start sdap_access request\n"); - sdap_access_reply(breq, PAM_SYSTEM_ERR); - return; - } + tevent_req_set_callback(subreq, sdap_pam_access_handler_done, req); + + return req; + +immediately: + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); + tevent_req_post(req, params->ev); - tevent_req_set_callback(req, sdap_access_done, breq); + return req; } -static void sdap_access_done(struct tevent_req *req) +static void sdap_pam_access_handler_done(struct tevent_req *subreq) { + struct sdap_pam_access_handler_state *state; + struct tevent_req *req; errno_t ret; - int pam_status; - struct be_req *breq = - tevent_req_callback_data(req, struct be_req); - ret = sdap_access_recv(req); - talloc_zfree(req); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_pam_access_handler_state); + + ret = sdap_access_recv(subreq); + talloc_free(subreq); switch (ret) { case EOK: - pam_status = PAM_SUCCESS; - break; - case ERR_ACCESS_DENIED: - pam_status = PAM_PERM_DENIED; + case ERR_PASSWORD_EXPIRED_WARN: + state->pd->pam_status = PAM_SUCCESS; break; case ERR_ACCOUNT_EXPIRED: - pam_status = PAM_ACCT_EXPIRED; + state->pd->pam_status = PAM_ACCT_EXPIRED; break; + case ERR_ACCESS_DENIED: case ERR_PASSWORD_EXPIRED: - pam_status = PAM_PERM_DENIED; - break; case ERR_PASSWORD_EXPIRED_REJECT: - pam_status = PAM_PERM_DENIED; - break; - case ERR_PASSWORD_EXPIRED_WARN: - pam_status = PAM_SUCCESS; + state->pd->pam_status = PAM_PERM_DENIED; break; case ERR_PASSWORD_EXPIRED_RENEW: - pam_status = PAM_NEW_AUTHTOK_REQD; + state->pd->pam_status = PAM_NEW_AUTHTOK_REQD; break; default: DEBUG(SSSDBG_CRIT_FAILURE, "Error retrieving access check result.\n"); - pam_status = PAM_SYSTEM_ERR; + state->pd->pam_status = PAM_SYSTEM_ERR; break; } - sdap_access_reply(breq, pam_status); + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); +} + +errno_t +sdap_pam_access_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data) +{ + struct sdap_pam_access_handler_state *state = NULL; + + state = tevent_req_data(req, struct sdap_pam_access_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_data = talloc_steal(mem_ctx, state->pd); + + return EOK; } diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c index 8d6a37b2c..107f6ded1 100644 --- a/src/providers/ldap/ldap_auth.c +++ b/src/providers/ldap/ldap_auth.c @@ -49,6 +49,7 @@ #include "providers/ldap/ldap_auth.h" #include "providers/ldap/sdap_access.h" + #define LDAP_PWEXPIRE_WARNING_TIME 0 static errno_t add_expired_warning(struct pam_data *pd, long exp_time) @@ -897,49 +898,215 @@ static errno_t auth_recv(struct tevent_req *req, TALLOC_CTX *memctx, return EOK; } -/* ==Perform-Password-Change===================== */ - -struct sdap_pam_chpass_state { - struct be_req *breq; +struct sdap_pam_auth_handler_state { struct pam_data *pd; - const char *username; - char *dn; - struct sdap_handle *sh; - - struct sdap_auth_ctx *ctx; + struct be_ctx *be_ctx; }; -static void sdap_auth4chpass_done(struct tevent_req *req); -static void sdap_pam_chpass_done(struct tevent_req *req); +static void sdap_pam_auth_handler_done(struct tevent_req *subreq); -void sdap_pam_chpass_handler(struct be_req *breq) +struct tevent_req * +sdap_pam_auth_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_auth_ctx *auth_ctx, + struct pam_data *pd, + struct dp_req_params *params) { - struct be_ctx *be_ctx = be_req_get_be_ctx(breq); - struct sdap_pam_chpass_state *state; - struct sdap_auth_ctx *ctx; + struct sdap_pam_auth_handler_state *state; struct tevent_req *subreq; + struct tevent_req *req; + + req = tevent_req_create(mem_ctx, &state, + struct sdap_pam_auth_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->pd = pd; + state->be_ctx = params->be_ctx; + pd->pam_status = PAM_SYSTEM_ERR; + + switch (pd->cmd) { + case SSS_PAM_AUTHENTICATE: + subreq = auth_send(state, params->ev, auth_ctx, + pd->user, pd->authtok, false); + if (subreq == NULL) { + pd->pam_status = PAM_SYSTEM_ERR; + goto immediately; + } + + tevent_req_set_callback(subreq, sdap_pam_auth_handler_done, req); + break; + case SSS_PAM_CHAUTHTOK_PRELIM: + subreq = auth_send(state, params->ev, auth_ctx, + pd->user, pd->authtok, true); + if (subreq == NULL) { + pd->pam_status = PAM_SYSTEM_ERR; + goto immediately; + } + + tevent_req_set_callback(subreq, sdap_pam_auth_handler_done, req); + break; + case SSS_PAM_CHAUTHTOK: + pd->pam_status = PAM_SYSTEM_ERR; + goto immediately; + + case SSS_PAM_ACCT_MGMT: + case SSS_PAM_SETCRED: + case SSS_PAM_OPEN_SESSION: + case SSS_PAM_CLOSE_SESSION: + pd->pam_status = PAM_SUCCESS; + goto immediately; + default: + pd->pam_status = PAM_MODULE_UNKNOWN; + goto immediately; + } + + return req; + +immediately: + /* 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 sdap_pam_auth_handler_done(struct tevent_req *subreq) +{ + struct sdap_pam_auth_handler_state *state; + struct tevent_req *req; + enum pwexpire pw_expire_type; + void *pw_expire_data; + const char *password; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_pam_auth_handler_state); + + ret = auth_recv(subreq, state, NULL, NULL, + &pw_expire_type, &pw_expire_data); + talloc_free(subreq); + + if (ret == EOK) { + ret = check_pwexpire_policy(pw_expire_type, pw_expire_data, state->pd, + state->be_ctx->domain->pwd_expiration_warning); + if (ret == EINVAL) { + /* Unknown password expiration type. */ + state->pd->pam_status = PAM_SYSTEM_ERR; + goto done; + } + } + + switch (ret) { + case EOK: + state->pd->pam_status = PAM_SUCCESS; + break; + case ERR_AUTH_DENIED: + state->pd->pam_status = PAM_PERM_DENIED; + break; + case ERR_AUTH_FAILED: + state->pd->pam_status = PAM_AUTH_ERR; + break; + case ETIMEDOUT: + case ERR_NETWORK_IO: + state->pd->pam_status = PAM_AUTHINFO_UNAVAIL; + be_mark_offline(state->be_ctx); + break; + case ERR_ACCOUNT_EXPIRED: + state->pd->pam_status = PAM_ACCT_EXPIRED; + break; + case ERR_PASSWORD_EXPIRED: + state->pd->pam_status = PAM_NEW_AUTHTOK_REQD; + break; + case ERR_ACCOUNT_LOCKED: + state->pd->account_locked = true; + state->pd->pam_status = PAM_PERM_DENIED; + break; + default: + state->pd->pam_status = PAM_SYSTEM_ERR; + break; + } + + if (ret == EOK && state->be_ctx->domain->cache_credentials) { + ret = sss_authtok_get_password(state->pd->authtok, &password, NULL); + if (ret == EOK) { + ret = sysdb_cache_password(state->be_ctx->domain, state->pd->user, + password); + } + + /* password caching failures are not fatal errors */ + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to cache password for %s\n", + state->pd->user); + } else { + DEBUG(SSSDBG_CONF_SETTINGS, "Password successfully cached for %s\n", + state->pd->user); + } + } + +done: + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); +} + +errno_t +sdap_pam_auth_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data) +{ + struct sdap_pam_auth_handler_state *state = NULL; + + state = tevent_req_data(req, struct sdap_pam_auth_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_data = talloc_steal(mem_ctx, state->pd); + + return EOK; +} + +struct sdap_pam_chpass_handler_state { + struct be_ctx *be_ctx; + struct tevent_context *ev; + struct sdap_auth_ctx *auth_ctx; struct pam_data *pd; - int dp_err = DP_ERR_FATAL; + struct sdap_handle *sh; + char *dn; +}; - ctx = talloc_get_type(be_ctx->bet_info[BET_CHPASS].pvt_bet_data, - struct sdap_auth_ctx); - pd = talloc_get_type(be_req_get_data(breq), struct pam_data); +static void sdap_pam_chpass_handler_auth_done(struct tevent_req *subreq); +static void sdap_pam_chpass_handler_chpass_done(struct tevent_req *subreq); +static void sdap_pam_chpass_handler_last_done(struct tevent_req *subreq); - if (be_is_offline(ctx->be)) { - DEBUG(SSSDBG_CONF_SETTINGS, - "Backend is marked offline, retry later!\n"); - pd->pam_status = PAM_AUTHINFO_UNAVAIL; - dp_err = DP_ERR_OFFLINE; - goto done; +struct tevent_req * +sdap_pam_chpass_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_auth_ctx *auth_ctx, + struct pam_data *pd, + struct dp_req_params *params) +{ + struct sdap_pam_chpass_handler_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + + req = tevent_req_create(mem_ctx, &state, + struct sdap_pam_chpass_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } + state->pd = pd; + state->be_ctx = params->be_ctx; + state->auth_ctx = auth_ctx; + state->ev = params->ev; + if ((pd->priv == 1) && (pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) && (sss_authtok_get_type(pd->authtok) != SSS_AUTHTOK_TYPE_PASSWORD)) { DEBUG(SSSDBG_CONF_SETTINGS, "Password reset by root is not supported.\n"); pd->pam_status = PAM_PERM_DENIED; - dp_err = DP_ERR_OK; - goto done; + goto immediately; } DEBUG(SSSDBG_OP_FAILURE, @@ -950,52 +1117,50 @@ void sdap_pam_chpass_handler(struct be_req *breq) if (pd->cmd != SSS_PAM_CHAUTHTOK && pd->cmd != SSS_PAM_CHAUTHTOK_PRELIM) { DEBUG(SSSDBG_OP_FAILURE, "chpass target was called by wrong pam command.\n"); - goto done; + goto immediately; } - state = talloc_zero(breq, struct sdap_pam_chpass_state); - if (!state) goto done; + subreq = auth_send(state, params->ev, auth_ctx, + pd->user, pd->authtok, true); + if (subreq == NULL) { + pd->pam_status = PAM_SYSTEM_ERR; + goto immediately; + } - state->breq = breq; - state->pd = pd; - state->username = pd->user; - state->ctx = ctx; + tevent_req_set_callback(subreq, sdap_pam_chpass_handler_auth_done, req); - subreq = auth_send(breq, be_ctx->ev, ctx, - state->username, pd->authtok, true); - if (!subreq) goto done; + return req; - tevent_req_set_callback(subreq, sdap_auth4chpass_done, state); - return; +immediately: + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); + tevent_req_post(req, params->ev); -done: - be_req_terminate(breq, dp_err, pd->pam_status, NULL); + return req; } -static void sdap_lastchange_done(struct tevent_req *req); -static void sdap_auth4chpass_done(struct tevent_req *req) +static void sdap_pam_chpass_handler_auth_done(struct tevent_req *subreq) { - struct sdap_pam_chpass_state *state = - tevent_req_callback_data(req, struct sdap_pam_chpass_state); - struct be_ctx *be_ctx = be_req_get_be_ctx(state->breq); - struct tevent_req *subreq; + struct sdap_pam_chpass_handler_state *state; + struct tevent_req *req; enum pwexpire pw_expire_type; void *pw_expire_data; - int dp_err = DP_ERR_FATAL; - int ret; size_t msg_len; uint8_t *msg; + errno_t ret; - ret = auth_recv(req, state, &state->sh, &state->dn, + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_pam_chpass_handler_state); + + ret = auth_recv(subreq, state, &state->sh, &state->dn, &pw_expire_type, &pw_expire_data); - talloc_zfree(req); + talloc_free(subreq); + if ((ret == EOK || ret == ERR_PASSWORD_EXPIRED) && state->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) { - DEBUG(SSSDBG_TRACE_ALL, - "Initial authentication for change password operation " - "successful.\n"); + DEBUG(SSSDBG_TRACE_ALL, "Initial authentication for change " + "password operation successful.\n"); state->pd->pam_status = PAM_SUCCESS; - dp_err = DP_ERR_OK; goto done; } @@ -1006,12 +1171,11 @@ static void sdap_auth4chpass_done(struct tevent_req *req) break; case PWEXPIRE_KERBEROS: ret = check_pwexpire_kerberos(pw_expire_data, time(NULL), NULL, - be_ctx->domain->pwd_expiration_warning); + state->be_ctx->domain->pwd_expiration_warning); if (ret == ERR_PASSWORD_EXPIRED) { - DEBUG(SSSDBG_CRIT_FAILURE, - "LDAP provider cannot change kerberos " - "passwords.\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "LDAP provider cannot change " + "kerberos passwords.\n"); state->pd->pam_status = PAM_SYSTEM_ERR; goto done; } @@ -1027,100 +1191,104 @@ static void sdap_auth4chpass_done(struct tevent_req *req) } switch (ret) { - case EOK: - case ERR_PASSWORD_EXPIRED: - DEBUG(SSSDBG_TRACE_LIBS, - "user [%s] successfully authenticated.\n", state->dn); - if (pw_expire_type == PWEXPIRE_SHADOW) { -/* TODO: implement async ldap modify request */ - DEBUG(SSSDBG_CRIT_FAILURE, - "Changing shadow password attributes not implemented.\n"); - state->pd->pam_status = PAM_MODULE_UNKNOWN; - goto done; - } else { - const char *password; - const char *new_password; - int timeout; - - ret = sss_authtok_get_password(state->pd->authtok, - &password, NULL); - if (ret) { - state->pd->pam_status = PAM_SYSTEM_ERR; - goto done; - } - ret = sss_authtok_get_password(state->pd->newauthtok, - &new_password, NULL); - if (ret) { - state->pd->pam_status = PAM_SYSTEM_ERR; + case EOK: + case ERR_PASSWORD_EXPIRED: + DEBUG(SSSDBG_TRACE_LIBS, + "user [%s] successfully authenticated.\n", state->dn); + if (pw_expire_type == PWEXPIRE_SHADOW) { + /* TODO: implement async ldap modify request */ + DEBUG(SSSDBG_CRIT_FAILURE, + "Changing shadow password attributes not implemented.\n"); + state->pd->pam_status = PAM_MODULE_UNKNOWN; goto done; - } + } else { + const char *password; + const char *new_password; + int timeout; + + ret = sss_authtok_get_password(state->pd->authtok, + &password, NULL); + if (ret) { + state->pd->pam_status = PAM_SYSTEM_ERR; + goto done; + } + ret = sss_authtok_get_password(state->pd->newauthtok, + &new_password, NULL); + if (ret) { + state->pd->pam_status = PAM_SYSTEM_ERR; + goto done; + } - timeout = dp_opt_get_int(state->ctx->opts->basic, SDAP_OPT_TIMEOUT); + timeout = dp_opt_get_int(state->auth_ctx->opts->basic, + SDAP_OPT_TIMEOUT); + + subreq = sdap_exop_modify_passwd_send(state, state->ev, + state->sh, state->dn, + password, new_password, + timeout); + if (subreq == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to change password for " + "%s\n", state->pd->user); + state->pd->pam_status = PAM_SYSTEM_ERR; + goto done; + } - subreq = sdap_exop_modify_passwd_send(state, be_ctx->ev, - state->sh, state->dn, - password, new_password, - timeout); - if (!subreq) { - DEBUG(SSSDBG_OP_FAILURE, - "Failed to change password for %s\n", state->username); - goto done; + tevent_req_set_callback(subreq, + sdap_pam_chpass_handler_chpass_done, + req); + return; } - tevent_req_set_callback(subreq, sdap_pam_chpass_done, state); - return; - } - break; - case ERR_AUTH_DENIED: - case ERR_AUTH_FAILED: - state->pd->pam_status = PAM_AUTH_ERR; - ret = pack_user_info_chpass_error(state->pd, "Old password not accepted.", - &msg_len, &msg); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "pack_user_info_chpass_error failed.\n"); - } else { - ret = pam_add_response(state->pd, SSS_PAM_USER_INFO, msg_len, - msg); + break; + case ERR_AUTH_DENIED: + case ERR_AUTH_FAILED: + state->pd->pam_status = PAM_AUTH_ERR; + ret = pack_user_info_chpass_error(state->pd, "Old password not " + "accepted.", &msg_len, &msg); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n"); + DEBUG(SSSDBG_CRIT_FAILURE, + "pack_user_info_chpass_error failed.\n"); + } else { + ret = pam_add_response(state->pd, SSS_PAM_USER_INFO, + msg_len, msg); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n"); + } } + break; + case ETIMEDOUT: + case ERR_NETWORK_IO: + state->pd->pam_status = PAM_AUTHINFO_UNAVAIL; + be_mark_offline(state->be_ctx); + break; + default: + state->pd->pam_status = PAM_SYSTEM_ERR; + break; } - break; - case ETIMEDOUT: - case ERR_NETWORK_IO: - state->pd->pam_status = PAM_AUTHINFO_UNAVAIL; - be_mark_offline(be_ctx); - dp_err = DP_ERR_OFFLINE; - break; - default: - state->pd->pam_status = PAM_SYSTEM_ERR; - } - done: - be_req_terminate(state->breq, dp_err, state->pd->pam_status, NULL); + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); } -static void sdap_pam_chpass_done(struct tevent_req *req) +static void sdap_pam_chpass_handler_chpass_done(struct tevent_req *subreq) { - struct sdap_pam_chpass_state *state = - tevent_req_callback_data(req, struct sdap_pam_chpass_state); - struct be_ctx *be_ctx = be_req_get_be_ctx(state->breq); - int dp_err = DP_ERR_FATAL; - int ret; + struct sdap_pam_chpass_handler_state *state; + struct tevent_req *req; char *user_error_message = NULL; char *lastchanged_name; - struct tevent_req *subreq; size_t msg_len; uint8_t *msg; + errno_t ret; - ret = sdap_exop_modify_passwd_recv(req, state, &user_error_message); - talloc_zfree(req); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_pam_chpass_handler_state); + + ret = sdap_exop_modify_passwd_recv(subreq, state, &user_error_message); + talloc_free(subreq); switch (ret) { case EOK: state->pd->pam_status = PAM_SUCCESS; - dp_err = DP_ERR_OK; break; case ERR_CHPASS_DENIED: state->pd->pam_status = PAM_NEW_AUTHTOK_REQD; @@ -1135,12 +1303,11 @@ static void sdap_pam_chpass_done(struct tevent_req *req) if (state->pd->pam_status != PAM_SUCCESS && user_error_message != NULL) { ret = pack_user_info_chpass_error(state->pd, user_error_message, - &msg_len, &msg); + &msg_len, &msg); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "pack_user_info_chpass_error failed.\n"); } else { - ret = pam_add_response(state->pd, SSS_PAM_USER_INFO, msg_len, - msg); + ret = pam_add_response(state->pd, SSS_PAM_USER_INFO, msg_len, msg); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n"); } @@ -1148,11 +1315,11 @@ static void sdap_pam_chpass_done(struct tevent_req *req) } if (state->pd->pam_status == PAM_SUCCESS && - dp_opt_get_bool(state->ctx->opts->basic, + dp_opt_get_bool(state->auth_ctx->opts->basic, SDAP_CHPASS_UPDATE_LAST_CHANGE)) { - lastchanged_name = state->ctx->opts->user_map[SDAP_AT_SP_LSTCHG].name; + lastchanged_name = state->auth_ctx->opts->user_map[SDAP_AT_SP_LSTCHG].name; - subreq = sdap_modify_shadow_lastchange_send(state, be_ctx->ev, + subreq = sdap_modify_shadow_lastchange_send(state, state->ev, state->sh, state->dn, lastchanged_name); if (subreq == NULL) { @@ -1160,181 +1327,51 @@ static void sdap_pam_chpass_done(struct tevent_req *req) goto done; } - tevent_req_set_callback(subreq, sdap_lastchange_done, state); + tevent_req_set_callback(subreq, sdap_pam_chpass_handler_last_done, req); return; } done: - be_req_terminate(state->breq, dp_err, state->pd->pam_status, NULL); + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); } -static void sdap_lastchange_done(struct tevent_req *req) +static void sdap_pam_chpass_handler_last_done(struct tevent_req *subreq) { - struct sdap_pam_chpass_state *state = - tevent_req_callback_data(req, struct sdap_pam_chpass_state); - int dp_err = DP_ERR_FATAL; + struct sdap_pam_chpass_handler_state *state; + struct tevent_req *req; errno_t ret; - ret = sdap_modify_shadow_lastchange_recv(req); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_pam_chpass_handler_state); + + ret = sdap_modify_shadow_lastchange_recv(subreq); + talloc_free(subreq); + if (ret != EOK) { state->pd->pam_status = PAM_SYSTEM_ERR; goto done; } - dp_err = DP_ERR_OK; state->pd->pam_status = PAM_SUCCESS; done: - be_req_terminate(state->breq, dp_err, state->pd->pam_status, NULL); -} - -/* ==Perform-User-Authentication-and-Password-Caching===================== */ - -struct sdap_pam_auth_state { - struct be_req *breq; - struct pam_data *pd; -}; - -static void sdap_pam_auth_done(struct tevent_req *req); - -void sdap_pam_auth_handler(struct be_req *breq) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(breq); - struct sdap_pam_auth_state *state; - struct sdap_auth_ctx *ctx; - struct tevent_req *subreq; - struct pam_data *pd; - int dp_err = DP_ERR_FATAL; - - ctx = talloc_get_type(be_ctx->bet_info[BET_AUTH].pvt_bet_data, - struct sdap_auth_ctx); - pd = talloc_get_type(be_req_get_data(breq), struct pam_data); - - if (be_is_offline(ctx->be)) { - DEBUG(SSSDBG_CONF_SETTINGS, - "Backend is marked offline, retry later!\n"); - pd->pam_status = PAM_AUTHINFO_UNAVAIL; - dp_err = DP_ERR_OFFLINE; - goto done; - } - - pd->pam_status = PAM_SYSTEM_ERR; - - switch (pd->cmd) { - case SSS_PAM_AUTHENTICATE: - case SSS_PAM_CHAUTHTOK_PRELIM: - - state = talloc_zero(breq, struct sdap_pam_auth_state); - if (!state) goto done; - - state->breq = breq; - state->pd = pd; - - subreq = auth_send(breq, be_ctx->ev, ctx, - pd->user, pd->authtok, - pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ? true : false); - if (!subreq) goto done; - - tevent_req_set_callback(subreq, sdap_pam_auth_done, state); - return; - - case SSS_PAM_CHAUTHTOK: - break; - - case SSS_PAM_ACCT_MGMT: - case SSS_PAM_SETCRED: - case SSS_PAM_OPEN_SESSION: - case SSS_PAM_CLOSE_SESSION: - pd->pam_status = PAM_SUCCESS; - dp_err = DP_ERR_OK; - break; - default: - pd->pam_status = PAM_MODULE_UNKNOWN; - dp_err = DP_ERR_OK; - } - -done: - be_req_terminate(breq, dp_err, pd->pam_status, NULL); + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); } -static void sdap_pam_auth_done(struct tevent_req *req) +errno_t +sdap_pam_chpass_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data) { - struct sdap_pam_auth_state *state = - tevent_req_callback_data(req, struct sdap_pam_auth_state); - struct be_ctx *be_ctx = be_req_get_be_ctx(state->breq); - enum pwexpire pw_expire_type; - void *pw_expire_data; - const char *password; - int dp_err = DP_ERR_OK; - int ret; + struct sdap_pam_chpass_handler_state *state = NULL; - ret = auth_recv(req, state, NULL, NULL, - &pw_expire_type, &pw_expire_data); - talloc_zfree(req); + state = tevent_req_data(req, struct sdap_pam_chpass_handler_state); - if (ret == EOK) { - ret = check_pwexpire_policy(pw_expire_type, pw_expire_data, state->pd, - be_ctx->domain->pwd_expiration_warning); - if (ret == EINVAL) { - /* Unknown password expiration type. */ - state->pd->pam_status = PAM_SYSTEM_ERR; - goto done; - } - } - - switch (ret) { - case EOK: - state->pd->pam_status = PAM_SUCCESS; - break; - case ERR_AUTH_DENIED: - state->pd->pam_status = PAM_PERM_DENIED; - break; - case ERR_AUTH_FAILED: - state->pd->pam_status = PAM_AUTH_ERR; - break; - case ETIMEDOUT: - case ERR_NETWORK_IO: - state->pd->pam_status = PAM_AUTHINFO_UNAVAIL; - break; - case ERR_ACCOUNT_EXPIRED: - state->pd->pam_status = PAM_ACCT_EXPIRED; - break; - case ERR_PASSWORD_EXPIRED: - state->pd->pam_status = PAM_NEW_AUTHTOK_REQD; - break; - case ERR_ACCOUNT_LOCKED: - state->pd->account_locked = true; - state->pd->pam_status = PAM_PERM_DENIED; - break; - default: - state->pd->pam_status = PAM_SYSTEM_ERR; - dp_err = DP_ERR_FATAL; - } - - if (ret == ETIMEDOUT || ret == ERR_NETWORK_IO) { - be_mark_offline(be_ctx); - dp_err = DP_ERR_OFFLINE; - goto done; - } - - if (ret == EOK && be_ctx->domain->cache_credentials) { + TEVENT_REQ_RETURN_ON_ERROR(req); - ret = sss_authtok_get_password(state->pd->authtok, &password, NULL); - if (ret == EOK) { - ret = sysdb_cache_password(be_ctx->domain, state->pd->user, - password); - } + *_data = talloc_steal(mem_ctx, state->pd); - /* password caching failures are not fatal errors */ - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Failed to cache password for %s\n", - state->pd->user); - } else { - DEBUG(SSSDBG_CONF_SETTINGS, "Password successfully cached for %s\n", - state->pd->user); - } - } - -done: - be_req_terminate(state->breq, dp_err, state->pd->pam_status, NULL); + return EOK; } diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index 27b62a635..c9f78ff8d 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -38,12 +38,6 @@ /* a fd the child process would log into */ int ldap_child_debug_fd = -1; -void sdap_handler_done(struct be_req *req, int dp_err, - int error, const char *errstr) -{ - return be_req_terminate(req, dp_err, error, errstr); -} - int ldap_id_setup_tasks(struct sdap_id_ctx *ctx) { return sdap_id_setup_tasks(ctx->be, ctx, ctx->opts->sdom, diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h index d50473c88..b39f67892 100644 --- a/src/providers/ldap/ldap_common.h +++ b/src/providers/ldap/ldap_common.h @@ -78,12 +78,15 @@ struct sdap_auth_ctx { struct sdap_service *chpass_service; }; -int sssm_ldap_id_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data); +struct tevent_req * +sdap_online_check_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_id_ctx *id_ctx, + void *data, + struct dp_req_params *params); -void sdap_check_online(struct be_req *breq); -void sdap_do_online_check(struct be_req *be_req, struct sdap_id_ctx *ctx); +errno_t sdap_online_check_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data); struct tevent_req* sdap_reinit_cleanup_send(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx, @@ -92,9 +95,15 @@ struct tevent_req* sdap_reinit_cleanup_send(TALLOC_CTX *mem_ctx, errno_t sdap_reinit_cleanup_recv(struct tevent_req *req); /* id */ -void sdap_account_info_handler(struct be_req *breq); -void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx, - struct sdap_id_conn_ctx *conn); +struct tevent_req * +sdap_account_info_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_id_ctx *id_ctx, + struct be_acct_req *data, + struct dp_req_params *params); + +errno_t sdap_account_info_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data); /* Set up enumeration and/or cleanup */ int ldap_id_setup_tasks(struct sdap_id_ctx *ctx); @@ -121,20 +130,39 @@ sdap_handle_acct_req_recv(struct tevent_req *req, int *_dp_error, const char **_err, int *sdap_ret); -/* auth */ -void sdap_pam_auth_handler(struct be_req *breq); +struct tevent_req * +sdap_pam_auth_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_auth_ctx *auth_ctx, + struct pam_data *pd, + struct dp_req_params *params); + +errno_t +sdap_pam_auth_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data); -/* chpass */ -void sdap_pam_chpass_handler(struct be_req *breq); +struct tevent_req * +sdap_pam_chpass_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_auth_ctx *auth_ctx, + struct pam_data *pd, + struct dp_req_params *params); -/* access */ -void sdap_pam_access_handler(struct be_req *breq); +errno_t +sdap_pam_chpass_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data); /* autofs */ -void sdap_autofs_handler(struct be_req *breq); +struct tevent_req * +sdap_autofs_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_id_ctx *id_ctx, + struct dp_autofs_data *data, + struct dp_req_params *params); -void sdap_handler_done(struct be_req *req, int dp_err, - int error, const char *errstr); +errno_t +sdap_autofs_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data); int sdap_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, const char *service_name, const char *dns_service_name, diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c index ee5b374db..49ce9db48 100644 --- a/src/providers/ldap/ldap_id.c +++ b/src/providers/ldap/ldap_id.c @@ -1286,152 +1286,11 @@ int groups_by_user_recv(struct tevent_req *req, int *dp_error_out, int *sdap_ret return EOK; } -static void sdap_check_online_done(struct tevent_req *req); -void sdap_check_online(struct be_req *be_req) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - struct sdap_id_ctx *ctx; - - ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data, - struct sdap_id_ctx); - - return sdap_do_online_check(be_req, ctx); -} - -struct sdap_online_check_ctx { - struct be_req *be_req; - struct sdap_id_ctx *id_ctx; -}; - -void sdap_do_online_check(struct be_req *be_req, struct sdap_id_ctx *ctx) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - struct tevent_req *req; - struct sdap_online_check_ctx *check_ctx; - errno_t ret; - - check_ctx = talloc_zero(be_req, struct sdap_online_check_ctx); - if (!check_ctx) { - ret = ENOMEM; - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed\n"); - goto fail; - } - check_ctx->id_ctx = ctx; - check_ctx->be_req = be_req; - - req = sdap_cli_connect_send(be_req, be_ctx->ev, ctx->opts, - be_ctx, ctx->conn->service, false, - CON_TLS_DFL, false); - if (req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "sdap_cli_connect_send failed.\n"); - ret = EIO; - goto fail; - } - tevent_req_set_callback(req, sdap_check_online_done, check_ctx); - - return; -fail: - sdap_handler_done(be_req, DP_ERR_FATAL, ret, NULL); -} - -static void sdap_check_online_reinit_done(struct tevent_req *req); - -static void sdap_check_online_done(struct tevent_req *req) -{ - struct sdap_online_check_ctx *check_ctx = tevent_req_callback_data(req, - struct sdap_online_check_ctx); - int ret; - int dp_err = DP_ERR_FATAL; - bool can_retry; - struct sdap_server_opts *srv_opts; - struct be_req *be_req; - struct sdap_id_ctx *id_ctx; - struct tevent_req *reinit_req = NULL; - bool reinit = false; - struct be_ctx *be_ctx; - - ret = sdap_cli_connect_recv(req, NULL, &can_retry, NULL, &srv_opts); - talloc_zfree(req); - - if (ret != EOK) { - if (!can_retry) { - dp_err = DP_ERR_OFFLINE; - } - } else { - dp_err = DP_ERR_OK; - - if (!check_ctx->id_ctx->srv_opts) { - srv_opts->max_user_value = 0; - srv_opts->max_group_value = 0; - srv_opts->max_service_value = 0; - srv_opts->max_sudo_value = 0; - } else if (strcmp(srv_opts->server_id, check_ctx->id_ctx->srv_opts->server_id) == 0 - && srv_opts->supports_usn - && check_ctx->id_ctx->srv_opts->last_usn > srv_opts->last_usn) { - check_ctx->id_ctx->srv_opts->max_user_value = 0; - check_ctx->id_ctx->srv_opts->max_group_value = 0; - check_ctx->id_ctx->srv_opts->max_service_value = 0; - check_ctx->id_ctx->srv_opts->max_sudo_value = 0; - check_ctx->id_ctx->srv_opts->last_usn = srv_opts->last_usn; - - reinit = true; - } - - sdap_steal_server_opts(check_ctx->id_ctx, &srv_opts); - } - - be_req = check_ctx->be_req; - be_ctx = be_req_get_be_ctx(be_req); - id_ctx = check_ctx->id_ctx; - talloc_free(check_ctx); - - if (reinit) { - DEBUG(SSSDBG_TRACE_FUNC, "Server reinitialization detected. " - "Cleaning cache.\n"); - reinit_req = sdap_reinit_cleanup_send(be_req, be_ctx, id_ctx); - if (reinit_req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to perform reinitialization " - "clean up.\n"); - /* not fatal */ - goto done; - } - - tevent_req_set_callback(reinit_req, sdap_check_online_reinit_done, - be_req); - return; - } - -done: - sdap_handler_done(be_req, dp_err, 0, NULL); -} - -static void sdap_check_online_reinit_done(struct tevent_req *req) -{ - struct be_req *be_req = NULL; - errno_t ret; - - be_req = tevent_req_callback_data(req, struct be_req); - ret = sdap_reinit_cleanup_recv(req); - talloc_zfree(req); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to perform reinitialization " - "clean up [%d]: %s\n", ret, strerror(ret)); - /* not fatal */ - } else { - DEBUG(SSSDBG_TRACE_FUNC, "Reinitialization clean up completed\n"); - } - - sdap_handler_done(be_req, DP_ERR_OK, 0, NULL); -} - /* =Get-Account-Info-Call================================================= */ /* FIXME: embed this function in sssd_be and only call out * specific functions from modules ? */ -void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx, - struct sdap_id_conn_ctx *conn); - static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct sdap_id_ctx *ctx, @@ -1445,20 +1304,6 @@ static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx, errno_t sdap_get_user_and_group_recv(struct tevent_req *req, int *dp_error_out, int *sdap_ret); -void sdap_account_info_handler(struct be_req *breq) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(breq); - struct sdap_id_ctx *ctx; - - ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data, struct sdap_id_ctx); - if (!ctx) { - DEBUG(SSSDBG_CRIT_FAILURE, "Could not get sdap ctx\n"); - return sdap_handler_done(breq, DP_ERR_FATAL, - EINVAL, "Invalid request data\n"); - } - return sdap_handle_account_info(breq, ctx, ctx->conn); -} - bool sdap_is_enum_request(struct be_acct_req *ar) { switch (ar->entry_type & BE_REQ_TYPE_MASK) { @@ -1757,66 +1602,6 @@ sdap_handle_acct_req_recv(struct tevent_req *req, return EOK; } -static void sdap_account_info_complete(struct tevent_req *req); - -void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx, - struct sdap_id_conn_ctx *conn) -{ - struct be_acct_req *ar; - struct tevent_req *req; - - if (be_is_offline(ctx->be)) { - return sdap_handler_done(breq, DP_ERR_OFFLINE, EAGAIN, "Offline"); - } - - ar = talloc_get_type(be_req_get_data(breq), struct be_acct_req); - if (ar == NULL) { - return sdap_handler_done(breq, DP_ERR_FATAL, - EINVAL, "Invalid private data"); - } - - if (sdap_is_enum_request(ar)) { - DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n"); - return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success"); - } - - req = sdap_handle_acct_req_send(breq, ctx->be, ar, ctx, - ctx->opts->sdom, conn, true); - if (req == NULL) { - return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory"); - } - tevent_req_set_callback(req, sdap_account_info_complete, breq); -} - -static void sdap_account_info_complete(struct tevent_req *req) -{ - const char *error_text; - const char *req_error_text; - struct be_req *breq = tevent_req_callback_data(req, struct be_req); - int ret, dp_error; - - ret = sdap_handle_acct_req_recv(req, &dp_error, &req_error_text, NULL); - talloc_zfree(req); - if (dp_error == DP_ERR_OK) { - if (ret == EOK) { - error_text = NULL; - } else { - DEBUG(SSSDBG_CRIT_FAILURE, - "Bug: dp_error is OK on failed request\n"); - dp_error = DP_ERR_FATAL; - error_text = req_error_text; - } - } else if (dp_error == DP_ERR_OFFLINE) { - error_text = "Offline"; - } else if (dp_error == DP_ERR_FATAL && ret == ENOMEM) { - error_text = "Out of memory"; - } else { - error_text = req_error_text; - } - - sdap_handler_done(breq, dp_error, ret, error_text); -} - struct get_user_and_group_state { struct tevent_context *ev; struct sdap_id_ctx *id_ctx; @@ -2012,3 +1797,88 @@ errno_t sdap_get_user_and_group_recv(struct tevent_req *req, return EOK; } + +struct sdap_account_info_handler_state { + struct dp_reply_std reply; +}; + +static void sdap_account_info_handler_done(struct tevent_req *subreq); + +struct tevent_req * +sdap_account_info_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_id_ctx *id_ctx, + struct be_acct_req *data, + struct dp_req_params *params) +{ + struct sdap_account_info_handler_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct sdap_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 = sdap_handle_acct_req_send(state, params->be_ctx, data, id_ctx, + id_ctx->opts->sdom, id_ctx->conn, true); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } + + tevent_req_set_callback(subreq, sdap_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 sdap_account_info_handler_done(struct tevent_req *subreq) +{ + struct sdap_account_info_handler_state *state; + struct tevent_req *req; + const char *error_msg; + int dp_error; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_account_info_handler_state); + + ret = sdap_handle_acct_req_recv(subreq, &dp_error, &error_msg, NULL); + talloc_zfree(subreq); + + /* TODO For backward compatibility we always return EOK to DP now. */ + dp_reply_std_set(&state->reply, dp_error, ret, error_msg); + tevent_req_done(req); +} + +errno_t sdap_account_info_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data) +{ + struct sdap_account_info_handler_state *state = NULL; + + state = tevent_req_data(req, struct sdap_account_info_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *data = state->reply; + + return EOK; +} diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c index cfd68d9b9..566924cbc 100644 --- a/src/providers/ldap/ldap_init.c +++ b/src/providers/ldap/ldap_init.c @@ -32,31 +32,10 @@ #include "providers/fail_over_srv.h" #include "providers/be_refresh.h" -static void sdap_shutdown(struct be_req *req); - -/* Id Handler */ -struct bet_ops sdap_id_ops = { - .handler = sdap_account_info_handler, - .finalize = sdap_shutdown, - .check_online = sdap_check_online -}; - -/* Auth Handler */ -struct bet_ops sdap_auth_ops = { - .handler = sdap_pam_auth_handler, - .finalize = sdap_shutdown -}; - -/* Chpass Handler */ -struct bet_ops sdap_chpass_ops = { - .handler = sdap_pam_chpass_handler, - .finalize = sdap_shutdown -}; - -/* Access Handler */ -struct bet_ops sdap_access_ops = { - .handler = sdap_pam_access_handler, - .finalize = sdap_shutdown +struct ldap_init_ctx { + struct sdap_options *options; + struct sdap_id_ctx *id_ctx; + struct sdap_auth_ctx *auth_ctx; }; /* Please use this only for short lists */ @@ -85,259 +64,193 @@ errno_t check_order_list_for_duplicates(char **list, return EOK; } -static int ldap_id_init_internal(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +static errno_t ldap_init_auth_ctx(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct sdap_id_ctx *id_ctx, + struct sdap_options *options, + struct sdap_auth_ctx **_auth_ctx) { - struct sdap_id_ctx *ctx = NULL; - const char *urls; - const char *backup_urls; - const char *dns_service_name; - const char *sasl_mech; - struct sdap_service *sdap_service; - struct sdap_options *opts = NULL; - int ret; - - /* If we're already set up, just return that */ - if(bectx->bet_info[BET_ID].mod_name && - strcmp("ldap", bectx->bet_info[BET_ID].mod_name) == 0) { - DEBUG(SSSDBG_TRACE_INTERNAL, - "Re-using sdap_id_ctx for this provider\n"); - *ops = bectx->bet_info[BET_ID].bet_ops; - *pvt_data = bectx->bet_info[BET_ID].pvt_bet_data; - return EOK; - } + struct sdap_auth_ctx *auth_ctx; - ret = ldap_get_options(bectx, bectx->domain, bectx->cdb, - bectx->conf_path, &opts); - if (ret != EOK) { - goto done; + auth_ctx = talloc(mem_ctx, struct sdap_auth_ctx); + if (auth_ctx == NULL) { + return ENOMEM; } - dns_service_name = dp_opt_get_string(opts->basic, - SDAP_DNS_SERVICE_NAME); - DEBUG(SSSDBG_CONF_SETTINGS, - "Service name for discovery set to %s\n", dns_service_name); + auth_ctx->be = be_ctx; + auth_ctx->opts = options; + auth_ctx->service = id_ctx->conn->service; + auth_ctx->chpass_service = NULL; - urls = dp_opt_get_string(opts->basic, SDAP_URI); - backup_urls = dp_opt_get_string(opts->basic, SDAP_BACKUP_URI); + *_auth_ctx = auth_ctx; - ret = sdap_service_init(bectx, bectx, "LDAP", - dns_service_name, urls, backup_urls, - &sdap_service); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Failed to initialize failover service!\n"); - goto done; - } + return EOK; +} - ctx = sdap_id_ctx_new(bectx, bectx, sdap_service); - if (!ctx) { - ret = ENOMEM; - goto done; +static errno_t init_chpass_service(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct sdap_options *opts, + struct sdap_service **_chpass_service) +{ + errno_t ret; + const char *urls; + const char *backup_urls; + const char *dns_service_name; + struct sdap_service *chpass_service; + + dns_service_name = dp_opt_get_string(opts->basic, + SDAP_CHPASS_DNS_SERVICE_NAME); + if (dns_service_name != NULL) { + DEBUG(SSSDBG_TRACE_LIBS, + "Service name for chpass discovery set to %s\n", + dns_service_name); } - ctx->opts = talloc_steal(ctx, opts); - sasl_mech = dp_opt_get_string(ctx->opts->basic, SDAP_SASL_MECH); - if (sasl_mech && strcasecmp(sasl_mech, "GSSAPI") == 0) { - if (dp_opt_get_bool(ctx->opts->basic, SDAP_KRB5_KINIT)) { - ret = sdap_gssapi_init(ctx, ctx->opts->basic, - ctx->be, ctx->conn->service, - &ctx->krb5_service); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "sdap_gssapi_init failed [%d][%s].\n", - ret, strerror(ret)); - goto done; - } + urls = dp_opt_get_string(opts->basic, SDAP_CHPASS_URI); + backup_urls = dp_opt_get_string(opts->basic, SDAP_CHPASS_BACKUP_URI); + + if (urls != NULL || backup_urls != NULL || dns_service_name != NULL) { + ret = sdap_service_init(mem_ctx, + be_ctx, + "LDAP_CHPASS", + dns_service_name, + urls, + backup_urls, + &chpass_service); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to initialize failover service!\n"); + return ret; } + } else { + DEBUG(SSSDBG_TRACE_ALL, + "ldap_chpass_uri and ldap_chpass_dns_service_name not set, " + "using ldap_uri.\n"); + chpass_service = NULL; } - ret = setup_tls_config(ctx->opts->basic); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "setup_tls_config failed [%d][%s].\n", - ret, strerror(ret)); - goto done; - } + *_chpass_service = chpass_service; + return EOK; +} - /* Set up the ID mapping object */ - ret = sdap_idmap_init(ctx, ctx, &ctx->opts->idmap_ctx); - if (ret != EOK) goto done; +static errno_t get_access_order_list(TALLOC_CTX *mem_ctx, + const char *order, + char ***_order_list) +{ + errno_t ret; + char **order_list; + int order_list_len; - ret = sdap_setup_child(); + ret = split_on_separator(mem_ctx, order, ',', true, true, + &order_list, &order_list_len); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "setup_child failed [%d][%s].\n", - ret, strerror(ret)); + DEBUG(SSSDBG_CRIT_FAILURE, "split_on_separator failed.\n"); goto done; } - /* setup SRV lookup plugin */ - ret = be_fo_set_dns_srv_lookup_plugin(bectx, NULL); + ret = check_order_list_for_duplicates(order_list, false); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set SRV lookup plugin " - "[%d]: %s\n", ret, strerror(ret)); + DEBUG(SSSDBG_CRIT_FAILURE, + "check_order_list_for_duplicates failed.\n"); goto done; } - /* setup periodical refresh of expired records */ - ret = sdap_refresh_init(bectx->refresh_ctx, ctx); - if (ret != EOK && ret != EEXIST) { - DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh " - "will not work [%d]: %s\n", ret, strerror(ret)); + if (order_list_len > LDAP_ACCESS_LAST) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Currently only [%d] different access rules are supported.\n", + LDAP_ACCESS_LAST); + ret = EINVAL; + goto done; } - *ops = &sdap_id_ops; - *pvt_data = ctx; - ret = EOK; + *_order_list = order_list; done: if (ret != EOK) { - talloc_free(opts); - talloc_free(ctx); + talloc_free(order_list); } + return ret; } -int sssm_ldap_id_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +static errno_t check_expire_policy(struct sdap_options *opts) { - int ret; - struct sdap_id_ctx *ctx = NULL; - - ret = ldap_id_init_internal(bectx, ops, (void **) &ctx); - if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, - "ldap_id_init_internal failed [%d][%s].\n", - ret, strerror(ret)); - goto done; - } - - ret = ldap_id_setup_tasks(ctx); - if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, - "sdap_id_setup_tasks failed [%d][%s].\n", - ret, strerror(ret)); - goto done; + const char *expire_policy; + bool matched_policy = false; + const char *policies[] = {LDAP_ACCOUNT_EXPIRE_SHADOW, + LDAP_ACCOUNT_EXPIRE_AD, + LDAP_ACCOUNT_EXPIRE_NDS, + LDAP_ACCOUNT_EXPIRE_RHDS, + LDAP_ACCOUNT_EXPIRE_IPA, + LDAP_ACCOUNT_EXPIRE_389DS, + NULL}; + + expire_policy = dp_opt_get_cstring(opts->basic, + SDAP_ACCOUNT_EXPIRE_POLICY); + if (expire_policy == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Warning: LDAP access rule 'expire' is set, " + "but no ldap_account_expire_policy configured. " + "All domain users will be denied access.\n"); + return EOK; } - *pvt_data = ctx; - ret = EOK; - -done: - if (ret != EOK) { - talloc_free(ctx); + for (unsigned i = 0; policies[i] != NULL; i++) { + if (strcasecmp(expire_policy, policies[i]) == 0) { + matched_policy = true; + break; + } } - return ret; -} - -int sssm_ldap_auth_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) -{ - void *data; - struct sdap_id_ctx *id_ctx; - struct sdap_auth_ctx *ctx; - int ret; - ret = ldap_id_init_internal(bectx, ops, &data); - if (ret == EOK) { - id_ctx = talloc_get_type(data, struct sdap_id_ctx); - - ctx = talloc(bectx, struct sdap_auth_ctx); - if (!ctx) return ENOMEM; - - ctx->be = bectx; - ctx->opts = id_ctx->opts; - ctx->service = id_ctx->conn->service; - ctx->chpass_service = NULL; - - *ops = &sdap_auth_ops; - *pvt_data = ctx; + if (matched_policy == false) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unsupported LDAP account expire policy [%s].\n", + expire_policy); + return EINVAL; } - return ret; + return EOK; } -int sssm_ldap_chpass_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +static errno_t get_access_filter(TALLOC_CTX *mem_ctx, + struct sdap_options *opts, + const char **_filter) { - int ret; - void *data; - struct sdap_auth_ctx *ctx = NULL; - const char *urls; - const char *backup_urls; - const char *dns_service_name; - - ret = sssm_ldap_auth_init(bectx, ops, &data); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ldap_auth_init failed.\n"); - goto done; - } - - ctx = talloc_get_type(data, struct sdap_auth_ctx); + const char *filter; - dns_service_name = dp_opt_get_string(ctx->opts->basic, - SDAP_CHPASS_DNS_SERVICE_NAME); - if (dns_service_name) { - DEBUG(SSSDBG_TRACE_LIBS, - "Service name for chpass discovery set to %s\n", - dns_service_name); + filter = dp_opt_get_cstring(opts->basic, SDAP_ACCESS_FILTER); + if (filter == NULL) { + /* It's okay if this is NULL. In that case we will simply act + * like the 'deny' provider. + */ + DEBUG(SSSDBG_FATAL_FAILURE, + "Warning: LDAP access rule 'filter' is set, " + "but no ldap_access_filter configured. " + "All domain users will be denied access.\n"); + return EOK; } - urls = dp_opt_get_string(ctx->opts->basic, SDAP_CHPASS_URI); - backup_urls = dp_opt_get_string(ctx->opts->basic, SDAP_CHPASS_BACKUP_URI); - if (!urls && !backup_urls && !dns_service_name) { - DEBUG(SSSDBG_TRACE_ALL, - "ldap_chpass_uri and ldap_chpass_dns_service_name not set, " - "using ldap_uri.\n"); - ctx->chpass_service = NULL; - } else { - ret = sdap_service_init(ctx, ctx->be, "LDAP_CHPASS", dns_service_name, - urls, backup_urls, &ctx->chpass_service); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Failed to initialize failover service!\n"); - goto done; - } + filter = sdap_get_access_filter(mem_ctx, filter); + if (filter == NULL) { + return ENOMEM; } + *_filter = filter; - *ops = &sdap_chpass_ops; - *pvt_data = ctx; - ret = EOK; - -done: - if (ret != EOK) { - talloc_free(ctx); - } - return ret; + return EOK; } -int sssm_ldap_access_init(struct be_ctx *bectx, - struct bet_ops **ops, - void **pvt_data) +static errno_t set_access_rules(TALLOC_CTX *mem_ctx, + struct sdap_access_ctx *access_ctx, + struct sdap_options *opts) { - int ret; - struct sdap_access_ctx *access_ctx; - const char *filter; + errno_t ret; + char **order_list = NULL; const char *order; - char **order_list; - int order_list_len; size_t c; - const char *dummy; - - access_ctx = talloc_zero(bectx, struct sdap_access_ctx); - if(access_ctx == NULL) { - ret = ENOMEM; - goto done; - } - ret = ldap_id_init_internal(bectx, ops, (void **)&access_ctx->id_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "ldap_id_init_internal failed.\n"); - goto done; - } + /* To make sure that in case of failure it's safe to be freed */ + access_ctx->filter = NULL; order = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic, SDAP_ACCESS_ORDER); @@ -347,73 +260,28 @@ int sssm_ldap_access_init(struct be_ctx *bectx, order = "filter"; } - ret = split_on_separator(access_ctx, order, ',', true, true, - &order_list, &order_list_len); + ret = get_access_order_list(mem_ctx, order, &order_list); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "split_on_separator failed.\n"); - goto done; - } - - ret = check_order_list_for_duplicates(order_list, false); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "check_order_list_for_duplicates failed.\n"); - goto done; - } - - if (order_list_len > LDAP_ACCESS_LAST) { DEBUG(SSSDBG_CRIT_FAILURE, - "Currently only [%d] different access rules are supported.\n", - LDAP_ACCESS_LAST); - ret = EINVAL; + "get_access_order_list failed: [%d][%s].\n", + ret, sss_strerror(ret)); goto done; } for (c = 0; order_list[c] != NULL; c++) { + if (strcasecmp(order_list[c], LDAP_ACCESS_FILTER_NAME) == 0) { access_ctx->access_rule[c] = LDAP_ACCESS_FILTER; - - filter = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic, - SDAP_ACCESS_FILTER); - if (filter == NULL) { - /* It's okay if this is NULL. In that case we will simply act - * like the 'deny' provider. - */ - DEBUG(SSSDBG_FATAL_FAILURE, - "Warning: LDAP access rule 'filter' is set, " - "but no ldap_access_filter configured. " - "All domain users will be denied access.\n"); - } else { - access_ctx->filter = sdap_get_access_filter(access_ctx, - filter); - if (access_ctx->filter == NULL) { - ret = ENOMEM; - goto done; - } + if (get_access_filter(mem_ctx, opts, &access_ctx->filter) != EOK) { + goto done; } + } else if (strcasecmp(order_list[c], LDAP_ACCESS_EXPIRE_NAME) == 0) { access_ctx->access_rule[c] = LDAP_ACCESS_EXPIRE; - - dummy = dp_opt_get_cstring(access_ctx->id_ctx->opts->basic, - SDAP_ACCOUNT_EXPIRE_POLICY); - if (dummy == NULL) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Warning: LDAP access rule 'expire' is set, " - "but no ldap_account_expire_policy configured. " - "All domain users will be denied access.\n"); - } else { - if (strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_SHADOW) != 0 && - strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_AD) != 0 && - strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_NDS) != 0 && - strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_RHDS) != 0 && - strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_IPA) != 0 && - strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_389DS) != 0) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Unsupported LDAP account expire policy [%s].\n", - dummy); - ret = EINVAL; - goto done; - } + if (check_expire_policy(opts) != EOK) { + goto done; } + } else if (strcasecmp(order_list[c], LDAP_ACCESS_SERVICE_NAME) == 0) { access_ctx->access_rule[c] = LDAP_ACCESS_SERVICE; } else if (strcasecmp(order_list[c], LDAP_ACCESS_HOST_NAME) == 0) { @@ -441,75 +309,327 @@ int sssm_ldap_access_init(struct be_ctx *bectx, access_ctx->access_rule[c] = LDAP_ACCESS_EMPTY; if (c == 0) { DEBUG(SSSDBG_FATAL_FAILURE, "Warning: access_provider=ldap set, " - "but ldap_access_order is empty. " - "All domain users will be denied access.\n"); + "but ldap_access_order is empty. " + "All domain users will be denied access.\n"); + } + +done: + talloc_free(order_list); + if (ret != EOK) { + talloc_zfree(access_ctx->filter); + } + return ret; +} + +static errno_t get_sdap_service(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct sdap_options *opts, + struct sdap_service **_sdap_service) +{ + errno_t ret; + const char *urls; + const char *backup_urls; + const char *dns_service_name; + struct sdap_service *sdap_service; + + urls = dp_opt_get_string(opts->basic, SDAP_URI); + backup_urls = dp_opt_get_string(opts->basic, SDAP_BACKUP_URI); + dns_service_name = dp_opt_get_string(opts->basic, SDAP_DNS_SERVICE_NAME); + if (dns_service_name != NULL) { + DEBUG(SSSDBG_CONF_SETTINGS, + "Service name for discovery set to %s\n", dns_service_name); + } + + ret = sdap_service_init(mem_ctx, be_ctx, "LDAP", + dns_service_name, + urls, + backup_urls, + &sdap_service); + if (ret != EOK) { + return ret; + } + + *_sdap_service = sdap_service; + return EOK; +} + +static bool should_call_gssapi_init(struct sdap_options *opts) +{ + const char *sasl_mech; + + sasl_mech = dp_opt_get_string(opts->basic, SDAP_SASL_MECH); + if (sasl_mech == NULL) { + return false; + } + + if (strcasecmp(sasl_mech, "GSSAPI") != 0) { + return false; + } + + if (dp_opt_get_bool(opts->basic, SDAP_KRB5_KINIT) == false) { + return false; + } + + return true; +} + +static errno_t ldap_init_misc(struct be_ctx *be_ctx, + struct sdap_options *options, + struct sdap_id_ctx *id_ctx) +{ + errno_t ret; + + if (should_call_gssapi_init(options)) { + ret = sdap_gssapi_init(id_ctx, options->basic, be_ctx, + id_ctx->conn->service, &id_ctx->krb5_service); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "sdap_gssapi_init failed [%d][%s].\n", + ret, sss_strerror(ret)); + return ret; + } + } + + ret = setup_tls_config(options->basic); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get TLS options [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + /* Setup the ID mapping object */ + ret = sdap_idmap_init(id_ctx, id_ctx, &options->idmap_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Could not initialize ID mapping. In case ID mapping properties " + "changed on the server, please remove the SSSD database\n"); + return ret; + } + + ret = ldap_id_setup_tasks(id_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup background tasks " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + + ret = sdap_setup_child(); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup sdap child [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + /* Setup SRV lookup plugin */ + ret = be_fo_set_dns_srv_lookup_plugin(be_ctx, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set SRV lookup plugin " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + + /* Setup periodical refresh of expired records */ + ret = sdap_refresh_init(be_ctx->refresh_ctx, id_ctx); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh will not work " + "[%d]: %s\n", ret, sss_strerror(ret)); + } + + return EOK; +} + +errno_t sssm_ldap_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct data_provider *provider, + const char *module_name, + void **_module_data) +{ + struct sdap_service *sdap_service; + struct ldap_init_ctx *init_ctx; + errno_t ret; + + init_ctx = talloc_zero(mem_ctx, struct ldap_init_ctx); + if (init_ctx == NULL) { + return ENOMEM; + } + + /* Always initialize options since it is needed everywhere. */ + ret = ldap_get_options(init_ctx, be_ctx->domain, be_ctx->cdb, + be_ctx->conf_path, &init_ctx->options); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize LDAP options " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + /* Always initialize id_ctx since it is needed everywhere. */ + ret = get_sdap_service(init_ctx, be_ctx, init_ctx->options, &sdap_service); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to initialize failover service " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + init_ctx->id_ctx = sdap_id_ctx_new(init_ctx, be_ctx, sdap_service); + if (init_ctx->id_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize LDAP ID context\n"); + ret = ENOMEM; + goto done; } - *ops = &sdap_access_ops; - *pvt_data = access_ctx; + init_ctx->id_ctx->opts = init_ctx->options; + + /* Setup miscellaneous things. */ + ret = ldap_init_misc(be_ctx, init_ctx->options, init_ctx->id_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to init LDAP module " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + /* Initialize auth_ctx only if one of the target is enabled. */ + if (dp_target_enabled(provider, module_name, DPT_AUTH, DPT_CHPASS)) { + ret = ldap_init_auth_ctx(init_ctx, be_ctx, init_ctx->id_ctx, + init_ctx->options, &init_ctx->auth_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create auth context " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + } + + *_module_data = init_ctx; ret = EOK; done: if (ret != EOK) { - talloc_free(access_ctx); + talloc_free(init_ctx); } + return ret; } -int sssm_ldap_sudo_init(struct be_ctx *be_ctx, - struct bet_ops **ops, - void **pvt_data) +errno_t sssm_ldap_id_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { -#ifdef BUILD_SUDO + struct ldap_init_ctx *init_ctx; struct sdap_id_ctx *id_ctx; - void *data; - int ret; - ret = ldap_id_init_internal(be_ctx, ops, &data); + init_ctx = talloc_get_type(module_data, struct ldap_init_ctx); + id_ctx = init_ctx->id_ctx; + + dp_set_method(dp_methods, DPM_ACCOUNT_HANDLER, + sdap_account_info_handler_send, sdap_account_info_handler_recv, id_ctx, + struct sdap_id_ctx, struct be_acct_req, struct dp_reply_std); + + dp_set_method(dp_methods, DPM_CHECK_ONLINE, + sdap_online_check_handler_send, sdap_online_check_handler_recv, id_ctx, + struct sdap_id_ctx, void, struct dp_reply_std); + + return EOK; +} + +errno_t sssm_ldap_auth_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) +{ + struct ldap_init_ctx *init_ctx; + struct sdap_auth_ctx *auth_ctx; + + init_ctx = talloc_get_type(module_data, struct ldap_init_ctx); + auth_ctx = init_ctx->auth_ctx; + + dp_set_method(dp_methods, DPM_AUTH_HANDLER, + sdap_pam_auth_handler_send, sdap_pam_auth_handler_recv, auth_ctx, + struct sdap_auth_ctx, struct pam_data, struct pam_data *); + + return EOK; +} + +errno_t sssm_ldap_chpass_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) +{ + struct ldap_init_ctx *init_ctx; + struct sdap_auth_ctx *auth_ctx; + errno_t ret; + + init_ctx = talloc_get_type(module_data, struct ldap_init_ctx); + auth_ctx = init_ctx->auth_ctx; + + ret = init_chpass_service(auth_ctx, be_ctx, init_ctx->options, + &auth_ctx->chpass_service); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Cannot init LDAP ID provider [%d]: %s\n", - ret, strerror(ret)); + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize chpass service " + "[%d]: %s\n", ret, sss_strerror(ret)); return ret; } - id_ctx = talloc_get_type(data, struct sdap_id_ctx); - if (!id_ctx) { - DEBUG(SSSDBG_CRIT_FAILURE, "No ID provider?\n"); - return EIO; - } + dp_set_method(dp_methods, DPM_AUTH_HANDLER, + sdap_pam_chpass_handler_send, sdap_pam_chpass_handler_recv, auth_ctx, + struct sdap_auth_ctx, struct pam_data, struct pam_data *); - return sdap_sudo_init(be_ctx, id_ctx, ops, pvt_data); -#else - DEBUG(SSSDBG_MINOR_FAILURE, "Sudo init handler called but SSSD is " - "built without sudo support, ignoring\n"); return EOK; -#endif } -int sssm_ldap_autofs_init(struct be_ctx *be_ctx, - struct bet_ops **ops, - void **pvt_data) +errno_t sssm_ldap_access_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { -#ifdef BUILD_AUTOFS - struct sdap_id_ctx *id_ctx; - void *data; - int ret; + struct ldap_init_ctx *init_ctx; + struct sdap_access_ctx *access_ctx; + errno_t ret; + + init_ctx = talloc_get_type(module_data, struct ldap_init_ctx); + + access_ctx = talloc_zero(mem_ctx, struct sdap_access_ctx); + if(access_ctx == NULL) { + ret = ENOMEM; + goto done; + } - ret = ldap_id_init_internal(be_ctx, ops, &data); + access_ctx->id_ctx = init_ctx->id_ctx; + + ret = set_access_rules(access_ctx, access_ctx, access_ctx->id_ctx->opts); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Cannot init LDAP ID provider [%d]: %s\n", - ret, strerror(ret)); - return ret; + DEBUG(SSSDBG_CRIT_FAILURE, + "set_access_rules failed: [%d][%s].\n", + ret, sss_strerror(ret)); + goto done; } - id_ctx = talloc_get_type(data, struct sdap_id_ctx); - if (!id_ctx) { - DEBUG(SSSDBG_CRIT_FAILURE, "No ID provider?\n"); - return EIO; + dp_set_method(dp_methods, DPM_ACCESS_HANDLER, + sdap_pam_access_handler_send, sdap_pam_access_handler_recv, access_ctx, + struct sdap_access_ctx, struct pam_data, struct pam_data *); + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(access_ctx); } - return sdap_autofs_init(be_ctx, id_ctx, ops, pvt_data); + return ret; +} + +errno_t sssm_ldap_autofs_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) +{ +#ifdef BUILD_AUTOFS + struct ldap_init_ctx *init_ctx; + + DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing LDAP autofs handler\n"); + init_ctx = talloc_get_type(module_data, struct ldap_init_ctx); + + return sdap_autofs_init(mem_ctx, be_ctx, init_ctx->id_ctx, dp_methods); #else DEBUG(SSSDBG_MINOR_FAILURE, "Autofs init handler called but SSSD is " "built without autofs support, ignoring\n"); @@ -517,9 +637,21 @@ int sssm_ldap_autofs_init(struct be_ctx *be_ctx, #endif } -static void sdap_shutdown(struct be_req *req) +errno_t sssm_ldap_sudo_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - /* TODO: Clean up any internal data */ - sdap_handler_done(req, DP_ERR_OK, EOK, NULL); -} +#ifdef BUILD_SUDO + struct ldap_init_ctx *init_ctx; + + DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing LDAP sudo handler\n"); + init_ctx = talloc_get_type(module_data, struct ldap_init_ctx); + return sdap_sudo_init(mem_ctx, be_ctx, init_ctx->id_ctx, dp_methods); +#else + DEBUG(SSSDBG_MINOR_FAILURE, "Sudo init handler called but SSSD is " + "built without sudo support, ignoring\n"); + return EOK; +#endif +} diff --git a/src/providers/ldap/sdap_access.h b/src/providers/ldap/sdap_access.h index 93ddfd5e5..049daced6 100644 --- a/src/providers/ldap/sdap_access.h +++ b/src/providers/ldap/sdap_access.h @@ -75,6 +75,17 @@ struct sdap_access_ctx { int access_rule[LDAP_ACCESS_LAST + 1]; }; +struct tevent_req * +sdap_pam_access_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_access_ctx *access_ctx, + struct pam_data *pd, + struct dp_req_params *params); + +errno_t +sdap_pam_access_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data); + struct tevent_req * sdap_access_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, diff --git a/src/providers/ldap/sdap_autofs.c b/src/providers/ldap/sdap_autofs.c index f65b6ea61..c02c04d5c 100644 --- a/src/providers/ldap/sdap_autofs.c +++ b/src/providers/ldap/sdap_autofs.c @@ -34,103 +34,6 @@ #include "db/sysdb_autofs.h" #include "util/util.h" -static void -sdap_autofs_shutdown(struct be_req *req) -{ - sdap_handler_done(req, DP_ERR_OK, EOK, NULL); -} - -void sdap_autofs_handler(struct be_req *be_req); - -/* Autofs Handler */ -struct bet_ops sdap_autofs_ops = { - .handler = sdap_autofs_handler, - .finalize = sdap_autofs_shutdown -}; - -int sdap_autofs_init(struct be_ctx *be_ctx, - struct sdap_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data) -{ - int ret; - - DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing autofs LDAP back end\n"); - - *ops = &sdap_autofs_ops; - *pvt_data = id_ctx; - - ret = ldap_get_autofs_options(id_ctx, be_ctx->cdb, - be_ctx->conf_path, id_ctx->opts); - if (ret != EOK) { - return ret; - } - - return ret; -} - -static struct tevent_req * -sdap_autofs_get_map_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct sdap_id_ctx *ctx, - const char *map_name); - -static void sdap_autofs_handler_done(struct tevent_req *req); - -void sdap_autofs_handler(struct be_req *be_req) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - struct sdap_id_ctx *id_ctx; - struct be_autofs_req *autofs_req; - struct tevent_req *req; - const char *master_map; - int ret = EOK; - - DEBUG(SSSDBG_TRACE_INTERNAL, "sdap autofs handler called\n"); - - id_ctx = talloc_get_type(be_ctx->bet_info[BET_AUTOFS].pvt_bet_data, - struct sdap_id_ctx); - - if (be_is_offline(id_ctx->be)) { - return sdap_handler_done(be_req, DP_ERR_OFFLINE, EAGAIN, "Offline"); - } - - autofs_req = talloc_get_type(be_req_get_data(be_req), struct be_autofs_req); - - DEBUG(SSSDBG_FUNC_DATA, "Requested refresh for: %s\n", - autofs_req->mapname ? autofs_req->mapname : "\n"); - - if (autofs_req->mapname != NULL) { - master_map = dp_opt_get_string(id_ctx->opts->basic, - SDAP_AUTOFS_MAP_MASTER_NAME); - if (strcmp(master_map, autofs_req->mapname) == 0) { - autofs_req->invalidate = true; - DEBUG(SSSDBG_FUNC_DATA, "Refresh of automount master map triggered: %s\n", - autofs_req->mapname); - } - } - - if (autofs_req->invalidate) { - ret = sysdb_invalidate_autofs_maps(id_ctx->be->domain); - if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, "Could not invalidate autofs maps, " - "backend might return stale entries\n"); - } - } - - req = sdap_autofs_get_map_send(be_req, be_ctx->ev, - id_ctx, autofs_req->mapname); - if (!req) { - ret = ENOMEM; - goto fail; - } - tevent_req_set_callback(req, sdap_autofs_handler_done, be_req); - - return; -fail: - be_req_terminate(be_req, DP_ERR_FATAL, ret, NULL); -} - struct autofs_get_map_state { struct tevent_context *ev; struct sdap_id_ctx *ctx; @@ -298,15 +201,121 @@ sdap_autofs_get_map_recv(struct tevent_req *req, int *dp_error_out) return EOK; } -static void -sdap_autofs_handler_done(struct tevent_req *req) +struct sdap_autofs_handler_state { + struct dp_reply_std reply; +}; + +static void sdap_autofs_handler_done(struct tevent_req *subreq); + +struct tevent_req * +sdap_autofs_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_id_ctx *id_ctx, + struct dp_autofs_data *data, + struct dp_req_params *params) { - struct be_req *be_req = - tevent_req_callback_data(req, struct be_req); - int dperr; + struct sdap_autofs_handler_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + const char *master_map; + errno_t ret; - ret = sdap_autofs_get_map_recv(req, &dperr); - sdap_handler_done(be_req, dperr, ret, strerror(ret)); + req = tevent_req_create(mem_ctx, &state, struct sdap_autofs_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + DEBUG(SSSDBG_FUNC_DATA, "Requested refresh for: %s\n", data->mapname); + + master_map = dp_opt_get_string(id_ctx->opts->basic, + SDAP_AUTOFS_MAP_MASTER_NAME); + if (strcmp(master_map, data->mapname) == 0) { + DEBUG(SSSDBG_FUNC_DATA, "Refresh of automount master map triggered: " + "%s\n", data->mapname); + + ret = sysdb_invalidate_autofs_maps(id_ctx->be->domain); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Could not invalidate autofs maps, " + "backend might return stale entries\n"); + } + } + + subreq = sdap_autofs_get_map_send(mem_ctx, params->ev, + id_ctx, data->mapname); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send request for %s.\n", + data->mapname); + ret = ENOMEM; + goto immediately; + } + + tevent_req_set_callback(subreq, sdap_autofs_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 sdap_autofs_handler_done(struct tevent_req *subreq) +{ + struct sdap_autofs_handler_state *state; + struct tevent_req *req; + int dp_error; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_autofs_handler_state); + + ret = sdap_autofs_get_map_recv(subreq, &dp_error); + 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 +sdap_autofs_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data) +{ + struct sdap_autofs_handler_state *state = NULL; + + state = tevent_req_data(req, struct sdap_autofs_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *data = state->reply; + + return EOK; +} + +errno_t sdap_autofs_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct sdap_id_ctx *id_ctx, + struct dp_method *dp_methods) +{ + errno_t ret; + + DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing autofs LDAP back end\n"); + + ret = ldap_get_autofs_options(id_ctx, be_ctx->cdb, be_ctx->conf_path, + id_ctx->opts); + if (ret != EOK) { + return ret; + } + + dp_set_method(dp_methods, DPM_AUTOFS_HANDLER, + sdap_autofs_handler_send, sdap_autofs_handler_recv, id_ctx, + struct sdap_id_ctx, struct dp_autofs_data, struct dp_reply_std); + + return EOK; +} diff --git a/src/providers/ldap/sdap_autofs.h b/src/providers/ldap/sdap_autofs.h index 0369e2645..593d8c94f 100644 --- a/src/providers/ldap/sdap_autofs.h +++ b/src/providers/ldap/sdap_autofs.h @@ -25,10 +25,10 @@ #ifndef _SDAP_AUTOFS_H_ #define _SDAP_AUTOFS_H_ -int sdap_autofs_init(struct be_ctx *be_ctx, - struct sdap_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data); +errno_t sdap_autofs_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct sdap_id_ctx *id_ctx, + struct dp_method *dp_methods); struct tevent_req * sdap_autofs_setautomntent_send(TALLOC_CTX *memctx, diff --git a/src/providers/ldap/sdap_idmap.c b/src/providers/ldap/sdap_idmap.c index 4e322124c..b5dfc6cef 100644 --- a/src/providers/ldap/sdap_idmap.c +++ b/src/providers/ldap/sdap_idmap.c @@ -542,7 +542,7 @@ bool sdap_idmap_domain_has_algorithmic_mapping(struct sdap_idmap_ctx *ctx, TALLOC_CTX *tmp_ctx = NULL; if (dp_opt_get_bool(ctx->id_ctx->opts->basic, SDAP_ID_MAPPING) - && 0 == strcmp("ldap", ctx->id_ctx->be->bet_info[BET_ID].mod_name)) { + && dp_target_enabled(ctx->id_ctx->be->provider, "ldap", DPT_ID)) { return true; } diff --git a/src/providers/ldap/sdap_online_check.c b/src/providers/ldap/sdap_online_check.c new file mode 100644 index 000000000..f721a5f45 --- /dev/null +++ b/src/providers/ldap/sdap_online_check.c @@ -0,0 +1,249 @@ +/* + Authors: + Pavel Březina + + Copyright (C) 2016 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 +#include "util/util.h" +#include "providers/backend.h" +#include "providers/ldap/sdap_async.h" +#include "providers/ldap/ldap_common.h" + +struct sdap_online_check_state { + struct sdap_id_ctx *id_ctx; + struct be_ctx *be_ctx; +}; + +static void sdap_online_check_connect_done(struct tevent_req *subreq); +static void sdap_online_check_reinit_done(struct tevent_req *subreq); + +static struct tevent_req *sdap_online_check_send(TALLOC_CTX *mem_ctx, + struct sdap_id_ctx *id_ctx) +{ + struct sdap_online_check_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + struct be_ctx *be_ctx; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct sdap_online_check_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->id_ctx = id_ctx; + state->be_ctx = be_ctx = id_ctx->be; + + subreq = sdap_cli_connect_send(state, be_ctx->ev, id_ctx->opts, be_ctx, + id_ctx->conn->service, false, + CON_TLS_DFL, false); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } + + tevent_req_set_callback(subreq, sdap_online_check_connect_done, req); + + return req; + +immediately: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, be_ctx->ev); + + return req; +} + +static void sdap_online_check_connect_done(struct tevent_req *subreq) +{ + struct sdap_online_check_state *state; + struct sdap_server_opts *srv_opts; + struct sdap_id_ctx *id_ctx; + struct tevent_req *req; + bool can_retry; + bool reinit = false; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_online_check_state); + + id_ctx = state->id_ctx; + + ret = sdap_cli_connect_recv(subreq, state, &can_retry, NULL, &srv_opts); + talloc_zfree(subreq); + if (ret != EOK) { + if (can_retry == false) { + ret = ERR_OFFLINE; + } + + goto done; + } else { + if (id_ctx->srv_opts == NULL) { + srv_opts->max_user_value = 0; + srv_opts->max_group_value = 0; + srv_opts->max_service_value = 0; + srv_opts->max_sudo_value = 0; + } else if (strcmp(srv_opts->server_id, id_ctx->srv_opts->server_id) == 0 + && srv_opts->supports_usn + && id_ctx->srv_opts->last_usn > srv_opts->last_usn) { + id_ctx->srv_opts->max_user_value = 0; + id_ctx->srv_opts->max_group_value = 0; + id_ctx->srv_opts->max_service_value = 0; + id_ctx->srv_opts->max_sudo_value = 0; + id_ctx->srv_opts->last_usn = srv_opts->last_usn; + + reinit = true; + } + + sdap_steal_server_opts(id_ctx, &srv_opts); + } + + if (reinit) { + DEBUG(SSSDBG_TRACE_FUNC, "Server reinitialization detected. " + "Cleaning cache.\n"); + subreq = sdap_reinit_cleanup_send(state, state->be_ctx, id_ctx); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to perform reinitialization " + "clean up.\n"); + /* not fatal */ + goto done; + } + + tevent_req_set_callback(subreq, sdap_online_check_reinit_done, req); + return; + } + + ret = EOK; + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static void sdap_online_check_reinit_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + + ret = sdap_reinit_cleanup_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to perform reinitialization " + "clean up [%d]: %s\n", ret, strerror(ret)); + /* not fatal */ + } else { + DEBUG(SSSDBG_TRACE_FUNC, "Reinitialization clean up completed\n"); + } + + tevent_req_done(req); +} + +static errno_t sdap_online_check_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +struct sdap_online_check_handler_state { + struct dp_reply_std reply; +}; + +static void sdap_online_check_handler_done(struct tevent_req *subreq); + +struct tevent_req * +sdap_online_check_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_id_ctx *id_ctx, + void *data, + struct dp_req_params *params) +{ + struct sdap_online_check_handler_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct sdap_online_check_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + subreq = sdap_online_check_send(state, id_ctx); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } + + tevent_req_set_callback(subreq, sdap_online_check_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 sdap_online_check_handler_done(struct tevent_req *subreq) +{ + struct sdap_online_check_handler_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_online_check_handler_state); + + ret = sdap_online_check_recv(subreq); + talloc_zfree(subreq); + + /* TODO For backward compatibility we always return EOK to DP now. */ + dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ret, NULL); + tevent_req_done(req); +} + +errno_t sdap_online_check_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data) +{ + struct sdap_online_check_handler_state *state = NULL; + + state = tevent_req_data(req, struct sdap_online_check_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *data = state->reply; + + return EOK; +} diff --git a/src/providers/ldap/sdap_sudo.c b/src/providers/ldap/sdap_sudo.c index 2ed97b9aa..bf73f7b45 100644 --- a/src/providers/ldap/sdap_sudo.c +++ b/src/providers/ldap/sdap_sudo.c @@ -29,13 +29,118 @@ #include "providers/ldap/sdap_sudo.h" #include "db/sysdb_sudo.h" -static void sdap_sudo_handler(struct be_req *breq); - -struct bet_ops sdap_sudo_ops = { - .handler = sdap_sudo_handler, - .finalize = NULL +struct sdap_sudo_handler_state { + uint32_t type; + struct dp_reply_std reply; }; +static void sdap_sudo_handler_done(struct tevent_req *subreq); + +static struct tevent_req * +sdap_sudo_handler_send(TALLOC_CTX *mem_ctx, + struct sdap_sudo_ctx *sudo_ctx, + struct dp_sudo_data *data, + struct dp_req_params *params) +{ + struct sdap_sudo_handler_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct sdap_sudo_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->type = data->type; + + switch (data->type) { + case BE_REQ_SUDO_FULL: + DEBUG(SSSDBG_TRACE_FUNC, "Issuing a full refresh of sudo rules\n"); + subreq = sdap_sudo_full_refresh_send(state, sudo_ctx); + break; + case BE_REQ_SUDO_RULES: + DEBUG(SSSDBG_TRACE_FUNC, "Issuing a refresh of specific sudo rules\n"); + subreq = sdap_sudo_rules_refresh_send(state, sudo_ctx, data->rules); + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type: %d\n", data->type); + ret = EINVAL; + goto immediately; + } + + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send request: %d\n", data->type); + ret = ENOMEM; + goto immediately; + } + + tevent_req_set_callback(subreq, sdap_sudo_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 sdap_sudo_handler_done(struct tevent_req *subreq) +{ + struct sdap_sudo_handler_state *state; + struct tevent_req *req; + int dp_error; + bool deleted; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_sudo_handler_state); + + switch (state->type) { + case BE_REQ_SUDO_FULL: + ret = sdap_sudo_full_refresh_recv(subreq, &dp_error); + talloc_zfree(subreq); + break; + case BE_REQ_SUDO_RULES: + ret = sdap_sudo_rules_refresh_recv(subreq, &dp_error, &deleted); + talloc_zfree(subreq); + if (ret == EOK && deleted == true) { + ret = ENOENT; + } + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type: %d\n", state->type); + dp_error = DP_ERR_FATAL; + ret = ERR_INTERNAL; + break; + } + + /* 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); +} + +static errno_t +sdap_sudo_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data) +{ + struct sdap_sudo_handler_state *state = NULL; + + state = tevent_req_data(req, struct sdap_sudo_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *data = state->reply; + + return EOK; +} + static void sdap_sudo_online_cb(void *pvt) { struct sdap_sudo_ctx *sudo_ctx; @@ -51,43 +156,40 @@ static void sdap_sudo_online_cb(void *pvt) sudo_ctx->run_hostinfo = true; } -int sdap_sudo_init(struct be_ctx *be_ctx, - struct sdap_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data) +errno_t sdap_sudo_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct sdap_id_ctx *id_ctx, + struct dp_method *dp_methods) { - struct sdap_sudo_ctx *sudo_ctx = NULL; + struct sdap_sudo_ctx *sudo_ctx; int ret; DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing sudo LDAP back end\n"); - sudo_ctx = talloc_zero(be_ctx, struct sdap_sudo_ctx); + sudo_ctx = talloc_zero(mem_ctx, struct sdap_sudo_ctx); if (sudo_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc() failed\n"); return ENOMEM; } sudo_ctx->id_ctx = id_ctx; - *ops = &sdap_sudo_ops; - *pvt_data = sudo_ctx; - ret = ldap_get_sudo_options(be_ctx->cdb, - be_ctx->conf_path, id_ctx->opts, + ret = ldap_get_sudo_options(be_ctx->cdb, be_ctx->conf_path, id_ctx->opts, &sudo_ctx->use_host_filter, &sudo_ctx->include_regexp, &sudo_ctx->include_netgroups); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot get SUDO options [%d]: %s\n", - ret, strerror(ret)); + ret, sss_strerror(ret)); goto done; } if (sudo_ctx->use_host_filter) { - ret = be_add_online_cb(sudo_ctx, sudo_ctx->id_ctx->be, - sdap_sudo_online_cb, sudo_ctx, NULL); + ret = be_add_online_cb(sudo_ctx, be_ctx, sdap_sudo_online_cb, + sudo_ctx, NULL); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Unable to install online callback " - "[%d]: %s\n", ret, sss_strerror(ret)); + "[%d]: %s\n", ret, sss_strerror(ret)); goto done; } @@ -95,15 +197,18 @@ int sdap_sudo_init(struct be_ctx *be_ctx, sudo_ctx->run_hostinfo = true; } - ret = sdap_sudo_ptask_setup(sudo_ctx->id_ctx->be, sudo_ctx); + ret = sdap_sudo_ptask_setup(be_ctx, sudo_ctx); if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - "Unable to setup periodical refresh of sudo rules [%d]: %s\n", - ret, strerror(ret)); + DEBUG(SSSDBG_OP_FAILURE, "Unable to setup periodical refresh of " + "sudo rules [%d]: %s\n", ret, sss_strerror(ret)); /* periodical updates will not work, but specific-rule update * is no affected by this, therefore we don't have to fail here */ } + dp_set_method(dp_methods, DPM_SUDO_HANDLER, + sdap_sudo_handler_send, sdap_sudo_handler_recv, sudo_ctx, + struct sdap_sudo_ctx, struct dp_sudo_data, struct dp_reply_std); + ret = EOK; done: @@ -113,85 +218,3 @@ done: return ret; } - -static void sdap_sudo_reply(struct tevent_req *req) -{ - struct be_req *be_req = NULL; - struct be_sudo_req *sudo_req = NULL; - int dp_error; - bool deleted; - int ret; - - be_req = tevent_req_callback_data(req, struct be_req); - sudo_req = talloc_get_type(be_req_get_data(be_req), struct be_sudo_req); - - switch (sudo_req->type) { - case BE_REQ_SUDO_FULL: - ret = sdap_sudo_full_refresh_recv(req, &dp_error); - break; - case BE_REQ_SUDO_RULES: - ret = sdap_sudo_rules_refresh_recv(req, &dp_error, &deleted); - if (ret == EOK && deleted == true) { - ret = ENOENT; - } - break; - default: - DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type: %d\n", - sudo_req->type); - dp_error = DP_ERR_FATAL; - ret = ERR_INTERNAL; - break; - } - - talloc_zfree(req); - sdap_handler_done(be_req, dp_error, ret, strerror(ret)); -} - -static void sdap_sudo_handler(struct be_req *be_req) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); - struct tevent_req *req = NULL; - struct be_sudo_req *sudo_req = NULL; - struct sdap_sudo_ctx *sudo_ctx = NULL; - int ret = EOK; - - if (be_is_offline(be_ctx)) { - sdap_handler_done(be_req, DP_ERR_OFFLINE, EAGAIN, "Offline"); - return; - } - - sudo_ctx = talloc_get_type(be_ctx->bet_info[BET_SUDO].pvt_bet_data, - struct sdap_sudo_ctx); - - sudo_req = talloc_get_type(be_req_get_data(be_req), struct be_sudo_req); - - switch (sudo_req->type) { - case BE_REQ_SUDO_FULL: - DEBUG(SSSDBG_TRACE_FUNC, "Issuing a full refresh of sudo rules\n"); - req = sdap_sudo_full_refresh_send(be_req, sudo_ctx); - break; - case BE_REQ_SUDO_RULES: - DEBUG(SSSDBG_TRACE_FUNC, "Issuing a refresh of specific sudo rules\n"); - req = sdap_sudo_rules_refresh_send(be_req, sudo_ctx, sudo_req->rules); - break; - default: - DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type: %d\n", - sudo_req->type); - ret = EINVAL; - goto fail; - } - - if (req == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send request: %d\n", - sudo_req->type); - ret = ENOMEM; - goto fail; - } - - tevent_req_set_callback(req, sdap_sudo_reply, be_req); - - return; - -fail: - sdap_handler_done(be_req, DP_ERR_FATAL, ret, NULL); -} diff --git a/src/providers/ldap/sdap_sudo.h b/src/providers/ldap/sdap_sudo.h index fccd64c2f..0e732abf4 100644 --- a/src/providers/ldap/sdap_sudo.h +++ b/src/providers/ldap/sdap_sudo.h @@ -40,10 +40,10 @@ struct sdap_sudo_ctx { /* Common functions from ldap_sudo.c */ -int sdap_sudo_init(struct be_ctx *be_ctx, - struct sdap_id_ctx *id_ctx, - struct bet_ops **ops, - void **pvt_data); +errno_t sdap_sudo_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct sdap_id_ctx *id_ctx, + struct dp_method *dp_methods); /* sdap async interface */ struct tevent_req *sdap_sudo_refresh_send(TALLOC_CTX *mem_ctx, diff --git a/src/providers/proxy/proxy.h b/src/providers/proxy/proxy.h index 65479a448..11c85c54e 100644 --- a/src/providers/proxy/proxy.h +++ b/src/providers/proxy/proxy.h @@ -137,15 +137,34 @@ struct pc_init_ctx { struct sbus_connection *conn; }; +//int proxy_client_init(struct sbus_connection *conn, void *data); + #define PROXY_CHILD_PIPE "private/proxy_child" #define DEFAULT_BUFSIZE 4096 #define MAX_BUF_SIZE 1024*1024 /* max 1MiB */ /* From proxy_id.c */ -void proxy_get_account_info(struct be_req *breq); +struct tevent_req * +proxy_account_info_handler_send(TALLOC_CTX *mem_ctx, + struct proxy_id_ctx *id_ctx, + struct be_acct_req *data, + struct dp_req_params *params); + +errno_t proxy_account_info_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data); /* From proxy_auth.c */ -void proxy_pam_handler(struct be_req *req); +struct tevent_req * +proxy_pam_handler_send(TALLOC_CTX *mem_ctx, + struct proxy_auth_ctx *proxy_auth_ctx, + struct pam_data *pd, + struct dp_req_params *params); + +errno_t +proxy_pam_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data); /* From proxy_netgroup.c */ errno_t get_netgroup(struct proxy_id_ctx *ctx, @@ -167,4 +186,6 @@ errno_t enum_services(struct proxy_id_ctx *ctx, struct sysdb_ctx *sysdb, struct sss_domain_info *dom); +int proxy_client_init(struct sbus_connection *conn, void *data); + #endif /* __PROXY_H__ */ diff --git a/src/providers/proxy/proxy_auth.c b/src/providers/proxy/proxy_auth.c index edf058edc..6e7139aaa 100644 --- a/src/providers/proxy/proxy_auth.c +++ b/src/providers/proxy/proxy_auth.c @@ -24,75 +24,6 @@ #include "providers/proxy/proxy.h" -struct proxy_client_ctx { - struct be_req *be_req; - struct proxy_auth_ctx *auth_ctx; -}; - -static struct tevent_req *proxy_child_send(TALLOC_CTX *mem_ctx, - struct proxy_auth_ctx *ctx, - struct be_req *be_req); -static void proxy_child_done(struct tevent_req *child_req); -void proxy_pam_handler(struct be_req *req) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(req); - struct pam_data *pd; - struct proxy_auth_ctx *ctx; - struct tevent_req *child_req = NULL; - struct proxy_client_ctx *client_ctx; - - pd = talloc_get_type(be_req_get_data(req), struct pam_data); - - switch (pd->cmd) { - case SSS_PAM_AUTHENTICATE: - ctx = talloc_get_type(be_ctx->bet_info[BET_AUTH].pvt_bet_data, - struct proxy_auth_ctx); - break; - case SSS_PAM_CHAUTHTOK: - case SSS_PAM_CHAUTHTOK_PRELIM: - ctx = talloc_get_type(be_ctx->bet_info[BET_CHPASS].pvt_bet_data, - struct proxy_auth_ctx); - break; - case SSS_PAM_ACCT_MGMT: - ctx = talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data, - struct proxy_auth_ctx); - break; - case SSS_PAM_SETCRED: - case SSS_PAM_OPEN_SESSION: - case SSS_PAM_CLOSE_SESSION: - pd->pam_status = PAM_SUCCESS; - be_req_terminate(req, DP_ERR_OK, EOK, NULL); - return; - default: - DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported PAM task.\n"); - pd->pam_status = PAM_MODULE_UNKNOWN; - be_req_terminate(req, DP_ERR_OK, EINVAL, "Unsupported PAM task"); - return; - } - - client_ctx = talloc(req, struct proxy_client_ctx); - if (client_ctx == NULL) { - be_req_terminate(req, DP_ERR_FATAL, ENOMEM, NULL); - return; - } - client_ctx->auth_ctx = ctx; - client_ctx->be_req = req; - - /* Queue the request and spawn a child if there - * is an available slot. - */ - child_req = proxy_child_send(req, ctx, req); - if (child_req == NULL) { - /* Could not queue request - * Return an error - */ - be_req_terminate(req, DP_ERR_FATAL, EINVAL, "Could not queue request\n"); - return; - } - tevent_req_set_callback(child_req, proxy_child_done, client_ctx); - return; -} - struct pc_init_ctx; static int proxy_child_destructor(TALLOC_CTX *ctx) @@ -122,7 +53,7 @@ static struct tevent_req *proxy_child_init_send(TALLOC_CTX *mem_ctx, static void proxy_child_init_done(struct tevent_req *subreq); static struct tevent_req *proxy_child_send(TALLOC_CTX *mem_ctx, struct proxy_auth_ctx *auth_ctx, - struct be_req *be_req) + struct pam_data *pd) { struct tevent_req *req; struct tevent_req *subreq; @@ -138,9 +69,8 @@ static struct tevent_req *proxy_child_send(TALLOC_CTX *mem_ctx, return NULL; } - state->be_req = be_req; state->auth_ctx = auth_ctx; - state->pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); + state->pd = pd; /* Find an available key */ key.type = HASH_KEY_ULONG; @@ -731,69 +661,6 @@ static int proxy_child_recv(struct tevent_req *req, return EOK; } -static void proxy_child_done(struct tevent_req *req) -{ - struct proxy_client_ctx *client_ctx = - tevent_req_callback_data(req, struct proxy_client_ctx); - struct be_ctx *be_ctx = be_req_get_be_ctx(client_ctx->be_req); - struct pam_data *pd = NULL; - const char *password; - int ret; - struct tevent_immediate *imm; - - ret = proxy_child_recv(req, client_ctx, &pd); - talloc_zfree(req); - - /* Start the next auth in the queue, if any */ - client_ctx->auth_ctx->running--; - imm = tevent_create_immediate(be_ctx->ev); - if (imm == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "tevent_create_immediate failed.\n"); - /* We'll still finish the current request, but we're - * likely to have problems if there are queued events - * if we've gotten into this state. - * Hopefully this is impossible, since freeing req - * above should guarantee that we have enough memory - * to create this immediate event. - */ - } else { - tevent_schedule_immediate(imm, be_ctx->ev, - run_proxy_child_queue, - client_ctx->auth_ctx); - } - - if (ret != EOK) { - /* Pam child failed */ - be_req_terminate(client_ctx->be_req, DP_ERR_FATAL, ret, - "PAM child failed"); - return; - } - - /* Check if we need to save the cached credentials */ - if ((pd->cmd == SSS_PAM_AUTHENTICATE || pd->cmd == SSS_PAM_CHAUTHTOK) && - (pd->pam_status == PAM_SUCCESS) && be_ctx->domain->cache_credentials) { - - ret = sss_authtok_get_password(pd->authtok, &password, NULL); - if (ret) { - /* password caching failures are not fatal errors */ - DEBUG(SSSDBG_OP_FAILURE, "Failed to cache password\n"); - goto done; - } - - ret = sysdb_cache_password(be_ctx->domain, pd->user, password); - - /* password caching failures are not fatal errors */ - /* so we just log it any return */ - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Failed to cache password (%d)[%s]!?\n", - ret, strerror(ret)); - } - } - -done: - be_req_terminate(client_ctx->be_req, DP_ERR_OK, EOK, NULL); -} - static void run_proxy_child_queue(struct tevent_context *ev, struct tevent_immediate *imm, void *pvt) @@ -840,3 +707,144 @@ static void run_proxy_child_queue(struct tevent_context *ev, state->running = true; } } + +struct proxy_pam_handler_state { + struct pam_data *pd; + struct proxy_auth_ctx *auth_ctx; + struct be_ctx *be_ctx; +}; + +static void proxy_pam_handler_done(struct tevent_req *subreq); + +struct tevent_req * +proxy_pam_handler_send(TALLOC_CTX *mem_ctx, + struct proxy_auth_ctx *proxy_auth_ctx, + struct pam_data *pd, + struct dp_req_params *params) +{ + struct proxy_pam_handler_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + + req = tevent_req_create(mem_ctx, &state, struct proxy_pam_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + state->pd = pd; + state->auth_ctx = proxy_auth_ctx; + state->be_ctx = params->be_ctx; + + switch (pd->cmd) { + case SSS_PAM_AUTHENTICATE: + case SSS_PAM_CHAUTHTOK: + case SSS_PAM_CHAUTHTOK_PRELIM: + case SSS_PAM_ACCT_MGMT: + /* Queue the request and spawn a child if there is an available slot. */ + subreq = proxy_child_send(state, proxy_auth_ctx, state->pd); + if (subreq == NULL) { + pd->pam_status = PAM_SYSTEM_ERR; + goto immediately; + } + tevent_req_set_callback(subreq, proxy_pam_handler_done, req); + break; + case SSS_PAM_SETCRED: + case SSS_PAM_OPEN_SESSION: + case SSS_PAM_CLOSE_SESSION: + pd->pam_status = PAM_SUCCESS; + goto immediately; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported PAM task.\n"); + pd->pam_status = PAM_MODULE_UNKNOWN; + goto immediately; + } + + return req; + +immediately: + /* 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 proxy_pam_handler_done(struct tevent_req *subreq) +{ + struct proxy_pam_handler_state *state; + struct tevent_immediate *imm; + struct tevent_req *req; + const char *password; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct proxy_pam_handler_state); + + ret = proxy_child_recv(subreq, state, &state->pd); + talloc_zfree(subreq); + if (ret != EOK) { + state->pd->pam_status = PAM_SYSTEM_ERR; + goto done; + } + + /* Start the next auth in the queue, if any */ + state->auth_ctx->running--; + imm = tevent_create_immediate(state->be_ctx->ev); + if (imm == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_create_immediate failed.\n"); + /* We'll still finish the current request, but we're + * likely to have problems if there are queued events + * if we've gotten into this state. + * Hopefully this is impossible, since freeing req + * above should guarantee that we have enough memory + * to create this immediate event. + */ + } else { + tevent_schedule_immediate(imm, state->be_ctx->ev, + run_proxy_child_queue, + state->auth_ctx); + } + + /* Check if we need to save the cached credentials */ + if ((state->pd->cmd == SSS_PAM_AUTHENTICATE || state->pd->cmd == SSS_PAM_CHAUTHTOK) + && (state->pd->pam_status == PAM_SUCCESS) && state->be_ctx->domain->cache_credentials) { + + ret = sss_authtok_get_password(state->pd->authtok, &password, NULL); + if (ret) { + /* password caching failures are not fatal errors */ + DEBUG(SSSDBG_OP_FAILURE, "Failed to cache password\n"); + goto done; + } + + ret = sysdb_cache_password(state->be_ctx->domain, state->pd->user, password); + + /* password caching failures are not fatal errors */ + /* so we just log it any return */ + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to cache password (%d)[%s]!?\n", + ret, sss_strerror(ret)); + } + } + +done: + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); +} + +errno_t +proxy_pam_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data) +{ + struct proxy_pam_handler_state *state = NULL; + + state = tevent_req_data(req, struct proxy_pam_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_data = talloc_steal(mem_ctx, state->pd); + + return EOK; +} + diff --git a/src/providers/proxy/proxy_client.c b/src/providers/proxy/proxy_client.c new file mode 100644 index 000000000..fc1735f2a --- /dev/null +++ b/src/providers/proxy/proxy_client.c @@ -0,0 +1,192 @@ +/* + SSSD + + proxy_init.c + + Authors: + Stephen Gallagher + + Copyright (C) 2010 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 "config.h" + +#include "util/sss_format.h" +#include "providers/proxy/proxy.h" + +static int client_registration(struct sbus_request *dbus_req, void *data); + +static struct data_provider_iface proxy_methods = { + { &data_provider_iface_meta, 0 }, + .RegisterService = client_registration, + .pamHandler = NULL, + .sudoHandler = NULL, + .autofsHandler = NULL, + .hostHandler = NULL, + .getDomains = NULL, + .getAccountInfo = NULL, +}; + +struct proxy_client { + struct proxy_auth_ctx *proxy_auth_ctx; + struct sbus_connection *conn; + struct tevent_timer *timeout; + bool initialized; +}; + +static int client_registration(struct sbus_request *dbus_req, void *data) +{ + dbus_uint16_t version = DATA_PROVIDER_VERSION; + struct sbus_connection *conn; + struct proxy_client *proxy_cli; + dbus_uint16_t cli_ver; + uint32_t cli_id; + int hret; + hash_key_t key; + hash_value_t value; + struct tevent_req *req; + struct proxy_child_ctx *child_ctx; + struct pc_init_ctx *init_ctx; + int ret; + + conn = dbus_req->conn; + proxy_cli = talloc_get_type(data, struct proxy_client); + if (!proxy_cli) { + DEBUG(SSSDBG_FATAL_FAILURE, "Connection holds no valid init data\n"); + return EINVAL; + } + + /* First thing, cancel the timeout */ + DEBUG(SSSDBG_CONF_SETTINGS, + "Cancel proxy client ID timeout [%p]\n", proxy_cli->timeout); + talloc_zfree(proxy_cli->timeout); + + if (!sbus_request_parse_or_finish(dbus_req, + DBUS_TYPE_UINT16, &cli_ver, + DBUS_TYPE_UINT32, &cli_id, + DBUS_TYPE_INVALID)) { + sbus_disconnect(conn); + return EOK; /* handled */ + } + + DEBUG(SSSDBG_FUNC_DATA, "Proxy client [%"PRIu32"] connected\n", cli_id); + + /* Check the hash table */ + key.type = HASH_KEY_ULONG; + key.ul = cli_id; + if (!hash_has_key(proxy_cli->proxy_auth_ctx->request_table, &key)) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unknown child ID. Killing the connection\n"); + sbus_disconnect(proxy_cli->conn); + return EIO; + } + + /* reply that all is ok */ + ret = sbus_request_return_and_finish(dbus_req, + DBUS_TYPE_UINT16, &version, + DBUS_TYPE_INVALID); + if (ret != EOK) { + sbus_disconnect(conn); + return ret; + } + + hret = hash_lookup(proxy_cli->proxy_auth_ctx->request_table, &key, &value); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Hash error [%d][%s]\n", hret, hash_error_string(hret)); + sbus_disconnect(conn); + } + + /* Signal that the child is up and ready to receive the request */ + req = talloc_get_type(value.ptr, struct tevent_req); + child_ctx = tevent_req_data(req, struct proxy_child_ctx); + + if (!child_ctx->running) { + /* This should hopefully be impossible, but protect + * against it anyway. If we're not marked running, then + * the init_req will be NULL below and things will + * break. + */ + DEBUG(SSSDBG_CRIT_FAILURE, "Client connection from a request " + "that's not marked as running\n"); + return EIO; + } + + init_ctx = tevent_req_data(child_ctx->init_req, struct pc_init_ctx); + init_ctx->conn = conn; + tevent_req_done(child_ctx->init_req); + child_ctx->init_req = NULL; + + return EOK; +} + +static void init_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, void *ptr) +{ + struct proxy_client *proxy_cli; + + DEBUG(SSSDBG_OP_FAILURE, + "Client timed out before Identification [%p]!\n", te); + + proxy_cli = talloc_get_type(ptr, struct proxy_client); + + sbus_disconnect(proxy_cli->conn); + talloc_zfree(proxy_cli); + + /* If we time out here, we will also time out to + * pc_init_timeout(), so we'll finish the request + * there. + */ +} + +int proxy_client_init(struct sbus_connection *conn, void *data) +{ + struct proxy_auth_ctx *proxy_auth_ctx; + struct proxy_client *proxy_cli; + struct timeval tv; + + proxy_auth_ctx = talloc_get_type(data, struct proxy_auth_ctx); + + /* hang off this memory to the connection so that when the connection + * is freed we can potentially call a destructor */ + + proxy_cli = talloc_zero(conn, struct proxy_client); + if (!proxy_cli) { + DEBUG(SSSDBG_FATAL_FAILURE,"Out of memory?!\n"); + talloc_zfree(conn); + return ENOMEM; + } + proxy_cli->proxy_auth_ctx = proxy_auth_ctx; + proxy_cli->conn = conn; + proxy_cli->initialized = false; + + /* 5 seconds should be plenty */ + tv = tevent_timeval_current_ofs(5, 0); + + proxy_cli->timeout = tevent_add_timer(proxy_auth_ctx->be->ev, proxy_cli, + tv, init_timeout, proxy_cli); + if (!proxy_cli->timeout) { + DEBUG(SSSDBG_FATAL_FAILURE,"Out of memory?!\n"); + talloc_zfree(conn); + return ENOMEM; + } + DEBUG(SSSDBG_CONF_SETTINGS, + "Set-up proxy client ID timeout [%p]\n", proxy_cli->timeout); + + return sbus_conn_register_iface(conn, &proxy_methods.vtable, + DP_PATH, proxy_cli); +} diff --git a/src/providers/proxy/proxy_id.c b/src/providers/proxy/proxy_id.c index f8b8cbdf2..c2150924b 100644 --- a/src/providers/proxy/proxy_id.c +++ b/src/providers/proxy/proxy_id.c @@ -1362,150 +1362,159 @@ static int get_initgr_groups_process(TALLOC_CTX *memctx, /* =Proxy_Id-Functions====================================================*/ -void proxy_get_account_info(struct be_req *breq) +static struct dp_reply_std +proxy_account_info(TALLOC_CTX *mem_ctx, + struct proxy_id_ctx *ctx, + struct be_acct_req *data, + struct be_ctx *be_ctx, + struct sss_domain_info *domain) { - struct be_ctx *be_ctx = be_req_get_be_ctx(breq); - struct be_acct_req *ar; - struct proxy_id_ctx *ctx; + struct dp_reply_std reply; struct sysdb_ctx *sysdb; - struct sss_domain_info *domain; uid_t uid; gid_t gid; - int ret; + errno_t ret; char *endptr; - ar = talloc_get_type(be_req_get_data(breq), struct be_acct_req); - ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data, - struct proxy_id_ctx); - sysdb = be_ctx->domain->sysdb; - domain = be_ctx->domain; - - if (be_is_offline(be_ctx)) { - return be_req_terminate(breq, DP_ERR_OFFLINE, EAGAIN, "Offline"); - } + sysdb = domain->sysdb; - /* for now we support only core attrs */ - if (ar->attr_type != BE_ATTR_CORE) { - return be_req_terminate(breq, DP_ERR_FATAL, EINVAL, "Invalid attr type"); + /* For now we support only core attrs. */ + if (data->attr_type != BE_ATTR_CORE) { + dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL, "Invalid attr type"); + return reply; } - /* proxy provider does not support security ID lookups */ - if (ar->filter_type == BE_FILTER_SECID) { - return be_req_terminate(breq, DP_ERR_FATAL, ENOSYS, - "Invalid filter type"); + /* Proxy provider does not support security ID lookups. */ + if (data->filter_type == BE_FILTER_SECID) { + dp_reply_std_set(&reply, DP_ERR_FATAL, ENOSYS, + "Security lookups are not supported"); + return reply; } - switch (ar->entry_type & BE_REQ_TYPE_MASK) { + switch (data->entry_type & BE_REQ_TYPE_MASK) { case BE_REQ_USER: /* user */ - switch (ar->filter_type) { + switch (data->filter_type) { case BE_FILTER_ENUM: - ret = enum_users(breq, ctx, sysdb, domain); + ret = enum_users(mem_ctx, ctx, sysdb, domain); break; case BE_FILTER_NAME: - ret = get_pw_name(ctx, domain, ar->filter_value); + ret = get_pw_name(ctx, domain, data->filter_value); break; case BE_FILTER_IDNUM: - uid = (uid_t) strtouint32(ar->filter_value, &endptr, 10); - if (errno || *endptr || (ar->filter_value == endptr)) { - return be_req_terminate(breq, DP_ERR_FATAL, - EINVAL, "Invalid attr type"); + uid = (uid_t) strtouint32(data->filter_value, &endptr, 10); + if (errno || *endptr || (data->filter_value == endptr)) { + dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL, + "Invalid attr type"); + return reply; } ret = get_pw_uid(ctx, domain, uid); break; default: - return be_req_terminate(breq, DP_ERR_FATAL, - EINVAL, "Invalid filter type"); + dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL, + "Invalid filter type"); + return reply; } break; case BE_REQ_GROUP: /* group */ - switch (ar->filter_type) { + switch (data->filter_type) { case BE_FILTER_ENUM: - ret = enum_groups(breq, ctx, sysdb, domain); + ret = enum_groups(mem_ctx, ctx, sysdb, domain); break; case BE_FILTER_NAME: - ret = get_gr_name(ctx, sysdb, domain, ar->filter_value); + ret = get_gr_name(ctx, sysdb, domain, data->filter_value); break; case BE_FILTER_IDNUM: - gid = (gid_t) strtouint32(ar->filter_value, &endptr, 10); - if (errno || *endptr || (ar->filter_value == endptr)) { - return be_req_terminate(breq, DP_ERR_FATAL, - EINVAL, "Invalid attr type"); + gid = (gid_t) strtouint32(data->filter_value, &endptr, 10); + if (errno || *endptr || (data->filter_value == endptr)) { + dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL, + "Invalid attr type"); + return reply; } - ret = get_gr_gid(breq, ctx, sysdb, domain, gid, 0); + ret = get_gr_gid(mem_ctx, ctx, sysdb, domain, gid, 0); break; default: - return be_req_terminate(breq, DP_ERR_FATAL, - EINVAL, "Invalid filter type"); + dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL, + "Invalid filter type"); + return reply; } break; case BE_REQ_INITGROUPS: /* init groups for user */ - if (ar->filter_type != BE_FILTER_NAME) { - return be_req_terminate(breq, DP_ERR_FATAL, - EINVAL, "Invalid filter type"); + if (data->filter_type != BE_FILTER_NAME) { + dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL, + "Invalid filter type"); + return reply; } if (ctx->ops.initgroups_dyn == NULL) { - return be_req_terminate(breq, DP_ERR_FATAL, - ENODEV, "Initgroups call not supported"); + dp_reply_std_set(&reply, DP_ERR_FATAL, ENODEV, + "Initgroups call not supported"); + return reply; } - ret = get_initgr(breq, ctx, sysdb, domain, ar->filter_value); + ret = get_initgr(mem_ctx, ctx, sysdb, domain, data->filter_value); break; case BE_REQ_NETGROUP: - if (ar->filter_type != BE_FILTER_NAME) { - return be_req_terminate(breq, DP_ERR_FATAL, - EINVAL, "Invalid filter type"); + if (data->filter_type != BE_FILTER_NAME) { + dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL, + "Invalid filter type"); + return reply; } if (ctx->ops.setnetgrent == NULL || ctx->ops.getnetgrent_r == NULL || ctx->ops.endnetgrent == NULL) { - return be_req_terminate(breq, DP_ERR_FATAL, - ENODEV, "Netgroups are not supported"); + dp_reply_std_set(&reply, DP_ERR_FATAL, ENODEV, + "Netgroups are not supported"); + return reply; } - ret = get_netgroup(ctx, domain, ar->filter_value); + ret = get_netgroup(ctx, domain, data->filter_value); break; case BE_REQ_SERVICES: - switch (ar->filter_type) { + switch (data->filter_type) { case BE_FILTER_NAME: if (ctx->ops.getservbyname_r == NULL) { - return be_req_terminate(breq, DP_ERR_FATAL, - ENODEV, "Services are not supported"); + dp_reply_std_set(&reply, DP_ERR_FATAL, ENODEV, + "Services are not supported"); + return reply; } ret = get_serv_byname(ctx, domain, - ar->filter_value, - ar->extra_value); + data->filter_value, + data->extra_value); break; case BE_FILTER_IDNUM: if (ctx->ops.getservbyport_r == NULL) { - return be_req_terminate(breq, DP_ERR_FATAL, - ENODEV, "Services are not supported"); + dp_reply_std_set(&reply, DP_ERR_FATAL, ENODEV, + "Services are not supported"); + return reply; } ret = get_serv_byport(ctx, domain, - ar->filter_value, - ar->extra_value); + data->filter_value, + data->extra_value); break; case BE_FILTER_ENUM: if (!ctx->ops.setservent || !ctx->ops.getservent_r || !ctx->ops.endservent) { - return be_req_terminate(breq, DP_ERR_FATAL, - ENODEV, "Services are not supported"); + dp_reply_std_set(&reply, DP_ERR_FATAL, ENODEV, + "Services are not supported"); + return reply; } ret = enum_services(ctx, sysdb, domain); break; default: - return be_req_terminate(breq, DP_ERR_FATAL, - EINVAL, "Invalid filter type"); + dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL, + "Invalid filter type"); + return reply; } break; default: /*fail*/ - return be_req_terminate(breq, DP_ERR_FATAL, - EINVAL, "Invalid request type"); + dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL, + "Invalid filter type"); + return reply; } if (ret) { @@ -1514,8 +1523,56 @@ void proxy_get_account_info(struct be_req *breq) "proxy returned UNAVAIL error, going offline!\n"); be_mark_offline(be_ctx); } - be_req_terminate(breq, DP_ERR_FATAL, ret, NULL); - return; + + dp_reply_std_set(&reply, DP_ERR_FATAL, ret, NULL); + return reply; + } + + dp_reply_std_set(&reply, DP_ERR_OK, EOK, NULL); + return reply; +} + +struct proxy_account_info_handler_state { + struct dp_reply_std reply; +}; + +struct tevent_req * +proxy_account_info_handler_send(TALLOC_CTX *mem_ctx, + struct proxy_id_ctx *id_ctx, + struct be_acct_req *data, + struct dp_req_params *params) +{ + struct proxy_account_info_handler_state *state; + struct tevent_req *req; + + req = tevent_req_create(mem_ctx, &state, + struct proxy_account_info_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; } - be_req_terminate(breq, DP_ERR_OK, EOK, NULL); + + state->reply = proxy_account_info(state, id_ctx, data, params->be_ctx, + params->be_ctx->domain); + + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); + tevent_req_post(req, params->ev); + + return req; +} + +errno_t proxy_account_info_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data) +{ + struct proxy_account_info_handler_state *state = NULL; + + state = tevent_req_data(req, struct proxy_account_info_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *data = state->reply; + + return EOK; } diff --git a/src/providers/proxy/proxy_init.c b/src/providers/proxy/proxy_init.c index 0a6b11d4a..3a4cee91e 100644 --- a/src/providers/proxy/proxy_init.c +++ b/src/providers/proxy/proxy_init.c @@ -27,60 +27,26 @@ #include "util/sss_format.h" #include "providers/proxy/proxy.h" -static int client_registration(struct sbus_request *dbus_req, void *data); - -static struct data_provider_iface proxy_methods = { - { &data_provider_iface_meta, 0 }, - .RegisterService = client_registration, - .pamHandler = NULL, - .sudoHandler = NULL, - .autofsHandler = NULL, - .hostHandler = NULL, - .getDomains = NULL, - .getAccountInfo = NULL, -}; - -static void proxy_shutdown(struct be_req *req) -{ - /* TODO: Clean up any internal data */ - be_req_terminate(req, DP_ERR_OK, EOK, NULL); -} - -static void proxy_auth_shutdown(struct be_req *req) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(req); - talloc_free(be_ctx->bet_info[BET_AUTH].pvt_bet_data); - be_req_terminate(req, DP_ERR_OK, EOK, NULL); -} - -struct bet_ops proxy_id_ops = { - .handler = proxy_get_account_info, - .finalize = proxy_shutdown, - .check_online = NULL -}; - -struct bet_ops proxy_auth_ops = { - .handler = proxy_pam_handler, - .finalize = proxy_auth_shutdown -}; - -struct bet_ops proxy_access_ops = { - .handler = proxy_pam_handler, - .finalize = proxy_auth_shutdown -}; - -struct bet_ops proxy_chpass_ops = { - .handler = proxy_pam_handler, - .finalize = proxy_auth_shutdown -}; - -static void *proxy_dlsym(void *handle, const char *functemp, char *libname) +#define NSS_FN_NAME "_nss_%s_%s" + +#define ERROR_INITGR "The '%s' library does not provides the " \ + "_nss_XXX_initgroups_dyn function!\n" \ + "initgroups will be slow as it will require " \ + "full groups enumeration!\n" +#define ERROR_NETGR "The '%s' library does not support netgroups.\n" +#define ERROR_SERV "The '%s' library does not support services.\n" + +static void *proxy_dlsym(void *handle, + const char *name, + const char *libname) { char *funcname; void *funcptr; - funcname = talloc_asprintf(NULL, functemp, libname); - if (funcname == NULL) return NULL; + funcname = talloc_asprintf(NULL, NSS_FN_NAME, libname, name); + if (funcname == NULL) { + return NULL; + } funcptr = dlsym(handle, funcname); talloc_free(funcname); @@ -88,476 +54,331 @@ static void *proxy_dlsym(void *handle, const char *functemp, char *libname) return funcptr; } -int sssm_proxy_id_init(struct be_ctx *bectx, - struct bet_ops **ops, void **pvt_data) +static errno_t proxy_id_conf(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + char **_libname, + char **_libpath, + bool *_fast_alias) { - struct proxy_id_ctx *ctx; + TALLOC_CTX *tmp_ctx; char *libname; char *libpath; - int ret; + bool fast_alias; + errno_t ret; - ctx = talloc_zero(bectx, struct proxy_id_ctx); - if (!ctx) { + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); return ENOMEM; } - ctx->be = bectx; - ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, + ret = confdb_get_string(be_ctx->cdb, tmp_ctx, be_ctx->conf_path, CONFDB_PROXY_LIBNAME, NULL, &libname); - if (ret != EOK) goto done; - if (libname == NULL) { + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read confdb [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } else if (libname == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "No library name given\n"); ret = ENOENT; goto done; } - ret = confdb_get_bool(bectx->cdb, bectx->conf_path, - CONFDB_PROXY_FAST_ALIAS, false, &ctx->fast_alias); - if (ret != EOK) goto done; - - libpath = talloc_asprintf(ctx, "libnss_%s.so.2", libname); - if (!libpath) { - ret = ENOMEM; + ret = confdb_get_bool(be_ctx->cdb, be_ctx->conf_path, + CONFDB_PROXY_FAST_ALIAS, false, &fast_alias); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read confdb [%d]: %s\n", + ret, sss_strerror(ret)); goto done; } - ctx->handle = dlopen(libpath, RTLD_NOW); - if (!ctx->handle) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Unable to load %s module with path, error: %s\n", - libpath, dlerror()); - ret = ELIBACC; + libpath = talloc_asprintf(tmp_ctx, "libnss_%s.so.2", libname); + if (libpath == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n"); + ret = ENOMEM; goto done; } - ctx->ops.getpwnam_r = proxy_dlsym(ctx->handle, "_nss_%s_getpwnam_r", - libname); - if (!ctx->ops.getpwnam_r) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load NSS fns, error: %s\n", dlerror()); - ret = ELIBBAD; - goto done; - } + *_libname = talloc_steal(mem_ctx, libname); + *_libpath = talloc_steal(mem_ctx, libpath); + *_fast_alias = fast_alias; - ctx->ops.getpwuid_r = proxy_dlsym(ctx->handle, "_nss_%s_getpwuid_r", - libname); - if (!ctx->ops.getpwuid_r) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load NSS fns, error: %s\n", dlerror()); - ret = ELIBBAD; - goto done; - } + ret = EOK; - ctx->ops.setpwent = proxy_dlsym(ctx->handle, "_nss_%s_setpwent", libname); - if (!ctx->ops.setpwent) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load NSS fns, error: %s\n", dlerror()); - ret = ELIBBAD; - goto done; - } +done: + talloc_free(tmp_ctx); - ctx->ops.getpwent_r = proxy_dlsym(ctx->handle, "_nss_%s_getpwent_r", - libname); - if (!ctx->ops.getpwent_r) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load NSS fns, error: %s\n", dlerror()); - ret = ELIBBAD; - goto done; - } + return ret; +} - ctx->ops.endpwent = proxy_dlsym(ctx->handle, "_nss_%s_endpwent", libname); - if (!ctx->ops.endpwent) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load NSS fns, error: %s\n", dlerror()); - ret = ELIBBAD; - goto done; +static errno_t proxy_id_load_symbols(struct proxy_nss_ops *ops, + const char *libname, + void *handle) +{ + int i; + struct {void **dest; + const char *name; + const char *custom_error; + bool is_fatal; + } symbols[] = { + {(void**)&ops->getpwnam_r, "getpwnam_r", NULL, true}, + {(void**)&ops->getpwuid_r, "getpwuid_r", NULL, true}, + {(void**)&ops->setpwent, "setpwent", NULL, true}, + {(void**)&ops->getpwent_r, "getpwent_r", NULL, true}, + {(void**)&ops->endpwent, "endpwent", NULL, true}, + {(void**)&ops->getgrnam_r, "getgrnam_r", NULL, true}, + {(void**)&ops->getgrgid_r, "getgrgid_r", NULL, true}, + {(void**)&ops->setgrent, "setgrent", NULL, true}, + {(void**)&ops->getgrent_r, "getgrent_r", NULL, true}, + {(void**)&ops->endgrent, "endgrent", NULL, true}, + {(void**)&ops->initgroups_dyn, "initgroups_dyn", ERROR_INITGR, false}, + {(void**)&ops->setnetgrent, "setnetgrent", ERROR_NETGR, false}, + {(void**)&ops->getnetgrent_r, "getnetgrent_r", ERROR_NETGR, false}, + {(void**)&ops->endnetgrent, "endnetgrent", ERROR_NETGR, false}, + {(void**)&ops->getservbyname_r, "getservbyname_r", ERROR_SERV, false}, + {(void**)&ops->getservbyport_r, "getservbyport_r", ERROR_SERV, false}, + {(void**)&ops->setservent, "setservent", ERROR_SERV, false}, + {(void**)&ops->getservent_r, "getservent_r", ERROR_SERV, false}, + {(void**)&ops->endservent, "endservent", ERROR_SERV, false}, + {NULL, NULL, NULL, false} + }; + + for (i = 0; symbols[i].dest != NULL; i++) { + *symbols[i].dest = proxy_dlsym(handle, symbols[i].name, libname); + if (*symbols[i].dest == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to load _nss_%s_%s, " + "error: %s.\n", libname, symbols[i].name, dlerror()); + + if (symbols[i].custom_error != NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, symbols[i].custom_error, libname); + } + + if (symbols[i].is_fatal) { + return ELIBBAD; + } + } } - ctx->ops.getgrnam_r = proxy_dlsym(ctx->handle, "_nss_%s_getgrnam_r", - libname); - if (!ctx->ops.getgrnam_r) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load NSS fns, error: %s\n", dlerror()); - ret = ELIBBAD; - goto done; - } + return EOK; +} - ctx->ops.getgrgid_r = proxy_dlsym(ctx->handle, "_nss_%s_getgrgid_r", - libname); - if (!ctx->ops.getgrgid_r) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load NSS fns, error: %s\n", dlerror()); - ret = ELIBBAD; - goto done; - } +static errno_t proxy_setup_sbus(TALLOC_CTX *mem_ctx, + struct proxy_auth_ctx *ctx, + struct be_ctx *be_ctx) +{ + char *sbus_address; + errno_t ret; - ctx->ops.setgrent = proxy_dlsym(ctx->handle, "_nss_%s_setgrent", libname); - if (!ctx->ops.setgrent) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load NSS fns, error: %s\n", dlerror()); - ret = ELIBBAD; - goto done; + sbus_address = talloc_asprintf(mem_ctx, "unix:path=%s/%s_%s", PIPE_PATH, + PROXY_CHILD_PIPE, be_ctx->domain->name); + if (sbus_address == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed.\n"); + return ENOMEM; } - ctx->ops.getgrent_r = proxy_dlsym(ctx->handle, "_nss_%s_getgrent_r", - libname); - if (!ctx->ops.getgrent_r) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load NSS fns, error: %s\n", dlerror()); - ret = ELIBBAD; - goto done; + ret = sbus_new_server(mem_ctx, be_ctx->ev, sbus_address, 0, be_ctx->gid, + false, &ctx->sbus_srv, proxy_client_init, ctx); + talloc_free(sbus_address); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Could not set up sbus server.\n"); + return ret; } - ctx->ops.endgrent = proxy_dlsym(ctx->handle, "_nss_%s_endgrent", libname); - if (!ctx->ops.endgrent) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load NSS fns, error: %s\n", dlerror()); - ret = ELIBBAD; - goto done; - } + return EOK; +} - ctx->ops.initgroups_dyn = proxy_dlsym(ctx->handle, "_nss_%s_initgroups_dyn", - libname); - if (!ctx->ops.initgroups_dyn) { - DEBUG(SSSDBG_CRIT_FAILURE, "The '%s' library does not provides the " - "_nss_XXX_initgroups_dyn function!\n" - "initgroups will be slow as it will require " - "full groups enumeration!\n", libname); - } +static errno_t proxy_auth_conf(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + char **_pam_target) +{ + char *pam_target; + errno_t ret; - ctx->ops.setnetgrent = proxy_dlsym(ctx->handle, "_nss_%s_setnetgrent", - libname); - if (!ctx->ops.setnetgrent) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load _nss_%s_setnetgrent, error: %s. " - "The library does not support netgroups.\n", libname, - dlerror()); + ret = confdb_get_string(be_ctx->cdb, mem_ctx, be_ctx->conf_path, + CONFDB_PROXY_PAM_TARGET, NULL, &pam_target); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read confdb [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; } - ctx->ops.getnetgrent_r = proxy_dlsym(ctx->handle, "_nss_%s_getnetgrent_r", - libname); - if (!ctx->ops.getgrent_r) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load _nss_%s_getnetgrent_r, error: %s. " - "The library does not support netgroups.\n", libname, - dlerror()); + if (pam_target == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Missing option %s.\n", + CONFDB_PROXY_PAM_TARGET); + return EINVAL; } - ctx->ops.endnetgrent = proxy_dlsym(ctx->handle, "_nss_%s_endnetgrent", - libname); - if (!ctx->ops.endnetgrent) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Failed to load _nss_%s_endnetgrent, error: %s. " - "The library does not support netgroups.\n", libname, - dlerror()); - } + *_pam_target = pam_target; - ctx->ops.getservbyname_r = proxy_dlsym(ctx->handle, - "_nss_%s_getservbyname_r", - libname); - if (!ctx->ops.getservbyname_r) { - DEBUG(SSSDBG_MINOR_FAILURE, - "Failed to load _nss_%s_getservbyname_r, error: %s. " - "The library does not support services.\n", - libname, - dlerror()); - } + return EOK; +} - ctx->ops.getservbyport_r = proxy_dlsym(ctx->handle, - "_nss_%s_getservbyport_r", - libname); - if (!ctx->ops.getservbyport_r) { - DEBUG(SSSDBG_MINOR_FAILURE, - "Failed to load _nss_%s_getservbyport_r, error: %s. " - "The library does not support services.\n", - libname, - dlerror()); - } +static errno_t proxy_init_auth_ctx(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct proxy_auth_ctx **_auth_ctx) +{ + struct proxy_auth_ctx *auth_ctx; + errno_t ret; + int hret; - ctx->ops.setservent = proxy_dlsym(ctx->handle, - "_nss_%s_setservent", - libname); - if (!ctx->ops.setservent) { - DEBUG(SSSDBG_MINOR_FAILURE, - "Failed to load _nss_%s_setservent, error: %s. " - "The library does not support services.\n", - libname, - dlerror()); + auth_ctx = talloc_zero(mem_ctx, struct proxy_auth_ctx); + if (auth_ctx == NULL) { + return ENOMEM; } - ctx->ops.getservent_r = proxy_dlsym(ctx->handle, - "_nss_%s_getservent_r", - libname); - if (!ctx->ops.getservent_r) { - DEBUG(SSSDBG_MINOR_FAILURE, - "Failed to load _nss_%s_getservent_r, error: %s. " - "The library does not support services.\n", - libname, - dlerror()); - } + auth_ctx->be = be_ctx; + auth_ctx->timeout_ms = SSS_CLI_SOCKET_TIMEOUT / 4; + auth_ctx->next_id = 1; - ctx->ops.endservent = proxy_dlsym(ctx->handle, - "_nss_%s_endservent", - libname); - if (!ctx->ops.endservent) { - DEBUG(SSSDBG_MINOR_FAILURE, - "Failed to load _nss_%s_endservent, error: %s. " - "The library does not support services.\n", - libname, - dlerror()); + ret = proxy_auth_conf(auth_ctx, be_ctx, &auth_ctx->pam_target); + if (ret != EOK) { + goto done; } - *ops = &proxy_id_ops; - *pvt_data = ctx; - ret = EOK; - -done: + ret = proxy_setup_sbus(auth_ctx, auth_ctx, be_ctx); if (ret != EOK) { - talloc_free(ctx); + goto done; } - return ret; -} - -struct proxy_client { - struct proxy_auth_ctx *proxy_auth_ctx; - struct sbus_connection *conn; - struct tevent_timer *timeout; - bool initialized; -}; - -static void init_timeout(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval t, void *ptr); -static int proxy_client_init(struct sbus_connection *conn, void *data) -{ - struct proxy_auth_ctx *proxy_auth_ctx; - struct proxy_client *proxy_cli; - struct timeval tv; - proxy_auth_ctx = talloc_get_type(data, struct proxy_auth_ctx); - - /* hang off this memory to the connection so that when the connection - * is freed we can potentially call a destructor */ + /* Set up request hash table */ + /* FIXME: get max_children from configuration file */ + auth_ctx->max_children = 10; - proxy_cli = talloc_zero(conn, struct proxy_client); - if (!proxy_cli) { - DEBUG(SSSDBG_FATAL_FAILURE,"Out of memory?!\n"); - talloc_zfree(conn); - return ENOMEM; - } - proxy_cli->proxy_auth_ctx = proxy_auth_ctx; - proxy_cli->conn = conn; - proxy_cli->initialized = false; - - /* 5 seconds should be plenty */ - tv = tevent_timeval_current_ofs(5, 0); - - proxy_cli->timeout = tevent_add_timer(proxy_auth_ctx->be->ev, proxy_cli, - tv, init_timeout, proxy_cli); - if (!proxy_cli->timeout) { - DEBUG(SSSDBG_FATAL_FAILURE,"Out of memory?!\n"); - talloc_zfree(conn); - return ENOMEM; + hret = hash_create(auth_ctx->max_children * 2, &auth_ctx->request_table, + NULL, NULL); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, "Could not initialize request table\n"); + ret = EIO; + goto done; } - DEBUG(SSSDBG_CONF_SETTINGS, - "Set-up proxy client ID timeout [%p]\n", proxy_cli->timeout); - return sbus_conn_register_iface(conn, &proxy_methods.vtable, - DP_PATH, proxy_cli); -} + *_auth_ctx = auth_ctx; -static void init_timeout(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval t, void *ptr) -{ - struct proxy_client *proxy_cli; - - DEBUG(SSSDBG_OP_FAILURE, - "Client timed out before Identification [%p]!\n", te); - - proxy_cli = talloc_get_type(ptr, struct proxy_client); + ret = EOK; - sbus_disconnect(proxy_cli->conn); - talloc_zfree(proxy_cli); +done: + if (ret != EOK) { + talloc_free(auth_ctx); + } - /* If we time out here, we will also time out to - * pc_init_timeout(), so we'll finish the request - * there. - */ + return ret; } -static int client_registration(struct sbus_request *dbus_req, void *data) +errno_t sssm_proxy_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct data_provider *provider, + const char *module_name, + void **_module_data) { - dbus_uint16_t version = DATA_PROVIDER_VERSION; - struct sbus_connection *conn; - struct proxy_client *proxy_cli; - dbus_uint16_t cli_ver; - uint32_t cli_id; - int hret; - hash_key_t key; - hash_value_t value; - struct tevent_req *req; - struct proxy_child_ctx *child_ctx; - struct pc_init_ctx *init_ctx; - int ret; - - conn = dbus_req->conn; - proxy_cli = talloc_get_type(data, struct proxy_client); - if (!proxy_cli) { - DEBUG(SSSDBG_FATAL_FAILURE, "Connection holds no valid init data\n"); - return EINVAL; - } + struct proxy_auth_ctx *auth_ctx; + errno_t ret; - /* First thing, cancel the timeout */ - DEBUG(SSSDBG_CONF_SETTINGS, - "Cancel proxy client ID timeout [%p]\n", proxy_cli->timeout); - talloc_zfree(proxy_cli->timeout); - - if (!sbus_request_parse_or_finish(dbus_req, - DBUS_TYPE_UINT16, &cli_ver, - DBUS_TYPE_UINT32, &cli_id, - DBUS_TYPE_INVALID)) { - sbus_disconnect(conn); - return EOK; /* handled */ + if (!dp_target_enabled(provider, module_name, + DPT_ACCESS, DPT_AUTH, DPT_CHPASS)) { + return EOK; } - DEBUG(SSSDBG_FUNC_DATA, "Proxy client [%"PRIu32"] connected\n", cli_id); - - /* Check the hash table */ - key.type = HASH_KEY_ULONG; - key.ul = cli_id; - if (!hash_has_key(proxy_cli->proxy_auth_ctx->request_table, &key)) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Unknown child ID. Killing the connection\n"); - sbus_disconnect(proxy_cli->conn); - return EIO; - } + /* Initialize auth_ctx since one of the access, auth or chpass is set. */ - /* reply that all is ok */ - ret = sbus_request_return_and_finish(dbus_req, - DBUS_TYPE_UINT16, &version, - DBUS_TYPE_INVALID); + ret = proxy_init_auth_ctx(mem_ctx, be_ctx, &auth_ctx); if (ret != EOK) { - sbus_disconnect(conn); + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create auth context [%d]: %s\n", + ret, sss_strerror(ret)); return ret; } - hret = hash_lookup(proxy_cli->proxy_auth_ctx->request_table, &key, &value); - if (hret != HASH_SUCCESS) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Hash error [%d][%s]\n", hret, hash_error_string(hret)); - sbus_disconnect(conn); - } - - /* Signal that the child is up and ready to receive the request */ - req = talloc_get_type(value.ptr, struct tevent_req); - child_ctx = tevent_req_data(req, struct proxy_child_ctx); - - if (!child_ctx->running) { - /* This should hopefully be impossible, but protect - * against it anyway. If we're not marked running, then - * the init_req will be NULL below and things will - * break. - */ - DEBUG(SSSDBG_CRIT_FAILURE, "Client connection from a request " - "that's not marked as running\n"); - return EIO; - } - - init_ctx = tevent_req_data(child_ctx->init_req, struct pc_init_ctx); - init_ctx->conn = conn; - tevent_req_done(child_ctx->init_req); - child_ctx->init_req = NULL; + *_module_data = auth_ctx; return EOK; } -int sssm_proxy_auth_init(struct be_ctx *bectx, - struct bet_ops **ops, void **pvt_data) +errno_t sssm_proxy_id_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - struct proxy_auth_ctx *ctx; - int ret; - int hret; - char *sbus_address; - - /* If we're already set up, just return that */ - if(bectx->bet_info[BET_AUTH].mod_name && - strcmp("proxy", bectx->bet_info[BET_AUTH].mod_name) == 0) { - DEBUG(SSSDBG_TRACE_INTERNAL, - "Re-using proxy_auth_ctx for this provider\n"); - *ops = bectx->bet_info[BET_AUTH].bet_ops; - *pvt_data = bectx->bet_info[BET_AUTH].pvt_bet_data; - return EOK; - } + struct proxy_id_ctx *ctx; + char *libname; + char *libpath; + errno_t ret; - ctx = talloc_zero(bectx, struct proxy_auth_ctx); - if (!ctx) { + ctx = talloc_zero(mem_ctx, struct proxy_id_ctx); + if (ctx == NULL) { return ENOMEM; } - ctx->be = bectx; - ctx->timeout_ms = SSS_CLI_SOCKET_TIMEOUT/4; - ctx->next_id = 1; - - ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, - CONFDB_PROXY_PAM_TARGET, NULL, - &ctx->pam_target); - if (ret != EOK) goto done; - if (!ctx->pam_target) { - DEBUG(SSSDBG_CRIT_FAILURE, "Missing option proxy_pam_target.\n"); - ret = EINVAL; + + ctx->be = be_ctx; + + ret = proxy_id_conf(ctx, be_ctx, &libname, &libpath, &ctx->fast_alias); + if (ret != EOK) { goto done; } - sbus_address = talloc_asprintf(ctx, "unix:path=%s/%s_%s", PIPE_PATH, - PROXY_CHILD_PIPE, bectx->domain->name); - if (sbus_address == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n"); - ret = ENOMEM; + ctx->handle = dlopen(libpath, RTLD_NOW); + if (ctx->handle == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to load %s module, " + "error: %s\n", libpath, dlerror()); + ret = ELIBACC; goto done; } - ret = sbus_new_server(ctx, bectx->ev, sbus_address, 0, bectx->gid, - false, &ctx->sbus_srv, proxy_client_init, ctx); + ret = proxy_id_load_symbols(&ctx->ops, libname, ctx->handle); if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, "Could not set up sbus server.\n"); + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to load NSS symbols [%d]: %s\n", + ret, sss_strerror(ret)); goto done; } - /* Set up request hash table */ - /* FIXME: get max_children from configuration file */ - ctx->max_children = 10; + dp_set_method(dp_methods, DPM_ACCOUNT_HANDLER, + proxy_account_info_handler_send, proxy_account_info_handler_recv, ctx, + struct proxy_id_ctx, struct be_acct_req, struct dp_reply_std); - hret = hash_create(ctx->max_children * 2, &ctx->request_table, - NULL, NULL); - if (hret != HASH_SUCCESS) { - DEBUG(SSSDBG_FATAL_FAILURE, "Could not initialize request table\n"); - ret = EIO; - goto done; - } - - *ops = &proxy_auth_ops; - *pvt_data = ctx; + ret = EOK; done: if (ret != EOK) { talloc_free(ctx); } + return ret; } -int sssm_proxy_access_init(struct be_ctx *bectx, - struct bet_ops **ops, void **pvt_data) +errno_t sssm_proxy_auth_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - int ret; - ret = sssm_proxy_auth_init(bectx, ops, pvt_data); - *ops = &proxy_access_ops; - return ret; + struct proxy_auth_ctx *auth_ctx; + + auth_ctx = talloc_get_type(module_data, struct proxy_auth_ctx); + + dp_set_method(dp_methods, DPM_AUTH_HANDLER, + proxy_pam_handler_send, proxy_pam_handler_recv, auth_ctx, + struct proxy_auth_ctx, struct pam_data, struct pam_data *); + + return EOK; } -int sssm_proxy_chpass_init(struct be_ctx *bectx, - struct bet_ops **ops, void **pvt_data) +errno_t sssm_proxy_chpass_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - int ret; - ret = sssm_proxy_auth_init(bectx, ops, pvt_data); - *ops = &proxy_chpass_ops; - return ret; + return sssm_proxy_auth_init(mem_ctx, be_ctx, module_data, dp_methods); +} + +errno_t sssm_proxy_access_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) +{ + struct proxy_auth_ctx *auth_ctx; + + auth_ctx = talloc_get_type(module_data, struct proxy_auth_ctx); + + dp_set_method(dp_methods, DPM_ACCESS_HANDLER, + proxy_pam_handler_send, proxy_pam_handler_recv, auth_ctx, + struct proxy_auth_ctx, struct pam_data, struct pam_data *); + + return EOK; } diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c index d4fa615f2..ca6d49db4 100644 --- a/src/providers/simple/simple_access.c +++ b/src/providers/simple/simple_access.c @@ -34,13 +34,80 @@ #define TIMEOUT_OF_REFRESH_FILTER_LISTS 5 -static void simple_access_check(struct tevent_req *req); static errno_t simple_access_parse_names(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx, char **list, - char ***_out); + char ***_out) +{ + TALLOC_CTX *tmp_ctx = NULL; + char **out = NULL; + char *domain = NULL; + char *name = NULL; + size_t size; + size_t i; + errno_t ret; + + if (list == NULL) { + *_out = NULL; + return EOK; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); + ret = ENOMEM; + goto done; + } + + for (size = 0; list[size] != NULL; size++) { + /* count size */ + } + + out = talloc_zero_array(tmp_ctx, char*, size + 1); + if (out == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n"); + ret = ENOMEM; + goto done; + } + + /* Since this is access provider, we should fail on any error so we don't + * allow unauthorized access. */ + for (i = 0; i < size; i++) { + ret = sss_parse_name(tmp_ctx, be_ctx->domain->names, list[i], + &domain, &name); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name '%s' [%d]: %s\n", + list[i], ret, sss_strerror(ret)); + goto done; + } + + if (domain == NULL || strcasecmp(domain, be_ctx->domain->name) == 0 || + (be_ctx->domain->flat_name != NULL && + strcasecmp(domain, be_ctx->domain->flat_name) == 0)) { + /* This object belongs to main SSSD domain. Those users and groups + * are stored without domain part, so we will strip it off. + * */ + out[i] = talloc_move(out, &name); + } else { + /* Subdomain users and groups are stored as fully qualified names, + * thus we will remember the domain part. + * + * Since subdomains may come and go, we will look for their + * existence later, during each access check. + */ + out[i] = talloc_move(out, &list[i]); + } + } + + *_out = talloc_steal(mem_ctx, out); + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} -static int simple_access_obtain_filter_lists(struct simple_ctx *ctx) +int simple_access_obtain_filter_lists(struct simple_ctx *ctx) { struct be_ctx *bectx = ctx->be_ctx; int ret; @@ -103,185 +170,141 @@ failed: return ret; } -void simple_access_handler(struct be_req *be_req) -{ - struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); +struct simple_access_handler_state { struct pam_data *pd; +}; + +static void simple_access_handler_done(struct tevent_req *subreq); + +static struct tevent_req * +simple_access_handler_send(TALLOC_CTX *mem_ctx, + struct simple_ctx *simple_ctx, + struct pam_data *pd, + struct dp_req_params *params) +{ + struct simple_access_handler_state *state; + struct tevent_req *subreq; struct tevent_req *req; - struct simple_ctx *ctx; - int ret; + errno_t ret; time_t now; - pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); + req = tevent_req_create(mem_ctx, &state, + struct simple_access_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } - pd->pam_status = PAM_SYSTEM_ERR; + state->pd = pd; + pd->pam_status = PAM_SYSTEM_ERR; if (pd->cmd != SSS_PAM_ACCT_MGMT) { DEBUG(SSSDBG_CONF_SETTINGS, "simple access does not handle pam task %d.\n", pd->cmd); pd->pam_status = PAM_MODULE_UNKNOWN; - goto done; + goto immediately; } - ctx = talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data, - struct simple_ctx); - - now = time(NULL); - if ((now - ctx->last_refresh_of_filter_lists) + if ((now - simple_ctx->last_refresh_of_filter_lists) > TIMEOUT_OF_REFRESH_FILTER_LISTS) { - ret = simple_access_obtain_filter_lists(ctx); + ret = simple_access_obtain_filter_lists(simple_ctx); if (ret != EOK) { DEBUG(SSSDBG_MINOR_FAILURE, "Failed to refresh filter lists\n"); } - ctx->last_refresh_of_filter_lists = now; + simple_ctx->last_refresh_of_filter_lists = now; } - req = simple_access_check_send(be_req, be_ctx->ev, ctx, pd->user); - if (!req) { + subreq = simple_access_check_send(state, params->ev, simple_ctx, pd->user); + if (subreq == NULL) { pd->pam_status = PAM_SYSTEM_ERR; - goto done; + goto immediately; } - tevent_req_set_callback(req, simple_access_check, be_req); - return; -done: - be_req_terminate(be_req, DP_ERR_OK, pd->pam_status, NULL); + tevent_req_set_callback(subreq, simple_access_handler_done, req); + + return req; + +immediately: + /* 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 simple_access_check(struct tevent_req *req) +static void simple_access_handler_done(struct tevent_req *subreq) { - bool access_granted = false; + struct simple_access_handler_state *state; + struct tevent_req *req; + bool access_granted; errno_t ret; - struct pam_data *pd; - struct be_req *be_req; - be_req = tevent_req_callback_data(req, struct be_req); - pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct simple_access_handler_state); - ret = simple_access_check_recv(req, &access_granted); - talloc_free(req); + ret = simple_access_check_recv(subreq, &access_granted); + talloc_free(subreq); if (ret != EOK) { - pd->pam_status = PAM_SYSTEM_ERR; + state->pd->pam_status = PAM_SYSTEM_ERR; goto done; } if (access_granted) { - pd->pam_status = PAM_SUCCESS; + state->pd->pam_status = PAM_SUCCESS; } else { - pd->pam_status = PAM_PERM_DENIED; + state->pd->pam_status = PAM_PERM_DENIED; } done: - be_req_terminate(be_req, DP_ERR_OK, pd->pam_status, NULL); + /* TODO For backward compatibility we always return EOK to DP now. */ + tevent_req_done(req); } -static errno_t simple_access_parse_names(TALLOC_CTX *mem_ctx, - struct be_ctx *be_ctx, - char **list, - char ***_out) +static errno_t +simple_access_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct pam_data **_data) { - TALLOC_CTX *tmp_ctx = NULL; - char **out = NULL; - char *domain = NULL; - char *name = NULL; - size_t size; - size_t i; - errno_t ret; - - if (list == NULL) { - *_out = NULL; - return EOK; - } + struct simple_access_handler_state *state = NULL; - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); - ret = ENOMEM; - goto done; - } + state = tevent_req_data(req, struct simple_access_handler_state); - for (size = 0; list[size] != NULL; size++) { - /* count size */ - } + TEVENT_REQ_RETURN_ON_ERROR(req); - out = talloc_zero_array(tmp_ctx, char*, size + 1); - if (out == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n"); - ret = ENOMEM; - goto done; - } + *_data = talloc_steal(mem_ctx, state->pd); - /* Since this is access provider, we should fail on any error so we don't - * allow unauthorized access. */ - for (i = 0; i < size; i++) { - ret = sss_parse_name(tmp_ctx, be_ctx->domain->names, list[i], - &domain, &name); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name '%s' [%d]: %s\n", - list[i], ret, sss_strerror(ret)); - goto done; - } - - if (domain == NULL || strcasecmp(domain, be_ctx->domain->name) == 0 || - (be_ctx->domain->flat_name != NULL && - strcasecmp(domain, be_ctx->domain->flat_name) == 0)) { - /* This object belongs to main SSSD domain. Those users and groups - * are stored without domain part, so we will strip it off. - * */ - out[i] = talloc_move(out, &name); - } else { - /* Subdomain users and groups are stored as fully qualified names, - * thus we will remember the domain part. - * - * Since subdomains may come and go, we will look for their - * existence later, during each access check. - */ - out[i] = talloc_move(out, &list[i]); - } - } - - *_out = talloc_steal(mem_ctx, out); - ret = EOK; - -done: - talloc_free(tmp_ctx); - return ret; + return EOK; } -struct bet_ops simple_access_ops = { - .handler = simple_access_handler, - .finalize = NULL -}; - -int sssm_simple_access_init(struct be_ctx *bectx, struct bet_ops **ops, - void **pvt_data) +errno_t sssm_simple_access_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) { - int ret = EINVAL; struct simple_ctx *ctx; - ctx = talloc_zero(bectx, struct simple_ctx); + errno_t ret; + + ctx = talloc_zero(mem_ctx, struct simple_ctx); if (ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed.\n"); return ENOMEM; } - ctx->domain = bectx->domain; - ctx->be_ctx = bectx; + ctx->domain = be_ctx->domain; + ctx->be_ctx = be_ctx; ctx->last_refresh_of_filter_lists = 0; ret = simple_access_obtain_filter_lists(ctx); if (ret != EOK) { - goto failed; + talloc_free(ctx); + return ret; } - *ops = &simple_access_ops; - *pvt_data = ctx; + dp_set_method(dp_methods, DPM_ACCESS_HANDLER, + simple_access_handler_send, simple_access_handler_recv, ctx, + struct simple_ctx, struct pam_data, struct pam_data *); return EOK; - -failed: - talloc_free(ctx); - return ret; } - diff --git a/src/providers/simple/simple_access_check.c b/src/providers/simple/simple_access_check.c index 3b7c2fe93..094ed364a 100644 --- a/src/providers/simple/simple_access_check.c +++ b/src/providers/simple/simple_access_check.c @@ -268,7 +268,9 @@ simple_resolve_group_send(TALLOC_CTX *mem_ctx, goto done; } - subreq = be_get_account_info_send(state, ev, NULL, ctx->be_ctx, ar); + subreq = dp_req_send(state, ctx->be_ctx->provider, NULL, ar->domain, + "Simple Resolve Group", DPT_ID, DPM_ACCOUNT_HANDLER, + 0, ar, NULL); if (!subreq) { ret = ENOMEM; goto done; @@ -328,27 +330,24 @@ static void simple_resolve_group_done(struct tevent_req *subreq) { struct tevent_req *req; struct simple_resolve_group_state *state; - int err_maj; - int err_min; + struct dp_reply_std *reply; errno_t ret; - const char *err_msg; req = tevent_req_callback_data(subreq, struct tevent_req); state = tevent_req_data(req, struct simple_resolve_group_state); - ret = be_get_account_info_recv(subreq, state, - &err_maj, &err_min, &err_msg); + ret = dp_req_recv_ptr(state, subreq, struct dp_reply_std, &reply); talloc_zfree(subreq); if (ret) { - DEBUG(SSSDBG_OP_FAILURE, "be_get_account_info_recv failed\n"); + DEBUG(SSSDBG_OP_FAILURE, "dp_req_recv failed\n"); tevent_req_error(req, ret); return; } - if (err_maj) { + if (reply->dp_error != DP_ERR_OK) { DEBUG(SSSDBG_MINOR_FAILURE, "Cannot refresh data from DP: %u,%u: %s\n", - err_maj, err_min, err_msg); + reply->dp_error, reply->error, reply->message); tevent_req_error(req, EIO); return; } -- cgit