From b360e56e04fc7638d4aa6738d1f8c3c1a6144767 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Tue, 17 May 2011 16:37:32 +0200
Subject: [PATCH 2/3] Add a new option to override home directory value

https://fedorahosted.org/sssd/ticket/551
---
 src/confdb/confdb.c            |    9 +++-
 src/confdb/confdb.h            |    2 +
 src/config/SSSDConfig.py       |    1 +
 src/config/SSSDConfigTest.py   |    2 +
 src/config/etc/sssd.api.conf   |    2 +
 src/man/sssd.conf.5.xml        |   37 +++++++++++
 src/responder/nss/nsssrv.c     |    5 ++
 src/responder/nss/nsssrv.h     |    2 +
 src/responder/nss/nsssrv_cmd.c |  134 +++++++++++++++++++++++++++++++++++++++-
 9 files changed, 192 insertions(+), 2 deletions(-)

diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
index fdf409f..87d33f9 100644
--- a/src/confdb/confdb.c
+++ b/src/confdb/confdb.c
@@ -849,9 +849,16 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
         goto done;
     }
 
+    tmp = ldb_msg_find_attr_as_string(res->msgs[0],
+                                      CONFDB_NSS_OVERRIDE_HOMEDIR, NULL);
+    domain->override_homedir = talloc_strdup(domain, tmp);
+    if (!domain->name) {
+        ret = ENOMEM;
+        goto done;
+    }
+
     *_domain = domain;
     ret = EOK;
-
 done:
     talloc_free(tmp_ctx);
     return ret;
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 4e8a6dd..20e82b4 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -72,6 +72,7 @@
 #define CONFDB_NSS_FILTER_USERS "filter_users"
 #define CONFDB_NSS_FILTER_GROUPS "filter_groups"
 #define CONFDB_NSS_PWFIELD  "pwfield"
+#define CONFDB_NSS_OVERRIDE_HOMEDIR "override_homedir"
 
 /* PAM */
 #define CONFDB_PAM_CONF_ENTRY "config/pam"
