/* 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 "config.h" #include "providers/data_provider/dp.h" #include "providers/data_provider/dp_private.h" #include "providers/data_provider/dp_builtin.h" #include "providers/backend.h" #include "util/util.h" #define DP_TARGET_INIT_FN "sssm_%s_%s_init" #define DP_PROVIDER_OPT "%s_provider" #define DP_ACCESS_PERMIT "permit" #define DP_ACCESS_DENY "deny" #define DP_NO_PROVIDER "none" bool _dp_target_enabled(struct data_provider *provider, const char *module_name, ...) { struct dp_target *target; enum dp_targets type; va_list ap; bool bret; if (provider == NULL || provider->targets == NULL) { return false; } bret = false; va_start(ap, module_name); while ((type = va_arg(ap, enum dp_targets)) != DP_TARGET_SENTINEL) { target = provider->targets[type]; if (target == NULL || target->module_name == NULL) { DEBUG(SSSDBG_MINOR_FAILURE, "Uninitialized target %s\n", dp_target_to_string(type)); continue; } if (module_name == NULL) { bret = true; goto done; } if (strcmp(target->module_name, module_name) == 0) { bret = true; goto done; } } done: va_end(ap); return bret; } struct dp_module *dp_target_module(struct data_provider *provider, enum dp_targets target) { if (provider == NULL || provider->targets == NULL) { return NULL; } if (target >= DP_TARGET_SENTINEL || provider->targets[target] == NULL) { return NULL; } return provider->targets[target]->module; } void *dp_get_module_data(struct dp_module *dp_module) { return dp_module == NULL ? NULL : dp_module->module_data; } const char *dp_target_to_string(enum dp_targets target) { switch (target) { case DPT_ID: return "id"; case DPT_AUTH: return "auth"; case DPT_ACCESS: return "access"; case DPT_CHPASS: return "chpass"; case DPT_SUDO: return "sudo"; case DPT_AUTOFS: return "autofs"; case DPT_SELINUX: return "selinux"; case DPT_HOSTID: return "hostid"; case DPT_SUBDOMAINS: return "subdomains"; case DP_TARGET_SENTINEL: return NULL; } return NULL; } bool dp_target_initialized(struct dp_target **targets, enum dp_targets type) { if (targets == NULL || targets[type] == NULL) { return false; } return targets[type]->initialized; } static bool dp_target_sudo_enabled(struct be_ctx *be_ctx) { TALLOC_CTX *tmp_ctx; char **services; char *module; bool responder_enabled; bool enable; errno_t ret; int i; /* Do not disable it in case of error. */ enable = true; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); return enable; } 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, sss_strerror(ret)); goto done; } responder_enabled = false; 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, &module); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Unable to read from confdb [%d]: %s\n", ret, sss_strerror(ret)); goto done; } if (!responder_enabled) { if (module == NULL) { DEBUG(SSSDBG_TRACE_FUNC, "SUDO is not listed in services, " "disabling SUDO module.\n"); enable = false; goto done; } else if (strcmp(module, DP_NO_PROVIDER) != 0) { DEBUG(SSSDBG_MINOR_FAILURE, "SUDO provider is set, but it is not " "listed in active services. SUDO support will not work!\n"); enable = true; goto done; } } enable = true; done: talloc_free(tmp_ctx); return enable; } static const char *dp_target_module_name(struct dp_target **targets, enum dp_targets type) { if (targets[type] == NULL) { return NULL; } return targets[type]->module_name; } static const char *dp_target_default_module(struct dp_target **targets, enum dp_targets target) { switch (target) { case DPT_ID: return NULL; case DPT_ACCESS: return "permit"; case DPT_CHPASS: return dp_target_module_name(targets, DPT_AUTH); case DP_TARGET_SENTINEL: return NULL; default: return dp_target_module_name(targets, DPT_ID); } } static errno_t dp_target_run_constructor(struct dp_target *target, struct be_ctx *be_ctx) { char *fn_name = NULL; dp_target_init_fn fn; char *error; errno_t ret; fn_name = talloc_asprintf(target, DP_TARGET_INIT_FN, target->module->name, target->name); if (fn_name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n"); return ENOMEM; } dlerror(); /* clear any error */ fn = (dp_target_init_fn)dlsym(target->module->libhandle, fn_name); if (fn != NULL) { DEBUG(SSSDBG_TRACE_FUNC, "Executing target [%s] constructor\n", target->name); ret = fn(target, be_ctx, target->module->module_data, target->methods); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Target [%s] constructor failed " "[%d]: %s\n", target->name, ret, sss_strerror(ret)); goto done; } } else { error = dlerror(); if (error == NULL || !target->explicitly_configured) { /* Not found. */ ret = ELIBBAD; goto done; } else { /* Error. */ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to load target [%s] " "constructor: %s\n", target->name, error); ret = ELIBBAD; goto done; } } target->initialized = true; ret = EOK; done: talloc_free(fn_name); return ret; } static errno_t dp_target_special(struct be_ctx *be_ctx, struct dp_target *target, const char *module_name) { if (strcasecmp(module_name, DP_NO_PROVIDER) == 0) { DEBUG(SSSDBG_TRACE_FUNC, "Target [%s] is explicitly disabled.\n", target->name); target->initialized = false; target->module = NULL; return EOK; } if (target->target == DPT_ACCESS) { if (strcmp(module_name, DP_ACCESS_PERMIT) == 0) { dp_set_method(target->methods, DPM_ACCESS_HANDLER, dp_access_permit_handler_send, dp_access_permit_handler_recv, NULL, void, struct pam_data, struct pam_data *); target->module = NULL; target->initialized = true; return EOK; } if (strcmp(module_name, DP_ACCESS_DENY) == 0) { dp_set_method(target->methods, DPM_ACCESS_HANDLER, dp_access_deny_handler_send, dp_access_deny_handler_recv, NULL, void, struct pam_data, struct pam_data *); target->module = NULL; target->initialized = true; return EOK; } } if (target->target == DPT_SUDO) { if (dp_target_sudo_enabled(be_ctx)) { return EAGAIN; } else { target->module = NULL; target->initialized = false; return EOK; } } return EAGAIN; } static errno_t dp_target_init(struct be_ctx *be_ctx, struct data_provider *provider, struct dp_module **modules, struct dp_target *target) { errno_t ret; DEBUG(SSSDBG_TRACE_FUNC, "Initializing target [%s] with module [%s]\n", target->name, target->module_name); /* We have already name, module name and target set. We just load * the module and initialize it. */ target->methods = talloc_zero_array(target, struct dp_method, DP_METHOD_SENTINEL + 1); if (target->methods == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n"); ret = ENOMEM; goto done; } /* Handle special cases that do not require opening a module. */ ret = dp_target_special(be_ctx, target, target->module_name); if (ret == EOK || ret != EAGAIN) { goto done; } /* Load module first. Memory context is modules, not target here. */ target->module = dp_load_module(modules, be_ctx, provider, modules, target->module_name); if (target->module == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "Unable to load module %s\n", target->module_name); ret = ELIBBAD; goto done; } /* Run constructor. */ ret = dp_target_run_constructor(target, be_ctx); if (!target->explicitly_configured && (ret == ELIBBAD || ret == ENOTSUP)) { /* Target not found but it wasn't explicitly * configured so we shall just continue. */ DEBUG(SSSDBG_CRIT_FAILURE, "Target [%s] is not supported by " "module [%s].\n", target->name, target->module_name); ret = EOK; goto done; } else if (ret != EOK) { goto done; } ret = EOK; done: if (ret != EOK) { talloc_free(target->methods); } return ret; } static char *dp_get_module_name(TALLOC_CTX *mem_ctx, struct confdb_ctx *confdb_ctx, const char *conf_path, struct dp_target **targets, enum dp_targets type, bool *_is_default) { const char *name; const char *default_module; char *module; char *option; errno_t ret; name = dp_target_to_string(type); if (name == NULL) { return NULL; } option = talloc_asprintf(mem_ctx, DP_PROVIDER_OPT, name); if (option == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n"); return NULL; } ret = confdb_get_string(confdb_ctx, mem_ctx, conf_path, option, NULL, &module); talloc_free(option); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read provider value " "[%d]: %s\n", ret, sss_strerror(ret)); return NULL; } if (module != NULL) { *_is_default = false; return module; } *_is_default = true; default_module = dp_target_default_module(targets, type); return talloc_strdup(mem_ctx, default_module); } static errno_t dp_load_configuration(struct confdb_ctx *cdb, const char *conf_path, struct dp_target **targets) { enum dp_targets type; const char *name; bool is_default; char *module; errno_t ret; for (type = 0; type < DP_TARGET_SENTINEL; type++) { name = dp_target_to_string(type); if (name == NULL) { ret = ERR_INTERNAL; goto done; } module = dp_get_module_name(NULL, cdb, conf_path, targets, type, &is_default); if (module == NULL) { DEBUG(SSSDBG_CONF_SETTINGS, "No provider is specified for" " [%s]\n", name); continue; } else { DEBUG(SSSDBG_CONF_SETTINGS, "Using [%s] provider for [%s]\n", module, name); } targets[type]->explicitly_configured = is_default == false; targets[type]->name = name; targets[type]->target = type; targets[type]->module_name = talloc_steal(targets[type], module); } ret = EOK; done: return ret; } static errno_t dp_load_targets(struct be_ctx *be_ctx, struct data_provider *provider, struct dp_target **targets, struct dp_module **modules) { enum dp_targets type; errno_t ret; /* We load the configuration first and store module name to each target. * This way we ensure that we have this information available during * module initialization. */ ret = dp_load_configuration(be_ctx->cdb, be_ctx->conf_path, targets); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to load DP configuration " "[%d]: %s\n", ret, sss_strerror(ret)); goto done; } for (type = 0; type < DP_TARGET_SENTINEL; type++) { ret = dp_target_init(be_ctx, provider, modules, targets[type]); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to load target [%s] " "[%d]: %s.\n", targets[type]->name, ret, sss_strerror(ret)); ret = ERR_INTERNAL; goto done; } } ret = EOK; done: return ret; } errno_t dp_init_targets(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx, struct data_provider *provider, struct dp_module **modules) { struct dp_target **targets; enum dp_targets type; errno_t ret; /* Even though we know the exact number of targets we will allocate * them all dynamically so we can have correct talloc hierarchy where * all private data are attached to the target they belong to. */ targets = talloc_zero_array(mem_ctx, struct dp_target *, DP_TARGET_SENTINEL + 1); if (targets == NULL) { ret = ENOMEM; goto done; } for (type = 0; type != DP_TARGET_SENTINEL; type++) { targets[type] = talloc_zero(targets, struct dp_target); if (targets[type] == NULL) { ret = ENOMEM; goto done; } } /* We want this to be already available. */ provider->targets = targets; ret = dp_load_targets(be_ctx, provider, targets, modules); done: if (ret != EOK) { provider->targets = NULL; talloc_free(targets); } return ret; }