Blob Blame History Raw
From 05ae58c86eae80c7e69fb809dc3cd89d0b7418f4 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
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 <sbose@redhat.com>
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
---
 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.
                             <quote>id_provider=ldap</quote> only.
                         </para>
                         <para>
+                            For an easy way to configure a non-POSIX domains, please
+                            see the <quote>Application domains</quote> section.
+                        </para>
+                        <para>
                             Default: posix
                         </para>
                     </listitem>
@@ -2692,6 +2696,79 @@ subdomain_inherit = ldap_purge_cache_timeout
             </variablelist>
         </para>
 
+        <refsect2 id='app_domains'>
+            <title>Application domains</title>
+            <para>
+                SSSD, with its D-Bus interface (see
+                <citerefentry>
+                    <refentrytitle>sssd-ifp</refentrytitle>
+                    <manvolnum>5</manvolnum>
+                </citerefentry>) 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
+                <quote>[domain/<replaceable>NAME</replaceable>]</quote>
+                section, the administrator can set up an
+                <quote>[application/<replaceable>NAME</replaceable>]</quote>
+                section that internally represents a domain with type
+                <quote>application</quote> optionally inherits settings
+                from a tradition SSSD domain.
+            </para>
+            <para>
+                Please note that the application domain must still be
+                explicitly enabled in the <quote>domains</quote> parameter
+                so that the lookup order between the application domain
+                and its POSIX sibling domain is set correctly.
+            </para>
+            <variablelist>
+                <title>Application domain parameters</title>
+                <varlistentry>
+                    <term>inherit_from (string)</term>
+                    <listitem>
+                        <para>
+                            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 <quote>sibling</quote>
+                            domain settings.
+                        </para>
+                        <para>
+                            Default: Not set
+                        </para>
+                    </listitem>
+                </varlistentry>
+            </variablelist>
+            <para>
+                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.
+            </para>
+<programlisting>
+[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
+</programlisting>
+        </refsect2>
+
         <refsect2 id='local_domain'>
             <title>The local domain section</title>
             <para>
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