@@ -145,6 +146,7 @@ struct sss_domain_info {
     bool legacy_passwords;
 
     gid_t override_gid;
+    const char *override_homedir;
 
     uint32_t entry_cache_timeout;
 
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 25484db..8e962f3 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -58,6 +58,7 @@ option_strings = {
     'filter_groups' : _('Groups that SSSD should explicitly ignore'),
     'filter_users_in_groups' : _('Should filtered users appear in groups'),
     'pwfield' : _('The value of the password field the NSS provider should return'),
+    'override_homedir' : _('Override homedir value from the identity provider with this value'),
 
     # [pam]
     'offline_credentials_expiration' : _('How long to allow cached logins between online logins (days)'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 7bd45b4..5b01b99 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -481,6 +481,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'dns_resolver_timeout',
             'dns_discovery_domain',
             'override_gid',
+            'override_homedir',
             'id_provider',
             'auth_provider',
             'access_provider',
@@ -786,6 +787,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'dns_resolver_timeout',
             'dns_discovery_domain',
             'override_gid',
+            'override_homedir',
             'id_provider',
             'auth_provider',
             'access_provider',
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index dfb5bad..6f15495 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -27,6 +27,7 @@ filter_users = list, str, false
 filter_groups = list, str, false
 filter_users_in_groups = bool, None, false
 pwfield = str, None, false
+override_homedir = str, None, false
 
 [pam]
 # Authentication service
@@ -65,6 +66,7 @@ filter_groups = list, str, false
 dns_resolver_timeout = int, None, false
 dns_discovery_domain = str, None, false
 override_gid = int, None, false
+override_homedir = str, None, false
 
 # Special providers
 [provider/permit]
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 386dd03..57454dd 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -354,6 +354,43 @@
                         </para>
                     </listitem>
                 </varlistentry>
+                <varlistentry>
+                    <term>override_homedir (string)</term>
+                    <listitem>
+                        <para>
+                            Override the user's home directory. You
+                            can either provide an absolute value or a
+                            template. In the template, the following
+                            sequences are substituted:
+                            <variablelist>
+                                <varlistentry>
+                                    <term>%u</term>
+                                    <listitem><para>login name</para></listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%U</term>
+                                    <listitem><para>UID number</para></listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%d</term>
+                                    <listitem><para>domain name</para></listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%f</term>
+                                    <listitem><para>fully qualified user name (user@domain)</para></listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%%</term>
+                                    <listitem><para>a literal '%'</para>
+                                    </listitem>
+                                </varlistentry>
+                            </variablelist>
+                        </para>
+                        <para>
+                            This option can also be set per-domain.
+                        </para>
+                    </listitem>
+                </varlistentry>
             </variablelist>
         </refsect2>
         <refsect2 id='PAM'>
diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
index 6c20ca3..a5323bd 100644
--- a/src/responder/nss/nsssrv.c
+++ b/src/responder/nss/nsssrv.c
@@ -110,6 +110,11 @@ static int nss_get_config(struct nss_ctx *nctx,
                             &nctx->pwfield);
     if (ret != EOK) goto done;
 
+    ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
+                            CONFDB_NSS_OVERRIDE_HOMEDIR, NULL,
+                            &nctx->override_homedir);
+    if (ret != EOK) goto done;
+
     ret = 0;
 done:
     talloc_free(tmpctx);
diff --git a/src/responder/nss/nsssrv.h b/src/responder/nss/nsssrv.h
index 062d937..e3e774f 100644
--- a/src/responder/nss/nsssrv.h
+++ b/src/responder/nss/nsssrv.h
@@ -57,6 +57,8 @@ struct nss_ctx {
     bool filter_users_in_groups;
 
     char *pwfield;
+
+    char *override_homedir;
 };
 
 struct nss_packet;
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 2f510b9..4386da7 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -169,6 +169,114 @@ struct setent_ctx {
 /****************************************************************************
  * PASSWD db related functions
  ***************************************************************************/
+char *expand_homedir_template(TALLOC_CTX *mem_ctx, const char *template,
+                              const char *username, uint32_t uid,
+                              const char *domain)
+{
+    char *copy;
+    char *p;
+    char *n;
+    char *result = NULL;
+    char *res = NULL;
+    TALLOC_CTX *tmp_ctx = NULL;
+
+    if (template == NULL) {
+        DEBUG(1, ("Missing template.\n"));
+        return NULL;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) return NULL;
+
+    copy = talloc_strdup(tmp_ctx, template);
+    if (copy == NULL) {
+        DEBUG(1, ("talloc_strdup failed.\n"));
+        goto done;
+    }
+
+    result = talloc_strdup(tmp_ctx, "");
+    if (result == NULL) {
+        DEBUG(1, ("talloc_strdup failed.\n"));
+        goto done;
+    }
+
+    p = copy;
+    while ( (n = strchr(p, '%')) != NULL) {
+        *n = '\0';
+        n++;
+        if ( *n == '\0' ) {
+            DEBUG(1, ("format error, single %% at the end of the template.\n"));
+            goto done;
+        }
+        switch( *n ) {
+            case 'u':
+                if (username == NULL) {
+                    DEBUG(1, ("Cannot expand user name template "
+                              "because user name is empty.\n"));
+                    goto done;
+                }
+                result = talloc_asprintf_append(result, "%s%s", p,
+                                                username);
+                break;
+
+            case 'U':
+                if (uid == 0) {
+                    DEBUG(1, ("Cannot expand uid template "
+                              "because uid is invalid.\n"));
+                    goto done;
+                }
+                result = talloc_asprintf_append(result, "%s%d", p,
+                                                uid);
+                break;
+
+            case 'd':
+                if (domain == NULL) {
+                    DEBUG(1, ("Cannot expand domain name template "
+                              "because domain name is empty.\n"));
+                    goto done;
+                }
+                result = talloc_asprintf_append(result, "%s%s", p,
+                                                domain);
+                break;
+
+            case 'f':
+                if (domain == NULL || username == NULL) {
+                    DEBUG(1, ("Cannot expand fully qualified name template "
+                              "because domain or user name is empty.\n"));
+                    goto done;
+                }
+                result = talloc_asprintf_append(result, "%s%s@%s", p,
+                                                username, domain);
+                break;
+
+            case '%':
+                result = talloc_asprintf_append(result, "%s%%", p);
+                break;
+
+            default:
+                DEBUG(1, ("format error, unknown template [%%%c].\n", *n));
+                goto done;
+        }
+
+        if (result == NULL) {
+            DEBUG(1, ("talloc_asprintf_append failed.\n"));
+            goto done;
+        }
+
+        p = n + 1;
+    }
+
+    result = talloc_asprintf_append(result, "%s", p);
+    if (result == NULL) {
+        DEBUG(1, ("talloc_asprintf_append failed.\n"));
+        goto done;
+    }
+
+    res = talloc_move(mem_ctx, &result);
+done:
+    talloc_zfree(tmp_ctx);
+    return res;
+}
 
 static gid_t get_gid_override(struct ldb_message *msg,
                               struct sss_domain_info *dom)
@@ -178,6 +286,25 @@ static gid_t get_gid_override(struct ldb_message *msg,
         ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
 }
 
+static const char *get_homedir_override(TALLOC_CTX *mem_ctx,
+                                        struct ldb_message *msg,
+                                        struct nss_ctx *nctx,
+                                        struct sss_domain_info *dom,
+                                        const char *name,
+                                        uint32_t uid)
+{
+    if (dom->override_homedir) {
+        return expand_homedir_template(mem_ctx, dom->override_homedir,
+                                       name, uid, dom->name);
+    } else if (nctx->override_homedir) {
+        return expand_homedir_template(mem_ctx, nctx->override_homedir,
+                                       name, uid, dom->name);
+    }
+
+    return talloc_strdup(mem_ctx,
+            ldb_msg_find_attr_as_string(msg, SYSDB_HOMEDIR, NULL));
+}
+
 static int fill_pwent(struct sss_packet *packet,
                       struct sss_domain_info *dom,
                       struct nss_ctx *nctx,
@@ -203,6 +330,7 @@ static int fill_pwent(struct sss_packet *packet,
     const char *namefmt = nctx->rctx->names->fq_fmt;
     bool packet_initialized = false;
     int ncret;
+    TALLOC_CTX *tmp_ctx = NULL;
 
     if (add_domain) dom_len = strlen(domain);
 
@@ -210,6 +338,9 @@ static int fill_pwent(struct sss_packet *packet,
 
     num = 0;
     for (i = 0; i < *count; i++) {
+        talloc_zfree(tmp_ctx);
+        tmp_ctx = talloc_new(NULL);
+
         msg = msgs[i];
 
         name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
@@ -241,7 +372,7 @@ static int fill_pwent(struct sss_packet *packet,
         }
 
         gecos = ldb_msg_find_attr_as_string(msg, SYSDB_GECOS, NULL);
-        homedir = ldb_msg_find_attr_as_string(msg, SYSDB_HOMEDIR, NULL);
+        homedir = get_homedir_override(tmp_ctx, msg, nctx, dom, name, uid);
         shell = ldb_msg_find_attr_as_string(msg, SYSDB_SHELL, NULL);
 
         if (!gecos) gecos = "";
@@ -306,6 +437,7 @@ static int fill_pwent(struct sss_packet *packet,
 
         num++;
     }
+    talloc_zfree(tmp_ctx);
 
 done:
     *count = i;
-- 
1.7.4.4

