/* Authors: Jakub Hrozek Pavel Březina Copyright (C) 2014 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 "db/sysdb.h" #include "util/util.h" #include "confdb/confdb.h" #include "responder/common/responder.h" #include "responder/ifp/ifp_domains.h" #define RETURN_DOM_PROP_AS_STRING(dbus_req, pvt_data, out, property) do { \ struct sss_domain_info *__dom; \ \ *(out) = NULL; \ \ __dom = get_domain_info_from_req((dbus_req), (pvt_data)); \ if (__dom == NULL) { \ return; \ } \ \ *(out) = __dom->property; \ } while (0) static void ifp_list_domains_process(struct tevent_req *req); int ifp_list_domains(struct sbus_request *dbus_req, void *data) { struct ifp_ctx *ifp_ctx; struct ifp_req *ireq; struct tevent_req *req; DBusError *error; errno_t ret; ifp_ctx = talloc_get_type(data, struct ifp_ctx); if (ifp_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Invalid ifp context!\n"); error = sbus_error_new(dbus_req, DBUS_ERROR_FAILED, "Invalid ifp context!"); return sbus_request_fail_and_finish(dbus_req, error); } ret = ifp_req_create(dbus_req, ifp_ctx, &ireq); if (ret != EOK) { error = sbus_error_new(dbus_req, DBUS_ERROR_FAILED, "%s", sss_strerror(ret)); return sbus_request_fail_and_finish(dbus_req, error); } req = sss_dp_get_domains_send(ireq, ifp_ctx->rctx, false, NULL); if (req == NULL) { return sbus_request_finish(ireq->dbus_req, NULL); } tevent_req_set_callback(req, ifp_list_domains_process, ireq); return EOK; } static void ifp_list_domains_process(struct tevent_req *req) { struct sss_domain_info *dom; struct ifp_req *ireq; const char **paths; char *p; DBusError *error; size_t num_domains; size_t pi; errno_t ret; ireq = tevent_req_callback_data(req, struct ifp_req); ret = sss_dp_get_domains_recv(req); talloc_free(req); if (ret != EOK) { error = sbus_error_new(ireq->dbus_req, DBUS_ERROR_FAILED, "Failed to refresh domain objects\n"); sbus_request_fail_and_finish(ireq->dbus_req, error); return; } ret = sysdb_master_domain_update(ireq->ifp_ctx->rctx->domains); if (ret != EOK) { error = sbus_error_new(ireq->dbus_req, DBUS_ERROR_FAILED, "Failed to refresh subdomain list\n"); sbus_request_fail_and_finish(ireq->dbus_req, error); return; } num_domains = 0; for (dom = ireq->ifp_ctx->rctx->domains; dom != NULL; dom = get_next_domain(dom, true)) { num_domains++; } paths = talloc_zero_array(ireq, const char *, num_domains); if (paths == NULL) { sbus_request_finish(ireq->dbus_req, NULL); return; } pi = 0; for (dom = ireq->ifp_ctx->rctx->domains; dom != NULL; dom = get_next_domain(dom, true)) { p = ifp_reply_objpath(ireq, INFOPIPE_DOMAIN_PATH_PFX, dom->name); if (p == NULL) { DEBUG(SSSDBG_MINOR_FAILURE, "Could not create path for dom %s, skipping\n", dom->name); continue; } paths[pi] = p; pi++; } ret = infopipe_iface_ListDomains_finish(ireq->dbus_req, paths, num_domains); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Could not finish request!\n"); } } struct ifp_get_domain_state { const char *name; struct ifp_req *ireq; }; static void ifp_find_domain_by_name_process(struct tevent_req *req); int ifp_find_domain_by_name(struct sbus_request *dbus_req, void *data, const char *arg_name) { struct ifp_ctx *ifp_ctx; struct ifp_req *ireq; struct tevent_req *req; struct ifp_get_domain_state *state; DBusError *error; errno_t ret; ifp_ctx = talloc_get_type(data, struct ifp_ctx); if (ifp_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n"); error = sbus_error_new(dbus_req, DBUS_ERROR_FAILED, "Invalid ifp context!"); return sbus_request_fail_and_finish(dbus_req, error); } ret = ifp_req_create(dbus_req, ifp_ctx, &ireq); if (ret != EOK) { error = sbus_error_new(dbus_req, DBUS_ERROR_FAILED, "%s", sss_strerror(ret)); return sbus_request_fail_and_finish(dbus_req, error); } state = talloc_zero(ireq, struct ifp_get_domain_state); if (state == NULL) { return sbus_request_finish(dbus_req, NULL); } state->name = arg_name; state->ireq = ireq; req = sss_dp_get_domains_send(ireq, ifp_ctx->rctx, false, NULL); if (req == NULL) { return sbus_request_finish(dbus_req, NULL); } tevent_req_set_callback(req, ifp_find_domain_by_name_process, state); return EOK; } static void ifp_find_domain_by_name_process(struct tevent_req *req) { errno_t ret; struct ifp_req *ireq; struct ifp_get_domain_state *state; struct sss_domain_info *iter; const char *path; DBusError *error; state = tevent_req_callback_data(req, struct ifp_get_domain_state); ireq = state->ireq; ret = sss_dp_get_domains_recv(req); talloc_free(req); if (ret != EOK) { error = sbus_error_new(ireq->dbus_req, DBUS_ERROR_FAILED, "Failed to refresh domain objects\n"); sbus_request_fail_and_finish(ireq->dbus_req, error); return; } ret = sysdb_master_domain_update(ireq->ifp_ctx->rctx->domains); if (ret != EOK) { error = sbus_error_new(ireq->dbus_req, DBUS_ERROR_FAILED, "Failed to refresh subdomain list\n"); sbus_request_fail_and_finish(ireq->dbus_req, error); return; } /* Reply with the domain that was asked for */ for (iter = ireq->ifp_ctx->rctx->domains; iter != NULL; iter = get_next_domain(iter, true)) { if (strcasecmp(iter->name, state->name) == 0) { break; } } if (iter == NULL) { error = sbus_error_new(ireq->dbus_req, DBUS_ERROR_FAILED, "No such domain\n"); sbus_request_fail_and_finish(ireq->dbus_req, error); return; } path = ifp_reply_objpath(ireq, INFOPIPE_DOMAIN_PATH_PFX, iter->name); if (path == NULL) { DEBUG(SSSDBG_MINOR_FAILURE, "Could not create path for domain %s, skipping\n", iter->name); sbus_request_finish(ireq->dbus_req, NULL); return; } ret = infopipe_iface_FindDomainByName_finish(ireq->dbus_req, path); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Could not finish request!\n"); } } static struct sss_domain_info * get_domain_info_from_req(struct sbus_request *dbus_req, void *data) { struct ifp_ctx *ctx = NULL; struct sss_domain_info *domains = NULL; struct sss_domain_info *iter = NULL; const char *path = dbus_message_get_path(dbus_req->message); const char *raw_name = NULL; char *name = NULL; ctx = talloc_get_type(data, struct ifp_ctx); if (ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n"); return NULL; } raw_name = ifp_path_strip_prefix(path, INFOPIPE_DOMAIN_PATH_PFX "/"); if (raw_name == NULL) { return NULL; } name = ifp_bus_path_unescape(dbus_req, raw_name); if (name == NULL) { return NULL; } DEBUG(SSSDBG_TRACE_INTERNAL, "Looking for domain %s\n", name); domains = ctx->rctx->domains; for (iter = domains; iter != NULL; iter = get_next_domain(iter, true)) { if (strcasecmp(iter->name, name) == 0) { break; } } talloc_free(name); return iter; } static void get_server_list(struct sbus_request *dbus_req, void *data, const char ***_out, int *_out_len, bool backup) { static const char *srv[] = {"_srv_"}; struct sss_domain_info *dom = NULL; struct ifp_ctx *ctx = NULL; const char *conf_path = NULL; const char *option = NULL; const char **out = NULL; char **servers = NULL; int num_servers; errno_t ret; int i; *_out = NULL; *_out_len = 0; dom = get_domain_info_from_req(dbus_req, data); if (dom == NULL) { return; } if (dom->parent != NULL) { /* subdomains are not present in configuration */ ret = ENOENT; goto done; } ctx = talloc_get_type(data, struct ifp_ctx); if (ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Invalid ifp context!\n"); ret = ENOMEM; goto done; } conf_path = talloc_asprintf(dbus_req, CONFDB_DOMAIN_PATH_TMPL, dom->name); if (conf_path == NULL) { ret = ENOMEM; goto done; } /* TODO: replace hardcoded values with option names from the provider */ if (strcasecmp(dom->provider, "ldap") == 0) { option = backup == false ? "ldap_uri" : "ldap_backup_uri"; } else if (strcasecmp(dom->provider, "ipa") == 0) { option = backup == false ? "ipa_server" : "ipa_backup_server"; } else if (strcasecmp(dom->provider, "ad") == 0) { option = backup == false ? "ad_server" : "ad_backup_server"; } else { ret = EINVAL; goto done; } ret = confdb_get_string_as_list(ctx->rctx->cdb, dbus_req, conf_path, option, &servers); if (ret != EOK) { goto done; } for (num_servers = 0; servers[num_servers] != NULL; num_servers++); if (num_servers == 0) { ret = ENOENT; goto done; } out = talloc_zero_array(dbus_req, const char*, num_servers); if (out == NULL) { ret = ENOMEM; goto done; } for (i = 0; i < num_servers; i++) { out[i] = talloc_steal(out, servers[i]); } *_out = out; *_out_len = num_servers; ret = EOK; done: if (ret == ENOENT) { *_out = srv; *_out_len = 1; } return; } void ifp_dom_get_name(struct sbus_request *dbus_req, void *data, const char **_out) { RETURN_DOM_PROP_AS_STRING(dbus_req, data, _out, name); } void ifp_dom_get_provider(struct sbus_request *dbus_req, void *data, const char **_out) { RETURN_DOM_PROP_AS_STRING(dbus_req, data, _out, provider); } void ifp_dom_get_primary_servers(struct sbus_request *dbus_req, void *data, const char ***_out, int *_out_len) { get_server_list(dbus_req, data, _out, _out_len, false); } void ifp_dom_get_backup_servers(struct sbus_request *dbus_req, void *data, const char ***_out, int *_out_len) { get_server_list(dbus_req, data, _out, _out_len, true); } void ifp_dom_get_min_id(struct sbus_request *dbus_req, void *data, uint32_t *_out) { struct sss_domain_info *dom; *_out = 1; dom = get_domain_info_from_req(dbus_req, data); if (dom == NULL) { return; } *_out = dom->id_min; } void ifp_dom_get_max_id(struct sbus_request *dbus_req, void *data, uint32_t *_out) { struct sss_domain_info *dom; *_out = 0; dom = get_domain_info_from_req(dbus_req, data); if (dom == NULL) { return; } *_out = dom->id_max; } void ifp_dom_get_realm(struct sbus_request *dbus_req, void *data, const char **_out) { RETURN_DOM_PROP_AS_STRING(dbus_req, data, _out, realm); } void ifp_dom_get_forest(struct sbus_request *dbus_req, void *data, const char **_out) { RETURN_DOM_PROP_AS_STRING(dbus_req, data, _out, forest); } void ifp_dom_get_login_format(struct sbus_request *dbus_req, void *data, const char **_out) { RETURN_DOM_PROP_AS_STRING(dbus_req, data, _out, names->re_pattern); } void ifp_dom_get_fqdn_format(struct sbus_request *dbus_req, void *data, const char **_out) { RETURN_DOM_PROP_AS_STRING(dbus_req, data, _out, names->fq_fmt); } void ifp_dom_get_enumerable(struct sbus_request *dbus_req, void *data, bool *_out) { struct sss_domain_info *dom; *_out = false; dom = get_domain_info_from_req(dbus_req, data); if (dom == NULL) { return; } *_out = dom->enumerate; } void ifp_dom_get_use_fqdn(struct sbus_request *dbus_req, void *data, bool *_out) { struct sss_domain_info *dom; *_out = false; dom = get_domain_info_from_req(dbus_req, data); if (dom == NULL) { return; } *_out = dom->fqnames; } void ifp_dom_get_subdomain(struct sbus_request *dbus_req, void *data, bool *_out) { struct sss_domain_info *dom; *_out = false; dom = get_domain_info_from_req(dbus_req, data); if (dom == NULL) { return; } *_out = dom->parent ? true : false; } void ifp_dom_get_parent_domain(struct sbus_request *dbus_req, void *data, const char **_out) { struct sss_domain_info *dom; *_out = NULL; dom = get_domain_info_from_req(dbus_req, data); if (dom == NULL) { return; } if (dom->parent == NULL) { *_out = "/"; return; } *_out = ifp_reply_objpath(dbus_req, INFOPIPE_DOMAIN_PATH_PFX, dom->parent->name); }