From 05ae58c86eae80c7e69fb809dc3cd89d0b7418f4 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Mon, 27 Mar 2017 09:48:46 +0200 Subject: [PATCH 62/72] CONFDB: Allow configuring [application] sections as non-POSIX domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related to: https://pagure.io/SSSD/sssd/issue/3310 Allows to add a new section: [application/$name] This section internally (on the confdb level) expands to: [domain/$name] domain_type = application The reasons to add this new section is two-fold. One, to make the configuration of application domains more explicit and two, to make it possible to share configuration between two domains, one POSIX and one non-POSIX by application domain's inherit_from option: [application/$name] inherit_from = posix_domain_name Reviewed-by: Sumit Bose Reviewed-by: Pavel Březina --- src/confdb/confdb.c | 288 ++++++++++++++++++++++++++++++++++++++++++++--- src/confdb/confdb.h | 4 + src/config/cfg_rules.ini | 9 +- src/man/sssd.conf.5.xml | 77 +++++++++++++ src/monitor/monitor.c | 8 ++ 5 files changed, 368 insertions(+), 18 deletions(-) diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c index 70a1eb7b2c7e83dfa9d217a15c7d3d4c8580b891..88e114457deac3ca50c291a131122624fb6f6fe4 100644 --- a/src/confdb/confdb.c +++ b/src/confdb/confdb.c @@ -813,6 +813,50 @@ done: return ret; } +static int confdb_get_domain_section(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + const char *section, + const char *name, + struct ldb_result **_res) +{ + TALLOC_CTX *tmp_ctx; + int ret; + struct ldb_result *res; + struct ldb_dn *dn; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + dn = ldb_dn_new_fmt(tmp_ctx, cdb->ldb, "cn=%s,%s", name, section); + if (dn == NULL) { + ret = ENOMEM; + goto done; + } + + ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn, + LDB_SCOPE_BASE, NULL, NULL); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + goto done; + } + + if (res->count == 0) { + ret = ENOENT; + goto done; + } else if (res->count > 1) { + ret = E2BIG; + goto done; + } + + *_res = talloc_steal(mem_ctx, res); + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + static int confdb_get_domain_internal(struct confdb_ctx *cdb, TALLOC_CTX *mem_ctx, const char *name, @@ -821,7 +865,6 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, struct sss_domain_info *domain; struct ldb_result *res; TALLOC_CTX *tmp_ctx; - struct ldb_dn *dn; const char *tmp; int ret, val; uint32_t entry_cache_timeout; @@ -833,23 +876,15 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, tmp_ctx = talloc_new(mem_ctx); if (!tmp_ctx) return ENOMEM; - dn = ldb_dn_new_fmt(tmp_ctx, cdb->ldb, - "cn=%s,%s", name, CONFDB_DOMAIN_BASEDN); - if (!dn) { - ret = ENOMEM; - goto done; - } - - ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn, - LDB_SCOPE_BASE, NULL, NULL); - if (ret != LDB_SUCCESS) { - ret = EIO; - goto done; - } - - if (res->count != 1) { + ret = confdb_get_domain_section(tmp_ctx, cdb, CONFDB_DOMAIN_BASEDN, + name, &res); + if (ret == ENOENT) { DEBUG(SSSDBG_FATAL_FAILURE, "Unknown domain [%s]\n", name); - ret = ENOENT; + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Error %d: %s while retrieving %s\n", + ret, sss_strerror(ret), name); goto done; } @@ -1841,3 +1876,222 @@ int confdb_ensure_files_domain(struct confdb_ctx *cdb, return activate_files_domain(cdb, implicit_files_dom_name); #endif /* ADD_FILES_DOMAIN */ } + +static int confdb_get_parent_domain(TALLOC_CTX *mem_ctx, + const char *name, + struct confdb_ctx *cdb, + struct ldb_result *app_dom, + struct ldb_result **_parent_dom) +{ + const char *inherit_from; + + inherit_from = ldb_msg_find_attr_as_string(app_dom->msgs[0], + CONFDB_DOMAIN_INHERIT_FROM, NULL); + if (inherit_from == NULL) { + DEBUG(SSSDBG_CONF_SETTINGS, + "%s does not inherit from any POSIX domain\n", name); + *_parent_dom = NULL; + return EOK; + } + + return confdb_get_domain_section(mem_ctx, cdb, + CONFDB_DOMAIN_BASEDN, inherit_from, + _parent_dom); +} + +static int confdb_add_app_domain(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + const char *name) +{ + char *cdb_path = NULL; + const char *val[2] = { NULL, NULL }; + int ret; + + cdb_path = talloc_asprintf(mem_ctx, CONFDB_DOMAIN_PATH_TMPL, name); + if (cdb_path == NULL) { + return ENOMEM; + } + + val[0] = CONFDB_DOMAIN_TYPE_APP; + ret = confdb_add_param(cdb, true, cdb_path, CONFDB_DOMAIN_TYPE, val); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add id_provider [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + return EOK; +} + +static int confdb_merge_parent_domain(const char *name, + struct confdb_ctx *cdb, + struct ldb_result *app_section) +{ + int ret; + int ldb_flag; + struct ldb_result *parent_domain = NULL; + struct ldb_message *replace_msg = NULL; + struct ldb_message *app_msg = NULL; + struct ldb_dn *domain_dn; + TALLOC_CTX *tmp_ctx = NULL; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); + return ENOMEM; + } + + domain_dn = ldb_dn_new_fmt(tmp_ctx, + cdb->ldb, + "%s=%s,%s", + CONFDB_DOMAIN_ATTR, + name, + CONFDB_DOMAIN_BASEDN); + if (domain_dn == NULL) { + ret = ENOMEM; + goto done; + } + + /* Copy the parent domain parameters */ + ret = confdb_get_parent_domain(tmp_ctx, name, cdb, + app_section, &parent_domain); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot retrieve the parent domain [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + if (parent_domain != NULL) { + replace_msg = ldb_msg_copy(tmp_ctx, parent_domain->msgs[0]); + if (replace_msg == NULL) { + ret = ENOMEM; + goto done; + } + replace_msg->dn = domain_dn; + + for (unsigned i = 0; i < replace_msg->num_elements; i++) { + replace_msg->elements[i].flags = LDB_FLAG_MOD_ADD; + } + + ret = ldb_modify(cdb->ldb, replace_msg); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + DEBUG(SSSDBG_OP_FAILURE, + "Inheriting options from parent domain failed [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + } + + /* Finally, add any app-domain specific overrides */ + app_msg = ldb_msg_new(tmp_ctx); + if (app_msg == NULL) { + ret = ENOMEM; + goto done; + } + app_msg->dn = domain_dn; + + for (unsigned i = 0; i < app_section->msgs[0]->num_elements; i++) { + struct ldb_message_element *el = NULL; + + if (replace_msg != NULL) { + el = ldb_msg_find_element(replace_msg, + app_section->msgs[0]->elements[i].name); + if (el == NULL) { + /* Adding an element */ + ldb_flag = LDB_FLAG_MOD_ADD; + } else { + /* Overriding an element */ + ldb_flag = LDB_FLAG_MOD_REPLACE; + } + } else { + /* If there was no domain to inherit from, just add all */ + ldb_flag = LDB_FLAG_MOD_ADD; + } + + ret = ldb_msg_add(app_msg, + &app_section->msgs[0]->elements[i], + ldb_flag); + if (ret != EOK) { + continue; + } + } + + ret = ldb_modify(cdb->ldb, app_msg); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + DEBUG(SSSDBG_OP_FAILURE, + "Adding app-specific options failed [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + DEBUG(SSSDBG_TRACE_LIBS, "Added a domain section for %s\n", name); + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +int confdb_expand_app_domains(struct confdb_ctx *cdb) +{ + int ret; + char **domlist; + TALLOC_CTX *tmp_ctx; + struct ldb_result *app_domain = NULL; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = confdb_get_string_as_list(cdb, tmp_ctx, + CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_ACTIVE_DOMAINS, + &domlist); + if (ret == ENOENT) { + DEBUG(SSSDBG_FATAL_FAILURE, "No domains configured, fatal error!\n"); + goto done; + } else if (ret != EOK ) { + DEBUG(SSSDBG_FATAL_FAILURE, "Fatal error retrieving domains list!\n"); + goto done; + } + + for (int i = 0; domlist[i]; i++) { + ret = confdb_get_domain_section(tmp_ctx, cdb, + CONFDB_APP_DOMAIN_BASEDN, domlist[i], + &app_domain); + if (ret == ENOENT) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "%s is not an app domain\n", domlist[i]); + continue; + } else if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Error %d: %s while retrieving %s\n", + ret, sss_strerror(ret), domlist[i]); + goto done; + } + + ret = confdb_add_app_domain(tmp_ctx, cdb, domlist[i]); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot add the app domain section [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = confdb_merge_parent_domain(domlist[i], cdb, app_domain); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot add options into the app domain section [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + } + + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index a4046610f3cdbdb832de8924bf4397fb0018f2db..5a8d377c312f641f544b1c7cf38826192462ea3c 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -164,6 +164,7 @@ /* Domains */ #define CONFDB_DOMAIN_PATH_TMPL "config/domain/%s" #define CONFDB_DOMAIN_BASEDN "cn=domain,cn=config" +#define CONFDB_APP_DOMAIN_BASEDN "cn=application,cn=config" #define CONFDB_DOMAIN_ID_PROVIDER "id_provider" #define CONFDB_DOMAIN_AUTH_PROVIDER "auth_provider" #define CONFDB_DOMAIN_ACCESS_PROVIDER "access_provider" @@ -212,6 +213,7 @@ #define CONFDB_DOMAIN_TYPE "domain_type" #define CONFDB_DOMAIN_TYPE_POSIX "posix" #define CONFDB_DOMAIN_TYPE_APP "application" +#define CONFDB_DOMAIN_INHERIT_FROM "inherit_from" /* Local Provider */ #define CONFDB_LOCAL_DEFAULT_SHELL "default_shell" @@ -398,6 +400,8 @@ int confdb_get_domains(struct confdb_ctx *cdb, int confdb_ensure_files_domain(struct confdb_ctx *cdb, const char *implicit_files_dom_name); +int confdb_expand_app_domains(struct confdb_ctx *cdb); + /** * Get a null-terminated linked-list of all domain names * @param[in] mem_ctx The parent memory context for the value list diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini index 3c857236eaa55b313d176bc4bb479918163b60d5..8fd2d2c5236246394353a88c50d1510bd6233f77 100644 --- a/src/config/cfg_rules.ini +++ b/src/config/cfg_rules.ini @@ -12,6 +12,7 @@ section = secrets section = kcm section_re = ^secrets/users/[0-9]\+$ section_re = ^domain/.*$ +section_re = ^application/.*$ [rule/allowed_sssd_options] validator = ini_allowed_options @@ -286,7 +287,7 @@ option = responder_idle_timeout [rule/allowed_domain_options] validator = ini_allowed_options -section_re = ^domain/.*$ +section_re = ^(domain|application)/.*$ option = debug option = debug_level @@ -684,3 +685,9 @@ option = ldap_user_ssh_public_key option = ldap_user_uid_number option = ldap_user_uuid option = ldap_use_tokengroups + +[rule/allowed_application_options] +validator = ini_allowed_options +section_re = ^application/.*$ + +option = inherit_from diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml index 9abcff84a95ea1b27e36845e830cc125fdc89f90..8294793c765bfa6bf481693c7d7f206950454681 100644 --- a/src/man/sssd.conf.5.xml +++ b/src/man/sssd.conf.5.xml @@ -1539,6 +1539,10 @@ pam_account_locked_message = Account locked, please contact help desk. id_provider=ldap only. + For an easy way to configure a non-POSIX domains, please + see the Application domains section. + + Default: posix @@ -2692,6 +2696,79 @@ subdomain_inherit = ldap_purge_cache_timeout + + Application domains + + SSSD, with its D-Bus interface (see + + sssd-ifp + 5 + ) is appealing to applications + as a gateway to an LDAP directory where users and groups + are stored. However, contrary to the traditional SSSD + deployment where all users and groups either have POSIX + attributes or those attributes can be inferred from the + Windows SIDs, in many cases the users and groups in the + application support scenario have no POSIX attributes. + Instead of setting a + [domain/NAME] + section, the administrator can set up an + [application/NAME] + section that internally represents a domain with type + application optionally inherits settings + from a tradition SSSD domain. + + + Please note that the application domain must still be + explicitly enabled in the domains parameter + so that the lookup order between the application domain + and its POSIX sibling domain is set correctly. + + + Application domain parameters + + inherit_from (string) + + + The SSSD POSIX-type domain the application + domain inherits all settings from. The + application domain can moreover add its own + settings to the application settings that augment + or override the sibling + domain settings. + + + Default: Not set + + + + + + The following example illustrates the use of an application + domain. In this setup, the POSIX domain is connected to an LDAP + server and is used by the OS through the NSS responder. In addition, + the application domains also requests the telephoneNumber attribute, + stores it as the phone attribute in the cache and makes the phone + attribute reachable through the D-Bus interface. + + +[sssd] +domains = appdom, posixdom + +[ifp] +user_attributes = +phone + +[domain/posixdom] +id_provider = ldap +ldap_uri = ldap://ldap.example.com +ldap_search_base = dc=example,dc=com + +[application/appdom] +inherit_from = posixdom +ldap_user_extra_attrs = phone:telephoneNumber + + + The local domain section diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c index 7e7b5a07d11aecf1c0b11592213b90d385fd5076..2753b46667f7ae0b022776862c67a327d3356d6d 100644 --- a/src/monitor/monitor.c +++ b/src/monitor/monitor.c @@ -1064,6 +1064,14 @@ static int get_monitor_config(struct mt_ctx *ctx) /* Not fatal */ } + ret = confdb_expand_app_domains(ctx->cdb); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to expand application domains\n"); + /* This must not be fatal so that SSSD keeps running and lets + * admin correct the error. + */ + } + ret = confdb_get_domains(ctx->cdb, &ctx->domains); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "No domains configured.\n"); -- 2.9.3