/* 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 "sbus/sssd_dbus_errors.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_failover_list_services_ldap(struct be_ctx *be_ctx, const char **services, int *_count) { struct be_svc_data *svc; int count; count = 0; DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) { services[count] = talloc_strdup(services, svc->name); if (services[count] == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n"); return ENOMEM; } count++; } *_count = count; return EOK; } static errno_t dp_failover_list_services_ad(struct be_ctx *be_ctx, struct sss_domain_info *domain, const char **services, int *_count) { char *fo_svc_name = NULL; struct be_svc_data *svc; errno_t ret; int count; fo_svc_name = talloc_asprintf(services, "sd_%s", domain->name); if (fo_svc_name == NULL) { ret = ENOMEM; goto done; } count = 0; DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) { /* Drop each sd_gc_* since this service is not used with AD at all, * we only connect to AD_GC for global catalog. */ if (strncasecmp(svc->name, "sd_gc_", strlen("sd_gc_")) == 0) { continue; } /* Drop all subdomain services for different domain. */ if (strncasecmp(svc->name, "sd_", strlen("sd_")) == 0) { if (!IS_SUBDOMAIN(domain)) { continue; } if (strcasecmp(svc->name, fo_svc_name) != 0) { continue; } } if (IS_SUBDOMAIN(domain)) { /* Drop AD since we connect to subdomain.com for LDAP. */ if (strcasecmp(svc->name, "AD") == 0) { continue; } } services[count] = talloc_strdup(services, svc->name); if (services[count] == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n"); ret = ENOMEM; goto done; } count++; } *_count = count; ret = EOK; done: talloc_free(fo_svc_name); return ret; } static errno_t dp_failover_list_services_ipa(struct be_ctx *be_ctx, struct sss_domain_info *domain, const char **services, int *_count) { struct be_svc_data *svc; char *fo_svc_name = NULL; char *fo_gc_name = NULL; errno_t ret; int count; fo_svc_name = talloc_asprintf(services, "sd_%s", domain->name); if (fo_svc_name == NULL) { ret = ENOMEM; goto done; } fo_gc_name = talloc_asprintf(services, "sd_gc_%s", domain->name); if (fo_gc_name == NULL) { ret = ENOMEM; goto done; } count = 0; DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) { /* Drop all subdomain services for different domain. */ if (strncasecmp(svc->name, "sd_", strlen("sd_")) == 0) { if (!IS_SUBDOMAIN(domain)) { continue; } if (strcasecmp(svc->name, fo_svc_name) != 0 && strcasecmp(svc->name, fo_gc_name) != 0) { continue; } } services[count] = talloc_strdup(services, svc->name); if (services[count] == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n"); return ENOMEM; } count++; } *_count = count; ret = EOK; done: talloc_free(fo_svc_name); talloc_free(fo_gc_name); return ret; } enum dp_fo_svc_type { DP_FO_SVC_LDAP = 0, DP_FO_SVC_AD = 1, DP_FO_SVC_IPA = 1 << 1, DP_FO_SVC_MIXED = DP_FO_SVC_AD | DP_FO_SVC_IPA }; errno_t dp_failover_list_services(struct sbus_request *sbus_req, void *dp_cli, const char *domname) { enum dp_fo_svc_type svc_type = DP_FO_SVC_LDAP; struct sss_domain_info *domain; struct be_ctx *be_ctx; struct be_svc_data *svc; const char **services; int num_services; errno_t ret; be_ctx = dp_client_be(dp_cli); if (SBUS_IS_STRING_EMPTY(domname)) { domain = be_ctx->domain; } else { domain = find_domain_by_name(be_ctx->domain, domname, false); if (domain == NULL) { sbus_request_reply_error(sbus_req, SBUS_ERROR_UNKNOWN_DOMAIN, "Unknown domain %s", domname); return EOK; } } /** * Returning list of failover services is currently rather difficult * since there is only one failover context for the whole backend. * * The list of services for the given domain depends on whether it is * a master domain or a subdomain and whether we are using IPA, AD or * LDAP backend. * * For LDAP we just return everything we have. * For AD master domain we return AD, AD_GC. * For AD subdomain we return subdomain.com, AD_GC. * For IPA in client mode we return IPA. * For IPA in server mode we return IPA for master domain and * subdomain.com, gc_subdomain.com for subdomain. * * We also return everything else for all cases if any other service * such as kerberos is configured separately. */ /* Allocate enough space. */ num_services = 0; DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) { num_services++; if (strcasecmp(svc->name, "AD") == 0) { svc_type |= DP_FO_SVC_AD; } else if (strcasecmp(svc->name, "IPA") == 0) { svc_type |= DP_FO_SVC_IPA; } } services = talloc_zero_array(sbus_req, const char *, num_services); if (services == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n"); return ENOMEM; } /* Fill the list. */ switch (svc_type) { case DP_FO_SVC_LDAP: case DP_FO_SVC_MIXED: ret = dp_failover_list_services_ldap(be_ctx, services, &num_services); break; case DP_FO_SVC_AD: ret = dp_failover_list_services_ad(be_ctx, domain, services, &num_services); break; case DP_FO_SVC_IPA: ret = dp_failover_list_services_ipa(be_ctx, domain, services, &num_services); break; default: ret = ERR_INTERNAL; break; } if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create service list [%d]: %s\n", ret, sss_strerror(ret)); talloc_free(services); return ret; } iface_dp_failover_ListServices_finish(sbus_req, services, num_services); return EOK; } errno_t dp_failover_active_server(struct sbus_request *sbus_req, void *dp_cli, const char *service_name) { struct be_ctx *be_ctx; struct be_svc_data *svc; const char *server; bool found = false; be_ctx = dp_client_be(dp_cli); DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) { if (strcmp(svc->name, service_name) == 0) { found = true; break; } } if (!found) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get server name\n"); sbus_request_reply_error(sbus_req, SBUS_ERROR_NOT_FOUND, "Unknown service name"); return EOK; } if (svc->last_good_srv == NULL) { server = ""; } else { server = fo_get_server_name(svc->last_good_srv); if (server == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get server name\n"); sbus_request_reply_error(sbus_req, SBUS_ERROR_INTERNAL, "Unable to get server name"); return EOK; } } iface_dp_failover_ActiveServer_finish(sbus_req, server); return EOK; } errno_t dp_failover_list_servers(struct sbus_request *sbus_req, void *dp_cli, const char *service_name) { struct be_ctx *be_ctx; struct be_svc_data *svc; const char **servers; bool found = false; size_t count; be_ctx = dp_client_be(dp_cli); DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) { if (strcmp(svc->name, service_name) == 0) { found = true; break; } } if (!found) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get server list\n"); sbus_request_reply_error(sbus_req, SBUS_ERROR_NOT_FOUND, "Unknown service name"); return EOK; } servers = fo_svc_server_list(sbus_req, svc->fo_service, &count); if (servers == NULL) { return ENOMEM; } iface_dp_failover_ListServers_finish(sbus_req, servers, (int)count); return EOK; }