>From cecb67cc44f080c6f4b83d6e69d434fcb3c87fb0 Mon Sep 17 00:00:00 2001
From: Michal Zidek <mzidek@redhat.com>
Date: Wed, 27 Mar 2013 16:50:40 +0100
Subject: [PATCH] sss_cache: support for subdomains

https://fedorahosted.org/sssd/ticket/1741
---
 src/tools/sss_cache.c | 231 ++++++++++++++++++++++++++++++++++----------------
 1 file changed, 158 insertions(+), 73 deletions(-)

diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c
index ec09662..3bfc76a 100644
--- a/src/tools/sss_cache.c
+++ b/src/tools/sss_cache.c
@@ -86,7 +86,7 @@ static bool invalidate_entries(TALLOC_CTX *ctx,
                                enum sss_cache_entry entry_type,
                                const char *filter, const char *name);
 static errno_t update_all_filters(struct cache_tool_ctx *tctx,
-                                  char *domain_name);
+                                  struct sss_domain_info *dinfo);
 
 int main(int argc, const char *argv[])
 {
@@ -94,7 +94,9 @@ int main(int argc, const char *argv[])
     struct cache_tool_ctx *tctx = NULL;
     struct sysdb_ctx *sysdb;
     bool skipped = true;
+    bool main_domain; /* Indicates that processed domain is not a subdomain */
     struct sss_domain_info *dinfo;
+    struct sss_domain_info *dinfo_list;
 
     ret = init_context(argc, argv, &tctx);
     if (ret != EOK) {
@@ -103,41 +105,67 @@ int main(int argc, const char *argv[])
         goto done;
     }
 
-    for (dinfo = tctx->domains; dinfo; dinfo = get_next_domain(dinfo, false)) {
-        sysdb = dinfo->sysdb;
-
-        /* Update filters for each domain */
-        ret = update_all_filters(tctx, dinfo->name);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to update filters.\n"));
-            goto done;
-        }
+    for (dinfo_list = tctx->domains; dinfo_list;
+         dinfo_list = get_next_domain(dinfo_list, false)) {
+        sysdb = dinfo_list->sysdb;
+        sysdb_update_subdomains(dinfo_list);
+
+        main_domain = true;
+        dinfo = dinfo_list;
+        do {
+            sysdb = dinfo->sysdb;
+            /* Update filters for each domain */
+            ret = update_all_filters(tctx, dinfo);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to update filters.\n"));
+                goto done;
+            }
 
-        ret = sysdb_transaction_start(sysdb);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE, ("Could not start the transaction!\n"));
-            goto done;
-        }
+            ret = sysdb_transaction_start(sysdb);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                      ("Could not start the transaction!\n"));
+                goto done;
+            }
 
-        skipped &= !invalidate_entries(tctx, dinfo, sysdb, TYPE_USER,
-                                   tctx->user_filter, tctx->user_name);
-        skipped &= !invalidate_entries(tctx, dinfo, sysdb, TYPE_GROUP,
-                                   tctx->group_filter, tctx->group_name);
-        skipped &= !invalidate_entries(tctx, dinfo, sysdb, TYPE_NETGROUP,
-                                   tctx->netgroup_filter, tctx->netgroup_name);
-        skipped &= !invalidate_entries(tctx, dinfo, sysdb, TYPE_SERVICE,
-                                   tctx->service_filter, tctx->service_name);
-        skipped &= !invalidate_entries(tctx, dinfo, sysdb, TYPE_AUTOFSMAP,
-                                   tctx->autofs_filter, tctx->autofs_name);
-
-        ret = sysdb_transaction_commit(sysdb);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE, ("Could not commit the transaction!\n"));
-            ret = sysdb_transaction_cancel(sysdb);
+            skipped &= !invalidate_entries(tctx, dinfo, sysdb, TYPE_USER,
+                                           tctx->user_filter,
+                                           tctx->user_name);
+            skipped &= !invalidate_entries(tctx, dinfo, sysdb, TYPE_GROUP,
+                                           tctx->group_filter,
+                                           tctx->group_name);
+            skipped &= !invalidate_entries(tctx, dinfo, sysdb, TYPE_NETGROUP,
+                                           tctx->netgroup_filter,
+                                           tctx->netgroup_name);
+            skipped &= !invalidate_entries(tctx, dinfo, sysdb, TYPE_SERVICE,
+                                           tctx->service_filter,
+                                           tctx->service_name);
+            skipped &= !invalidate_entries(tctx, dinfo, sysdb, TYPE_AUTOFSMAP,
+                                           tctx->autofs_filter,
+                                           tctx->autofs_name);
+
+            ret = sysdb_transaction_commit(sysdb);
             if (ret != EOK) {
-                DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to cancel transaction\n"));
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                      ("Could not commit the transaction!\n"));
+                ret = sysdb_transaction_cancel(sysdb);
+                if (ret != EOK) {
+                    DEBUG(SSSDBG_CRIT_FAILURE,
+                          ("Failed to cancel transaction\n"));
+                }
             }
