>From 11222a22c512d3da80294f32f394f1653964a10b Mon Sep 17 00:00:00 2001
From: Pavel Reichl <preichl@redhat.com>
Date: Wed, 22 Jan 2014 16:47:22 +0000
Subject: [PATCH 2/2] AD: support for subdomain_homedir

If users from AD don't have set homedir then subdomain_homedir is used
as default.

Resolves:
https://fedorahosted.org/sssd/ticket/2169
---
 src/db/sysdb.h                        |   6 ++
 src/db/sysdb_ops.c                    |  68 ++++++++++++++++++
 src/providers/ipa/ipa_subdomains_id.c | 131 ++++++++++++++++++++++++++++++++++
 3 files changed, 205 insertions(+)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 1f779875db13f887ef4b211450f1b4e170628907..d6969c938a6d8ce991190f26b8c954e95edaff3b 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -596,6 +596,12 @@ int sysdb_add_user(struct sss_domain_info *domain,
                    int cache_timeout,
                    time_t now);
 
+/* Replace user homedir */
+errno_t sysdb_store_homedir_of_user(TALLOC_CTX *mem_ctx,
+                                    struct sss_domain_info *domain,
+                                    const char *fqname,
+                                    const char *homedir);
+
 /* Add group (only basic attrs and w/o checks) */
 int sysdb_add_basic_group(struct sss_domain_info *domain,
                           const char *name, gid_t gid);
diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
index cb331e1e245335c608d984d8f491f4378549185e..0d4e32c931dd29a84a7c1441e0e883c1250b33f7 100644
--- a/src/db/sysdb_ops.c
+++ b/src/db/sysdb_ops.c
@@ -988,6 +988,74 @@ done:
     return ret;
 }
 
