From 8a24dd94e79be8c3aa6d64cd7901e2c0696ffc1f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
Date: Tue, 22 Mar 2016 14:09:34 +0100
Subject: [PATCH] confdb: Make it possible to use config snippets

Resolves:
https://fedorahosted.org/sssd/ticket/2247

Signed-off-by: Lukas Slebodnik <lslebodn@redhat.com>
---
 Makefile.am                   |  4 ++-
 contrib/sssd.spec.in          |  1 +
 src/confdb/confdb.h           |  1 +
 src/confdb/confdb_setup.c     | 31 +++++++---------------
 src/confdb/confdb_setup.h     |  1 +
 src/external/libini_config.m4 | 12 +++++++++
 src/monitor/monitor.c         |  6 +++--
 src/tools/common/sss_tools.c  |  4 ++-
 src/util/sss_ini.c            | 62 ++++++++++++++++++++++++++++++++++++++++++-
 src/util/sss_ini.h            |  3 ++-
 10 files changed, 98 insertions(+), 27 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 152fdbf..d87896d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3849,6 +3849,7 @@ SSSD_USER_DIRS = \
     $(DESTDIR)$(pubconfpath)/krb5.include.d \
     $(DESTDIR)$(gpocachepath) \
     $(DESTDIR)$(sssdconfdir) \
+    $(DESTDIR)$(sssdconfdir)/conf.d \
     $(DESTDIR)$(sssddefaultconfdir) \
     $(DESTDIR)$(logpath) \
     $(NULL)
@@ -3883,7 +3884,8 @@ endif
 	$(INSTALL) -d -m 0755 $(DESTDIR)$(mcpath) $(DESTDIR)$(pipepath) \
             $(DESTDIR)$(pubconfpath) \
             $(DESTDIR)$(pubconfpath)/krb5.include.d $(DESTDIR)$(gpocachepath)
-	$(INSTALL) -d -m 0711 $(DESTDIR)$(sssdconfdir)
+	$(INSTALL) -d -m 0711 $(DESTDIR)$(sssdconfdir) \
+                          $(DESTDIR)$(sssdconfdir)/conf.d
 
 if HAVE_DOXYGEN
 docs:
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 769a074..1c2c593 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -782,6 +782,7 @@ done
 %attr(755,sssd,sssd) %dir %{gpocachepath}
 %attr(750,sssd,sssd) %dir %{_var}/log/%{name}
 %attr(711,sssd,sssd) %dir %{_sysconfdir}/sssd
+%attr(711,sssd,sssd) %dir %{_sysconfdir}/sssd/conf.d
 %ghost %attr(0600,sssd,sssd) %config(noreplace) %{_sysconfdir}/sssd/sssd.conf
 %if (0%{?use_systemd} == 1)
 %attr(755,root,root) %dir %{_sysconfdir}/systemd/system/sssd.service.d
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 0a3d6a0..2cd75b9 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -42,6 +42,7 @@
 #define CONFDB_FILE "config.ldb"
 #define SSSD_DEFAULT_CONFIG_FILE SSSD_DEFAULT_CONF_DIR"/sssd.conf"
 #define SSSD_CONFIG_FILE SSSD_CONF_DIR"/sssd.conf"
+#define CONFDB_DEFAULT_CONFIG_DIR SSSD_CONF_DIR"/conf.d"
 #define SSSD_MIN_ID 1
 #define SSSD_LOCAL_MINID 1000
 #define CONFDB_DEFAULT_SHELL_FALLBACK "/bin/sh"
diff --git a/src/confdb/confdb_setup.c b/src/confdb/confdb_setup.c
index 3443d68..1e4b9a2 100644
--- a/src/confdb/confdb_setup.c
+++ b/src/confdb/confdb_setup.c
@@ -127,14 +127,14 @@ static int confdb_create_base(struct confdb_ctx *cdb)
     return EOK;
 }
 
-static int confdb_init_db(const char *config_file, struct confdb_ctx *cdb)
+static int confdb_init_db(const char *config_file, const char *config_dir,
+                          struct confdb_ctx *cdb)
 {
     TALLOC_CTX *tmp_ctx;
     int ret;
     int sret = EOK;
     int version;
     char timestr[21];
-    char *lasttimestr;
     bool in_transaction = false;
     const char *config_ldif;
     const char *vals[2] = { timestr, NULL };
@@ -206,9 +206,6 @@ static int confdb_init_db(const char *config_file, struct confdb_ctx *cdb)
         goto done;
     }
 
-    /* Determine if the conf file has changed since we last updated
-     * the confdb
-     */
     ret = sss_ini_get_stat(init_data);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE,
@@ -225,22 +222,13 @@ static int confdb_init_db(const char *config_file, struct confdb_ctx *cdb)
               "Failed to convert time_t to string ??\n");
         ret = errno ? errno : EFAULT;
     }