-        }
+
+            if (main_domain) {
+                if (!dinfo->subdomains) {
+                    /* This domain has no subdomains */
+                    break;
+                }
+                main_domain = false;
+                dinfo = dinfo->subdomains;
+            } else {
+                dinfo = dinfo->next;
+            }
+        } while (dinfo);
     }
 
     if (skipped == true) {
@@ -158,59 +186,110 @@ done:
     return ret;
 }
 
-static errno_t update_filter(struct cache_tool_ctx *tctx, char *domain_name,
+static errno_t update_filter(struct cache_tool_ctx *tctx,
+                             struct sss_domain_info *dinfo,
                              char *name, bool update, const char *fmt,
-                             char **filter)
+                             char **_filter)
 {
     errno_t ret;
     char *parsed_domain = NULL;
     char *parsed_name = NULL;
+    char *name_lower = NULL;
+    char *domain_lower = NULL;
+    TALLOC_CTX *tmp_ctx = NULL;
+    char *use_name = NULL;
+    char *filter;
+
+    if (!name || !update) {
+        /* Nothing to do */
+        return EOK;
+    }
 
-    if (name && update) {
-        ret = sss_parse_name(tctx, tctx->nctx, name,
-                             &parsed_domain, &parsed_name);
-       if (ret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE, ("sss_parse_name failed\n"));
-            goto done;
-        }
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory.\n"));
+        return ENOMEM;
+    }
 
-        if (parsed_domain) {
-            talloc_free(*filter);
-            if (!strcmp(domain_name, parsed_domain)) {
-                if (fmt) {
-                    *filter = talloc_asprintf(tctx, fmt,
-                                              SYSDB_NAME, parsed_name);
-                } else {
-                    *filter = talloc_strdup(tctx, parsed_name);
-                }
-                if (*filter == NULL) {
-                    DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
-                    ret = ENOMEM;
-                    goto done;
-                }
-            } else {
-               /* Set to NULL to indicate that it will not be used
-                * in this domain */
-                *filter = NULL;
+    ret = sss_parse_name(tmp_ctx, tctx->nctx, name,
+                         &parsed_domain, &parsed_name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("sss_parse_name failed\n"));
+        goto done;
+    }
+
+    if (parsed_domain) {
+        /* If this is a subdomain, we must use lowercased fqdn in format
+         * name@domain in filters (the attribute name in sysdb is in this
+         * format for users in subdomains). For native domains, we just
+         * use the username without the domain. */
+        if (IS_SUBDOMAIN(dinfo)) {
+            name_lower = sss_tc_utf8_str_tolower(tmp_ctx, parsed_name);
+            domain_lower = sss_tc_utf8_str_tolower(tmp_ctx, parsed_domain);
+            if (!name_lower || !domain_lower) {
+                DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
+                ret = ENOMEM;
+                goto done;
+            }
+
+            use_name = talloc_asprintf(tmp_ctx, "%s@%s", name_lower,
+                                       domain_lower);
+            if (use_name == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
+                ret = ENOMEM;
+                goto done;
             }
         } else {
+            use_name = parsed_name;
+        }
+
+        talloc_free(*_filter);
+        if (!strcmp(dinfo->name, parsed_domain)) {
             if (fmt) {
-                *filter = talloc_asprintf(tctx, fmt, SYSDB_NAME, name);
+                filter = talloc_asprintf(tmp_ctx, fmt,
+                                          SYSDB_NAME, use_name);
             } else {
-                *filter = talloc_strdup(tctx, name);
+                filter = talloc_strdup(tmp_ctx, use_name);
             }
-            if (*filter == NULL) {
+            if (filter == NULL) {
                 DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
                 ret = ENOMEM;
                 goto done;
             }
+        } else {
+            /* We were able to parse the domain from given fqdn, but it
+             * does not match with currently processed domain. */
+            filter = NULL;
+        }
+    } else {
+        if (IS_SUBDOMAIN(dinfo)) {
+            use_name = sss_tc_utf8_str_tolower(tmp_ctx, name);
+            if (use_name == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
+                ret = ENOMEM;
+                goto done;
+            }
+        } else {
+            use_name = name;
+        }
+
+        if (fmt) {
+            filter = talloc_asprintf(tmp_ctx, fmt, SYSDB_NAME, name);
+        } else {
+            filter = talloc_strdup(tmp_ctx, name);
+        }
+        if (filter == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
+            ret = ENOMEM;
+            goto done;
         }
     }
 
+    talloc_free(*_filter);
+    *_filter = talloc_steal(tctx, filter);
     ret = EOK;
 done:
-    talloc_free(parsed_domain);
-    talloc_free(parsed_name);
+    talloc_free(tmp_ctx);
     return ret;
 
 }
@@ -218,18 +297,23 @@ done:
 /* This function updates all filters for specified domain using this
  * domains regex to parse string into domain and name (if exists). */
 static errno_t update_all_filters(struct cache_tool_ctx *tctx,
-                                 char *domain_name)
+                                  struct sss_domain_info *dinfo)
 {
     errno_t ret;
 
-    ret = sss_names_init(tctx, tctx->confdb, domain_name, &tctx->nctx);
+    if (IS_SUBDOMAIN(dinfo)) {
+        ret = sss_names_init(tctx, tctx->confdb, dinfo->parent->name,
+                             &tctx->nctx);
+    } else {
+        ret = sss_names_init(tctx, tctx->confdb, dinfo->name, &tctx->nctx);
+    }
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("sss_names_init() failed\n"));
         return ret;
     }
 
     /* Update user filter */