+/* =Replace-Homedir-Attribute-Of-User======================================== */
+
+errno_t
+sysdb_store_homedir_of_user(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain,
+                            const char *fqname, const char *homedir)
+{
+    errno_t ret;
+    errno_t sret;
+    TALLOC_CTX *tmp_ctx;
+    bool in_transaction = false;
+    struct sysdb_attrs *attrs;
+    struct sysdb_ctx *sysdb = domain->sysdb;
+
+    tmp_ctx = talloc_new(mem_ctx);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    attrs = sysdb_new_attrs(tmp_ctx);
+    if (attrs == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_attrs_add_string(attrs, SYSDB_HOMEDIR, homedir);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("Error setting homedir: [%s]\n",
+                                     strerror(ret)));
+        goto done;
+    }
+
+    ret = sysdb_transaction_start(sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to start transaction\n"));
+        goto done;
+    }
+
+    in_transaction = true;
+
+    ret = sysdb_set_user_attr(domain, fqname, attrs, SYSDB_MOD_REP);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Failed to update homedir information!\n"));
+        goto done;
+    }
+
+    ret = sysdb_transaction_commit(sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Cannot commit sysdb transaction [%d]: %s.\n",
+               ret, strerror(ret)));
+        goto done;
+    }
+
+    in_transaction = false;
+
+done:
+    if (in_transaction) {
+        sret = sysdb_transaction_cancel(sysdb);
+        if (sret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Could not cancel transaction.\n"));
+        }
+    }
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 static errno_t
 sysdb_remove_ghost_from_group(struct sss_domain_info *dom,
                               struct ldb_message *group,
diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
index c29a2a3047af105966b636422105abd15e8a3992..56df1c0dcc6abe022a38f60ac9b9b207803138ec 100644
--- a/src/providers/ipa/ipa_subdomains_id.c
+++ b/src/providers/ipa/ipa_subdomains_id.c
@@ -25,6 +25,7 @@
 #include <errno.h>
 
 #include "util/util.h"
+#include "util/sss_nss.h"
 #include "util/strtonum.h"
 #include "db/sysdb.h"
 #include "providers/ldap/ldap_common.h"
@@ -350,6 +351,126 @@ ipa_get_ad_id_ctx(struct ipa_id_ctx *ipa_ctx,
     return (iter) ? iter->ad_id_ctx : NULL;
 }
 
+static errno_t
+get_subdomain_homedir_of_user(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+                              const char *fqname, uint32_t uid,
+                              const char **_homedir)
+{
+    errno_t ret;
+    char *name;
+    /* TODO: domain can be removed when sss_parse_name supports NULL params */
+    char *domain;
+    const char *homedir;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(mem_ctx);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sss_parse_name(tmp_ctx, dom->names, fqname, &domain, &name);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    homedir = expand_homedir_template(tmp_ctx, dom->subdomain_homedir, name,
+                                      uid, NULL, dom->name, dom->flat_name);
+
+    if (homedir == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("expand_homedir_template failed\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    if (_homedir == NULL) {
+        ret = EINVAL;
+        goto done;
+    }
+    *_homedir = talloc_steal(mem_ctx, homedir);
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+
+
+static errno_t
+apply_subdomain_homedir(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+                        int filter_type, const char *filter_value)
+{
+    errno_t ret;
+    uint32_t uid;
+    const char *fqname;
+    const char *homedir;
+    struct ldb_result *res;
+
+    if (filter_type == BE_FILTER_NAME) {
+        ret = sysdb_getpwnam(mem_ctx, dom, filter_value, &res);
+    } else if (filter_type == BE_FILTER_IDNUM) {
+        errno = 0;
+        uid = strtouint32(filter_value, NULL, 10);
+        if (errno != 0) {
+            ret = errno;
+            goto done;
+        }
+        ret = sysdb_getpwuid(mem_ctx, dom, uid, &res);
+    } else {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Unsoported filter type: [%d].\n", filter_type));
+        ret = EINVAL;
+        goto done;
+    }
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Failed to make request to our cache: [%d]: [%s]\n",
+               ret, sss_strerror(ret)));
+        goto done;
+    }
+
+    if (res->count == 0) {
+        ret = ENOENT;
+        goto done;
+    }
+
+    homedir = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_HOMEDIR, NULL);
+
+    /*
+     * If user has explicitly set homedir it is NOT overriden by
+     * subdoman_homedir.
+     */
+    if (homedir == NULL) {
+        fqname = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL);
+        uid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_UIDNUM, 0);
+        if (uid == 0) {
+            DEBUG(SSSDBG_OP_FAILURE, ("UID for user [%s] is not known.\n",
+                                      filter_value));
+            ret = ENOENT;
+            goto done;
+        }
+
+        ret = get_subdomain_homedir_of_user(mem_ctx, dom, fqname, uid,
+                                            &homedir);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  ("get_subdomain_homedir_of_user failed: [%d]: [%s]\n",
+                   ret, sss_strerror(ret)));
+            goto done;
+        }
+
+        ret = sysdb_store_homedir_of_user(mem_ctx, dom, fqname, homedir);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  ("sysdb_store_homedir_of_user failed: [%d]: [%s]\n",
+                   ret, sss_strerror(ret)));
+            goto done;
+        }
+    }
+done:
+    return ret;
+}
+
 static void
 ipa_get_ad_acct_ad_part_done(struct tevent_req *subreq)
 {
@@ -367,6 +488,16 @@ ipa_get_ad_acct_ad_part_done(struct tevent_req *subreq)
         return;
     }
 
+    ret = apply_subdomain_homedir(state, state->user_dom,
+                                  state->ar->filter_type,
+                                  state->ar->filter_value);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("apply_subdomain_homedir failed: [%d]: [%s].\n",
+               ret, sss_strerror(ret)));
+        return;
+    }
+
     if ((state->ar->entry_type & BE_REQ_TYPE_MASK) != BE_REQ_INITGROUPS) {
         tevent_req_done(req);
         return;
-- 
1.8.4.2