-    ret = confdb_get_string(cdb, tmp_ctx, "config", "lastUpdate",
-                            NULL, &lasttimestr);
-    if (ret == EOK) {
-
-        /* check if we lastUpdate and last file modification change differ*/
-        if ((lasttimestr != NULL) && (strcmp(lasttimestr, timestr) == 0)) {
-            /* not changed, get out, nothing more to do */
-            ret = EOK;
-            goto done;
-        }
-    } else {
-        DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get lastUpdate attribute.\n");
-        goto done;
-    }
 
-    ret = sss_ini_get_config(init_data, config_file);
+    /* FIXME: Determine if the conf file or any snippet has changed
+     * since we last updated the confdb or if some snippet was
+     * added or removed.
+     */
+
+    ret = sss_ini_get_config(init_data, config_file, config_dir);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to load configuration\n");
         goto done;
@@ -358,6 +346,7 @@ done:
 errno_t confdb_setup(TALLOC_CTX *mem_ctx,
                      const char *cdb_file,
                      const char *config_file,
+                     const char *config_dir,
                      struct confdb_ctx **_cdb)
 {
     TALLOC_CTX *tmp_ctx;
@@ -413,7 +402,7 @@ errno_t confdb_setup(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    ret = confdb_init_db(config_file, cdb);
+    ret = confdb_init_db(config_file, config_dir, cdb);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "ConfDB initialization has failed "
               "[%d]: %s\n", ret, sss_strerror(ret));
diff --git a/src/confdb/confdb_setup.h b/src/confdb/confdb_setup.h
index 197673d..9f647ec 100644
--- a/src/confdb/confdb_setup.h
+++ b/src/confdb/confdb_setup.h
@@ -48,6 +48,7 @@
 errno_t confdb_setup(TALLOC_CTX *mem_ctx,
                      const char *cdb_file,
                      const char *config_file,
+                     const char *config_dir,
                      struct confdb_ctx **_cdb);
 
 #endif /* CONFDB_SETUP_H_ */
diff --git a/src/external/libini_config.m4 b/src/external/libini_config.m4
index 9e5c69f..a2bba42 100644
--- a/src/external/libini_config.m4
+++ b/src/external/libini_config.m4
@@ -19,6 +19,18 @@ PKG_CHECK_MODULES(INI_CONFIG_V0, [
                         INI_CONFIG_LIBS="$INI_CONFIG_V1_1_LIBS"
                         HAVE_LIBINI_CONFIG_V1_1=1
                         AC_DEFINE_UNQUOTED(HAVE_LIBINI_CONFIG_V1_1, 1, [libini_config version 1.1.0 or greater])
+                        PKG_CHECK_MODULES(INI_CONFIG_V1_3, [
+                            ini_config >= 1.3.0], [
+
+                                INI_CONFIG_CFLAGS="$INI_CONFIG_V1_3_CFLAGS"
+                                INI_CONFIG_LIBS="$INI_CONFIG_V1_3_LIBS"
+                                HAVE_LIBINI_CONFIG_V1_3=1
+                                AC_DEFINE_UNQUOTED(HAVE_LIBINI_CONFIG_V1_3, 1,
+                                                   [libini_config version 1.3.0 or greater])
+                            ], [
+                                AC_MSG_WARN([libini_config-devel >= 1.3.0 not available, using older version])
+                            ]
+                        )
                     ], [
                         AC_MSG_WARN([libini_config-devel >= 1.1.0 not available, using older version])
                     ]
diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
index 3f7ae7a..c71cdc2 100644
--- a/src/monitor/monitor.c
+++ b/src/monitor/monitor.c
@@ -1872,6 +1872,7 @@ static int monitor_ctx_destructor(void *mem)
  */
 errno_t load_configuration(TALLOC_CTX *mem_ctx,
                            const char *config_file,
+                           const char *config_dir,
                            struct mt_ctx **monitor)
 {
     errno_t ret;
@@ -1892,7 +1893,7 @@ errno_t load_configuration(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    ret = confdb_setup(ctx, cdb_file, config_file, &ctx->cdb);
+    ret = confdb_setup(ctx, cdb_file, config_file, config_dir, &ctx->cdb);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Unable to setup ConfDB [%d]: %s\n",
              ret, sss_strerror(ret));
@@ -3147,7 +3148,8 @@ int main(int argc, const char *argv[])
     }
 
     /* Parse config file, fail if cannot be done */
-    ret = load_configuration(tmp_ctx, config_file, &monitor);
+    ret = load_configuration(tmp_ctx, config_file, CONFDB_DEFAULT_CONFIG_DIR,
+                             &monitor);
     if (ret != EOK) {
         switch (ret) {
         case ERR_MISSING_CONF:
diff --git a/src/tools/common/sss_tools.c b/src/tools/common/sss_tools.c
index 0323c3c..ca90fe7 100644
--- a/src/tools/common/sss_tools.c
+++ b/src/tools/common/sss_tools.c
@@ -103,7 +103,9 @@ static errno_t sss_tool_confdb_init(TALLOC_CTX *mem_ctx,
         return ENOMEM;
     }
 
-    ret = confdb_setup(mem_ctx, path, SSSD_CONFIG_FILE, &confdb);
+    ret = confdb_setup(mem_ctx, path,
+                       SSSD_CONFIG_FILE, CONFDB_DEFAULT_CONFIG_DIR,
+                       &confdb);
     talloc_zfree(path);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Unable to setup ConfDB [%d]: %s\n",
diff --git a/src/util/sss_ini.c b/src/util/sss_ini.c
index 766a75e..667447e 100644
--- a/src/util/sss_ini.c
+++ b/src/util/sss_ini.c
@@ -46,6 +46,8 @@
 
 struct sss_ini_initdata {
     char **error_list;
+    struct ref_array *ra_success_list;
+    struct ref_array *ra_error_list;
     struct ini_cfgobj *sssd_config;
     struct value_obj *obj;
     const struct stat *cstat;
@@ -205,10 +207,19 @@ void sss_ini_config_print_errors(char **error_list)
 /* Load configuration */
 
 int sss_ini_get_config(struct sss_ini_initdata *init_data,
-                       const char *config_file)
+                       const char *config_file,
+                       const char *config_dir)
 {
     int ret;
 #ifdef HAVE_LIBINI_CONFIG_V1
+#ifdef HAVE_LIBINI_CONFIG_V1_3
+    const char *patterns[] = { "^[^\\.].*\\.conf", NULL };
+    const char *sections[] = { ".*", NULL };
+    uint32_t i = 0;
+    char *msg = NULL;
+    struct access_check snip_check;
+    struct ini_cfgobj *modified_sssd_config = NULL;
+#endif /* HAVE_LIBINI_CONFIG_V1_3 */
 
     /* Create config object */
     ret = ini_config_create(&(init_data->sssd_config));
@@ -244,6 +255,55 @@ int sss_ini_get_config(struct sss_ini_initdata *init_data,
         return ret;
     }
 
+#ifdef HAVE_LIBINI_CONFIG_V1_3
+    snip_check.flags = INI_ACCESS_CHECK_MODE | INI_ACCESS_CHECK_UID
+                       | INI_ACCESS_CHECK_GID;
+    snip_check.uid = 0; /* owned by root */
+    snip_check.gid = 0; /* owned by root */
+    snip_check.mode = S_IRUSR; /* r**------ */
+    snip_check.mask = ALLPERMS & ~(S_IWUSR | S_IXUSR);
+
+    ret = ini_config_augment(init_data->sssd_config,
+                             config_dir,
+                             patterns,
+                             sections,
+                             &snip_check,
+                             INI_STOP_ON_ANY,
+                             INI_MV1S_OVERWRITE,
+                             INI_PARSE_NOWRAP,
+                             INI_MV2S_OVERWRITE,
+                             &modified_sssd_config,
+                             &init_data->ra_error_list,
+                             &init_data->ra_success_list);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to augment configuration [%d]: %s",
+              ret, sss_strerror(ret));
+    }
+
+    while (ref_array_get(init_data->ra_success_list, i, &msg) != NULL) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Config merge success: %s\n", msg);
+        i++;
+    }
+
+    i = 0;
+    while (ref_array_get(init_data->ra_error_list, i, &msg) != NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Config merge error: %s\n", msg);
+        i++;
+    }
+
+    /* switch config objects if there are no errors */
+    if (modified_sssd_config != NULL) {
+        ini_config_destroy(init_data->sssd_config);
+        init_data->sssd_config = modified_sssd_config;
+    } else {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Using only main configuration file due to errors in merging\n");
+    }
+#endif
+
     return ret;
 
 #else
diff --git a/src/util/sss_ini.h b/src/util/sss_ini.h
index 3beaca1..f5b36de 100644
--- a/src/util/sss_ini.h
+++ b/src/util/sss_ini.h
@@ -58,7 +58,8 @@ int sss_ini_get_mtime(struct sss_ini_initdata *init_data,
 
 /* Load configuration */
 int sss_ini_get_config(struct sss_ini_initdata *init_data,
-                       const char *config_file);
+                       const char *config_file,
+                       const char *config_dir);
 /* Get configuration object */
 int sss_ini_get_cfgobj(struct sss_ini_initdata *init_data,
                        const char *section, const char *name);
-- 
2.5.0