-    ret = update_filter(tctx, domain_name, tctx->user_name,
+    ret = update_filter(tctx, dinfo, tctx->user_name,
                         tctx->update_user_filter, "(%s=%s)",
                         &tctx->user_filter);
     if (ret != EOK) {
@@ -237,7 +321,7 @@ static errno_t update_all_filters(struct cache_tool_ctx *tctx,
     }
 
     /* Update group filter */
-    ret = update_filter(tctx, domain_name, tctx->group_name,
+    ret = update_filter(tctx, dinfo, tctx->group_name,
                         tctx->update_group_filter, "(%s=%s)",
                         &tctx->group_filter);
     if (ret != EOK) {
@@ -245,7 +329,7 @@ static errno_t update_all_filters(struct cache_tool_ctx *tctx,
     }
 
     /* Update netgroup filter */
-    ret = update_filter(tctx, domain_name, tctx->netgroup_name,
+    ret = update_filter(tctx, dinfo, tctx->netgroup_name,
                         tctx->update_netgroup_filter, "(%s=%s)",
                         &tctx->netgroup_filter);
     if (ret != EOK) {
@@ -253,7 +337,7 @@ static errno_t update_all_filters(struct cache_tool_ctx *tctx,
     }
 
     /* Update service filter */
-    ret = update_filter(tctx, domain_name, tctx->service_name,
+    ret = update_filter(tctx, dinfo, tctx->service_name,
                         tctx->update_service_filter, "(%s=%s)",
                         &tctx->service_filter);
     if (ret != EOK) {
@@ -261,7 +345,7 @@ static errno_t update_all_filters(struct cache_tool_ctx *tctx,
     }
 
     /* Update autofs filter */
-    ret = update_filter(tctx, domain_name, tctx->autofs_name,
+    ret = update_filter(tctx, dinfo, tctx->autofs_name,
                         tctx->update_autofs_filter,
                         "(&(objectclass="SYSDB_AUTOFS_MAP_OC")(%s=%s))",
                         &tctx->autofs_filter);
@@ -288,7 +372,6 @@ static bool invalidate_entries(TALLOC_CTX *ctx,
     bool iret;
 
     if (!filter) return false;
-
     switch (entry_type) {
     case TYPE_USER:
         type_string = "user";
@@ -601,7 +684,9 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
     ret = init_domains(ctx, domain);
     if (ret != EOK) {
         if (domain) {
-            ERROR("Could not open domain %1$s\n", domain);
+            ERROR("Could not open domain %1$s. If the domain is a subdomain "
+                  "(trusted domain), use fully qualified name instead of "
+                  "--domain/-d parameter.\n", domain);
         } else {
             ERROR("Could not open available domains\n");
         }
-- 
1.8.0

