[SSSD] NSS: Add config option to expand homedir template format

Lukas Slebodnik lslebodn at redhat.com
Mon Jun 2 15:16:05 UTC 2014


On (02/06/14 10:01), Jakub Hrozek wrote:
>On Mon, Jun 02, 2014 at 07:26:49AM +0200, Lukas Slebodnik wrote:
>> On (01/06/14 19:23), Jakub Hrozek wrote:
>> >On Wed, May 28, 2014 at 06:22:05PM +0200, Lukas Slebodnik wrote:
>> >> On (27/05/14 16:32), Jakub Hrozek wrote:
>> >> >On Tue, May 27, 2014 at 01:03:42PM +0200, Pavel Reichl wrote:
>> >> >> On Tue, 2014-05-27 at 11:24 +0200, Lukas Slebodnik wrote:
>> >> >> > O
>> >> >> > >The fact of passing pointer to the same area in memory to 2 separate
>> >> >> > >arguments of sss_parse_name() is what I called potential source of bugs.
>> >> >> > >You said "It seems strange to me" so I hope you know what I mean.
>> >> >> > It is strange, but it isn't wrong.
>> >> >> >    * orig_name refers to old string
>> >> >> >    * homedir_ctx->username will refer to new string.
>> >> >> > I need to use old string in debug message if function fails.
>> >> >> I missed that. 
>> >> >> 
>> >> >> I did some testing and all seems to be working, so ACK to all patches.
>> >> >> 
>> >> >
>> >> >In the third patch, you need to add the file
>> >> >src/man/include/override_homedir.xml into src/man/po/po4a.cfg to make sure
>> >> You ment homedir_substring.xml
>> >
>> >Yes :)
>> >
>> >> 
>> >> >it's processed for translations.
>> >> >
>> >> Added
>> >> 
>> >> >Can you ask some native English speaker to check the contents of the
>> >> >text added?
>> >> >
>> >> >As a side note, it would be nice to treat any refactoring as an
>> >> >opportunity for adding more unit tests. Neither expand_homedir_template
>> >> >nor sss_parse_name_const have any tests.
>> >> The test for expand_homedir_template is in separate commit,
>> >> because patches with refactoring are complicated enough.
>> >> 
>> >> LS
>> >
>> >Thanks for the unit test!
>> >
>> >I don't have any other comments about functionality or code, just please
>> >amend the man pages as Stephen suggested.
done

>> will do after agreement about allocation of homedir_ctx.
>> I do not want to send patchset more than once :-).
>> 
>> >
>> >One more improvement might be that you don't have to allocate the
>> >homedir_ctx most of the time,
>> It is just a *one* allocation and reason is to have a zero initialized
>> structure. If you really want to avoid one call of talloc_zero I can replace it
>> with structure allocated on stack and zeroing structure with memset.
>
>We have a ZERO_STRUCT call precisely for this reason.
>
done

>This patch is targeted for sssd-1-11 and we're close to the 1.11.6
>deadline. If you think you can spin up another version quickly, then
>please do, otherwise let's clean up the unneeded allocation in 1.12.

new version attached

LS
-------------- next part --------------
>From d679c7b7dd9378ba7a49d3dc716d3e9f6341fa22 Mon Sep 17 00:00:00 2001
From: Lukas Slebodnik <lslebodn at redhat.com>
Date: Fri, 28 Feb 2014 23:42:19 +0100
Subject: [PATCH 1/4] UTIL: Add function sss_parse_name_const

Variable with type 'const char *' can be used as output argument in function
sss_parse_name, but there will be warning.

warning: passing 'const char **' to parameter of type 'char **'
         discards qualifiers in nested pointer types
         [-Wincompatible-pointer-types-discards-qualifiers]
---
 src/tests/cmocka/test_fqnames.c | 86 +++++++++++++++++++++++++++++++++++++++++
 src/util/usertools.c            | 24 ++++++++++++
 src/util/util.h                 |  4 ++
 3 files changed, 114 insertions(+)

diff --git a/src/tests/cmocka/test_fqnames.c b/src/tests/cmocka/test_fqnames.c
index 93e99bfc722894fc030a8706b00b4cc68df7c560..c761093703b5c1a6716ef868c8d2f08ba7cb213c 100644
--- a/src/tests/cmocka/test_fqnames.c
+++ b/src/tests/cmocka/test_fqnames.c
@@ -28,6 +28,7 @@
 #define NAME        "name"
 #define DOMNAME     "domname"
 #define FLATNAME    "flatname"
+#define SPECIALNAME "[]{}();:'|\",<.>/?!#$%^&*_+~`"
 #define PROVIDER    "local"
 #define CONNNAME    "conn"
 
@@ -323,11 +324,74 @@ void parse_name_test_teardown(void **state)
     assert_true(leak_check_teardown());
 }
 
+void sss_parse_name_check(struct parse_name_test_ctx *test_ctx,
+                          const char *input_name,
+                          const char exp_ret,
+                          const char *exp_name,
+                          const char *exp_domain)
+{
+    errno_t ret;
+    char *domain = NULL;
+    char *name = NULL;
+    const char *domain_const = NULL;
+    const char *name_const = NULL;
+
+
+    check_leaks_push(test_ctx);
+    ret = sss_parse_name(test_ctx, test_ctx->nctx, input_name,
+                         &domain, &name);
+    assert_int_equal(ret, exp_ret);
+
+    if (exp_name) {
+        assert_non_null(name);
+        assert_string_equal(name, exp_name);
+    }
+
+    if (exp_domain) {
+        assert_non_null(domain);
+        assert_string_equal(domain, exp_domain);
+    }
+
+    talloc_zfree(name);
+    talloc_zfree(domain);
+
+    ret = sss_parse_name_const(test_ctx, test_ctx->nctx, input_name,
+                               &domain_const, &name_const);
+    assert_int_equal(ret, exp_ret);
+
+    if (exp_name) {
+        assert_non_null(name_const);
+        assert_string_equal(name_const, exp_name);
+    }
+
+    if (exp_domain) {
+        assert_non_null(domain_const);
+        assert_string_equal(domain_const, exp_domain);
+    }
+
+    talloc_free(discard_const(name_const));
+    talloc_free(discard_const(domain_const));
+
+    assert_true(check_leaks_pop(test_ctx) == true);
+}
+
 void parse_name_plain(void **state)
 {
     struct parse_name_test_ctx *test_ctx = talloc_get_type(*state,
                                                            struct parse_name_test_ctx);
+    int ret;
+
     parse_name_check(test_ctx, NAME, NULL, EOK, NAME, NULL);
+
+    ret = sss_parse_name(test_ctx, test_ctx->nctx, NAME,
+                         NULL, NULL);
+    assert_int_equal(ret, EOK);
+    ret = sss_parse_name_const(test_ctx, test_ctx->nctx, NAME,
+                               NULL, NULL);
+    assert_int_equal(ret, EOK);
+
+    sss_parse_name_check(test_ctx, NAME, EOK, NAME, NULL);
+    sss_parse_name_check(test_ctx, SPECIALNAME, EOK, SPECIALNAME, NULL);
 }
 
 void parse_name_fqdn(void **state)
@@ -336,6 +400,11 @@ void parse_name_fqdn(void **state)
                                                            struct parse_name_test_ctx);
     parse_name_check(test_ctx, NAME"@"DOMNAME, NULL, EOK, NAME, DOMNAME);
     parse_name_check(test_ctx, NAME"@"DOMNAME2, NULL, EOK, NAME, DOMNAME2);
+
+    sss_parse_name_check(test_ctx, NAME"@"DOMNAME, EOK, NAME, DOMNAME);
+    sss_parse_name_check(test_ctx, NAME"@"DOMNAME2, EOK, NAME, DOMNAME2);
+    sss_parse_name_check(test_ctx, DOMNAME"\\"NAME, EOK, NAME, DOMNAME);
+    sss_parse_name_check(test_ctx, DOMNAME2"\\"NAME, EOK, NAME, DOMNAME2);
 }
 
 void parse_name_sub(void **state)
@@ -397,6 +466,20 @@ void test_init_nouser(void **state)
     assert_int_not_equal(ret, EOK);
 }
 
+void sss_parse_name_fail(void **state)
+{
+    struct parse_name_test_ctx *test_ctx = talloc_get_type(*state,
+                                                           struct parse_name_test_ctx);
+
+    sss_parse_name_check(test_ctx, "", EINVAL, NULL, NULL);
+    sss_parse_name_check(test_ctx, "@", EINVAL, NULL, NULL);
+    sss_parse_name_check(test_ctx, "\\", EINVAL, NULL, NULL);
+    sss_parse_name_check(test_ctx, "\\"NAME, EINVAL, NULL, NULL);
+    sss_parse_name_check(test_ctx, "@"NAME, EINVAL, NULL, NULL);
+    sss_parse_name_check(test_ctx, NAME"@", EINVAL, NULL, NULL);
+    sss_parse_name_check(test_ctx, NAME"\\", EINVAL, NULL, NULL);
+}
+
 int main(int argc, const char *argv[])
 {
     poptContext pc;
@@ -434,6 +517,9 @@ int main(int argc, const char *argv[])
         unit_test_setup_teardown(parse_name_default,
                                  parse_name_test_setup,
                                  parse_name_test_teardown),
+        unit_test_setup_teardown(sss_parse_name_fail,
+                                 parse_name_test_setup,
+                                 parse_name_test_teardown),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
diff --git a/src/util/usertools.c b/src/util/usertools.c
index ea2123061d36bada99bfca71b18b11efd2e343e0..57945c7f15f49213b37b25828c924bef70966a24 100644
--- a/src/util/usertools.c
+++ b/src/util/usertools.c
@@ -366,6 +366,30 @@ int sss_parse_name(TALLOC_CTX *memctx,
     return EOK;
 }
 
+int sss_parse_name_const(TALLOC_CTX *memctx,
+                         struct sss_names_ctx *snctx, const char *orig,
+                         const char **_domain, const char **_name)
+{
+    char *domain;
+    char *name;
+    int ret;
+
+    ret = sss_parse_name(memctx, snctx, orig,
+                         (_domain == NULL) ? NULL : &domain,
+                         (_name == NULL) ? NULL : &name);
+    if (ret == EOK) {
+        if (_domain != NULL) {
+            *_domain = domain;
+        }
+
+        if (_name != NULL) {
+            *_name = name;
+        }
+    }
+
+    return ret;
+}
+
 static struct sss_domain_info * match_any_domain_or_subdomain_name(
                                                 struct sss_domain_info *dom,
                                                 const char *dmatch)
diff --git a/src/util/util.h b/src/util/util.h
index 38d9fca4c30a3be46f5dccc28a8a061fa3190c2b..af2a578180826fa39240ec18c8e11ac8c2d2ae64 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -280,6 +280,10 @@ int sss_parse_name(TALLOC_CTX *memctx,
                    struct sss_names_ctx *snctx,
                    const char *orig, char **_domain, char **_name);
 
+int sss_parse_name_const(TALLOC_CTX *memctx,
+                         struct sss_names_ctx *snctx, const char *orig,
+                         const char **_domain, const char **_name);
+
 int sss_parse_name_for_domains(TALLOC_CTX *memctx,
                                struct sss_domain_info *domains,
                                const char *default_domain,
-- 
1.9.3

-------------- next part --------------
>From 73f89c832d0755e8048ef3930addba0e5f84cb14 Mon Sep 17 00:00:00 2001
From: Lukas Slebodnik <lslebodn at redhat.com>
Date: Mon, 2 Dec 2013 13:03:33 +0100
Subject: [PATCH 2/4] NSS: Refactor expand_homedir_template

Function expand_homedir_template had lot of parameters.
After adding new expand option, all function call should be rewritten,
(usually argument NULL will be added)

This patch wraps all necessary arguments to structure.
---
 src/providers/ipa/ipa_s2n_exop.c      | 17 ++++++++------
 src/providers/ipa/ipa_subdomains_id.c | 23 +++++++++++--------
 src/responder/nss/nsssrv_cmd.c        | 29 ++++++++++++++---------
 src/responder/pac/pacsrv_utils.c      | 11 +++++++--
 src/util/sss_nss.c                    | 43 ++++++++++++++++++++---------------
 src/util/sss_nss.h                    | 12 +++++++---
 6 files changed, 85 insertions(+), 50 deletions(-)

diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
index 59a99061ac3ad6f24a8af50e9c1d574282301a1d..82beb3658fa9b283d53abb9f5bbac39987a7e9b0 100644
--- a/src/providers/ipa/ipa_s2n_exop.c
+++ b/src/providers/ipa/ipa_s2n_exop.c
@@ -648,6 +648,7 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
     struct resp_attrs *simple_attrs = NULL;
     time_t now;
     uint64_t timeout = 10*60*60; /* FIXME: find a better timeout ! */
+    struct sss_nss_homedir_ctx homedir_ctx;
     const char *homedir = NULL;
     struct sysdb_attrs *user_attrs = NULL;
     struct sysdb_attrs *group_attrs = NULL;
@@ -738,13 +739,15 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
     switch (attrs->response_type) {
         case RESP_USER:
             if (state->dom->subdomain_homedir) {
-                homedir =  expand_homedir_template(state,
-                                                   state->dom->subdomain_homedir,
-                                                   attrs->a.user.pw_name,
-                                                   attrs->a.user.pw_uid,
-                                                   NULL,
-                                                   state->dom->name,
-                                                   state->dom->flat_name);
+                ZERO_STRUCT(homedir_ctx);
+                homedir_ctx.username = attrs->a.user.pw_name;
+                homedir_ctx.uid = attrs->a.user.pw_uid;
+                homedir_ctx.domain = state->dom->name;
+                homedir_ctx.flatname = state->dom->flat_name;
+
+                homedir = expand_homedir_template(state,
+                                                  state->dom->subdomain_homedir,
+                                                  &homedir_ctx);
                 if (homedir == NULL) {
                     ret = ENOMEM;
                     goto done;
diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
index 21e9f85a1bf9277073c94708d31612e3bd8e6113..8657f90fa359513141446554e7efd616421c2264 100644
--- a/src/providers/ipa/ipa_subdomains_id.c
+++ b/src/providers/ipa/ipa_subdomains_id.c
@@ -366,10 +366,10 @@ get_subdomain_homedir_of_user(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
                               const char **_homedir)
 {
     errno_t ret;
-    char *name;
-    char *lc_name;
+    const char *name;
     const char *homedir;
     TALLOC_CTX *tmp_ctx;
+    struct sss_nss_homedir_ctx homedir_ctx;
 
     tmp_ctx = talloc_new(mem_ctx);
     if (tmp_ctx == NULL) {
@@ -377,22 +377,27 @@ get_subdomain_homedir_of_user(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
         goto done;
     }
 
-    ret = sss_parse_name(tmp_ctx, dom->names, fqname, NULL, &name);
+    ZERO_STRUCT(homedir_ctx);
+
+    homedir_ctx.uid = uid;
+    homedir_ctx.domain = dom->name;
+    homedir_ctx.flatname = dom->flat_name;
+    ret = sss_parse_name_const(tmp_ctx, dom->names, fqname,
+                               NULL, &name);
     if (ret != EOK) {
         goto done;
     }
 
     /* To be compatible with the old winbind based user lookups and IPA
      * clients the user name in the home directory path will be lower-case. */
-    lc_name = sss_tc_utf8_str_tolower(tmp_ctx, name);
-    if (lc_name == NULL) {
-        ret =ENOMEM;
+    homedir_ctx.username = sss_tc_utf8_str_tolower(tmp_ctx, name);
+    if (homedir_ctx.username == NULL) {
+        ret = ENOMEM;
         goto done;
     }
 
-    homedir = expand_homedir_template(tmp_ctx, dom->subdomain_homedir, lc_name,
-                                      uid, NULL, dom->name, dom->flat_name);
-
+    homedir = expand_homedir_template(tmp_ctx, dom->subdomain_homedir,
+                                      &homedir_ctx);
     if (homedir == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "expand_homedir_template failed\n");
         ret = ENOMEM;
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index e00849a481064769a89ed92c447519796c031065..b47a1ded5e409fe9e18298774c8fce00b3537e15 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -172,17 +172,18 @@ 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 *orig_name,
-                                        uint32_t uid)
+                                        struct sss_nss_homedir_ctx *homedir_ctx)
 {
     const char *homedir;
-    char *name;
+    const char *orig_name = homedir_ctx->username;
     errno_t ret;
 
     homedir = ldb_msg_find_attr_as_string(msg, SYSDB_HOMEDIR, NULL);
+    homedir_ctx->original = homedir;
 
     /* Subdomain users store FQDN in their name attribute */
-    ret = sss_parse_name(mem_ctx, dom->names, orig_name, NULL, &name);
+    ret = sss_parse_name_const(mem_ctx, dom->names, orig_name,
+                               NULL, &homedir_ctx->username);
     if (ret != EOK) {
         DEBUG(SSSDBG_MINOR_FAILURE, "Could not parse [%s] into "
               "name-value components.\n", orig_name);
@@ -194,10 +195,10 @@ static const char *get_homedir_override(TALLOC_CTX *mem_ctx,
      */
     if (dom->override_homedir) {
         return expand_homedir_template(mem_ctx, dom->override_homedir,
-                                       name, uid, homedir, dom->name, NULL);
+                                       homedir_ctx);
     } else if (nctx->override_homedir) {
         return expand_homedir_template(mem_ctx, nctx->override_homedir,
-                                       name, uid, homedir, dom->name, NULL);
+                                       homedir_ctx);
     }
 
     if (!homedir || *homedir == '\0') {
@@ -206,12 +207,10 @@ static const char *get_homedir_override(TALLOC_CTX *mem_ctx,
          */
         if (dom->fallback_homedir) {
             return expand_homedir_template(mem_ctx, dom->fallback_homedir,
-                                           name, uid, homedir,
-                                           dom->name, NULL);
+                                           homedir_ctx);
         } else if (nctx->fallback_homedir) {
             return expand_homedir_template(mem_ctx, nctx->fallback_homedir,
-                                           name, uid, homedir,
-                                           dom->name, NULL);
+                                           homedir_ctx);
         }
     }
 
@@ -317,6 +316,7 @@ static int fill_pwent(struct sss_packet *packet,
     bool packet_initialized = false;
     int ncret;
     TALLOC_CTX *tmp_ctx = NULL;
+    struct sss_nss_homedir_ctx homedir_ctx;
 
     to_sized_string(&pwfield, nctx->pwfield);
 
@@ -372,7 +372,14 @@ static int fill_pwent(struct sss_packet *packet,
         } else {
             to_sized_string(&gecos, tmpstr);
         }
-        tmpstr = get_homedir_override(tmp_ctx, msg, nctx, dom, name.str, uid);
+
+        ZERO_STRUCT(homedir_ctx);
+
+        homedir_ctx.username = name.str;
+        homedir_ctx.uid = uid;
+        homedir_ctx.domain = dom->name;
+
+        tmpstr = get_homedir_override(tmp_ctx, msg, nctx, dom, &homedir_ctx);
         if (!tmpstr) {
             to_sized_string(&homedir, "/");
         } else {
diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c
index 035fe84c0f39aaa40a403a8ac5f19527b2191267..fe7e889349963e63ecd90706fc918a252af044dd 100644
--- a/src/responder/pac/pacsrv_utils.c
+++ b/src/responder/pac/pacsrv_utils.c
@@ -353,6 +353,7 @@ errno_t get_pwd_from_pac(TALLOC_CTX *mem_ctx,
     char *upn;
     hash_key_t key;
     hash_value_t value;
+    struct sss_nss_homedir_ctx homedir_ctx;
 
     pwd = talloc_zero(mem_ctx, struct passwd);
     if (pwd == NULL) {
@@ -439,9 +440,15 @@ errno_t get_pwd_from_pac(TALLOC_CTX *mem_ctx,
     /* Check if there is a special homedir template for sub-domains. If not a
      * fallback will be added by the NSS responder. */
     if (IS_SUBDOMAIN(dom) && dom->subdomain_homedir) {
+        ZERO_STRUCT(homedir_ctx);
+
+        homedir_ctx.username = lname;
+        homedir_ctx.uid = pwd->pw_uid;
+        homedir_ctx.domain = dom->name;
+        homedir_ctx.flatname = dom->flat_name;
+
         pwd->pw_dir = expand_homedir_template(pwd, dom->subdomain_homedir,
-                                              lname, pwd->pw_uid, NULL,
-                                              dom->name, dom->flat_name);
+                                              &homedir_ctx);
         if (pwd->pw_dir == NULL) {
             ret = ENOMEM;
             goto done;
diff --git a/src/util/sss_nss.c b/src/util/sss_nss.c
index 406c95cd0f76c9e73705bc49b4261160d26bfc58..25a030c3f7f6d6cf6baff0ca9ad73f94b3cbae8e 100644
--- a/src/util/sss_nss.c
+++ b/src/util/sss_nss.c
@@ -23,9 +23,7 @@
 #include "util/sss_nss.h"
 
 char *expand_homedir_template(TALLOC_CTX *mem_ctx, const char *template,
-                              const char *username, uint32_t uid,
-                              const char *original, const char *domain,
-                              const char *flatname)
+                              struct sss_nss_homedir_ctx *homedir_ctx)
 {
     char *copy;
     char *p;
@@ -40,6 +38,11 @@ char *expand_homedir_template(TALLOC_CTX *mem_ctx, const char *template,
         return NULL;
     }
 
+    if (homedir_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Missing home directory data.\n");
+        return NULL;
+    }
+
     tmp_ctx = talloc_new(NULL);
     if (!tmp_ctx) return NULL;
 
@@ -66,67 +69,71 @@ char *expand_homedir_template(TALLOC_CTX *mem_ctx, const char *template,
         }
         switch( *n ) {
             case 'u':
-                if (username == NULL) {
-                    DEBUG(SSSDBG_CRIT_FAILURE, "Cannot expand user name template "
-                                                "because user name is empty.\n");
+                if (homedir_ctx->username == NULL) {
+                    DEBUG(SSSDBG_CRIT_FAILURE,
+                          "Cannot expand user name template because user name "
+                          "is empty.\n");
                     goto done;
                 }
                 result = talloc_asprintf_append(result, "%s%s", p,
-                                                username);
+                                                homedir_ctx->username);
                 break;
 
             case 'U':
-                if (uid == 0) {
+                if (homedir_ctx->uid == 0) {
                     DEBUG(SSSDBG_CRIT_FAILURE, "Cannot expand uid template "
                                                 "because uid is invalid.\n");
                     goto done;
                 }
                 result = talloc_asprintf_append(result, "%s%d", p,
-                                                uid);
+                                                homedir_ctx->uid);
                 break;
 
             case 'd':
-                if (domain == NULL) {
+                if (homedir_ctx->domain == NULL) {
                     DEBUG(SSSDBG_CRIT_FAILURE, "Cannot expand domain name "
                                                 "template because domain name "
                                                 "is empty.\n");
                     goto done;
                 }
                 result = talloc_asprintf_append(result, "%s%s", p,
-                                                domain);
+                                                homedir_ctx->domain);
                 break;
 
             case 'f':
-                if (domain == NULL || username == NULL) {
+                if (homedir_ctx->domain == NULL
+                        || homedir_ctx->username == NULL) {
                     DEBUG(SSSDBG_CRIT_FAILURE, "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);
+                                                homedir_ctx->username,
+                                                homedir_ctx->domain);
                 break;
 
             case 'o':
-                if (original == NULL) {
+                if (homedir_ctx->original == NULL) {
                     DEBUG(SSSDBG_CRIT_FAILURE,
                           "Original home directory for %s is not available, "
-                           "using empty string\n", username);
+                           "using empty string\n", homedir_ctx->username);
                     orig = "";
                 } else {
-                    orig = original;
+                    orig = homedir_ctx->original;
                 }
                 result = talloc_asprintf_append(result, "%s%s", p, orig);
                 break;
 
             case 'F':
-                if (flatname == NULL) {
+                if (homedir_ctx->flatname == NULL) {
                     DEBUG(SSSDBG_CRIT_FAILURE, "Cannot expand domain name "
                                                 "template because domain flat "
                                                 "name is empty.\n");
                     goto done;
                 }
-                result = talloc_asprintf_append(result, "%s%s", p, flatname);
+                result = talloc_asprintf_append(result, "%s%s", p,
+                                                homedir_ctx->flatname);
                 break;
 
             case '%':
diff --git a/src/util/sss_nss.h b/src/util/sss_nss.h
index 6dcdbaef376ffb6cc45733ce6af8aeceaabe05bb..771b9ce58db858f8cef8cac6ae3bf9fb382ef0d0 100644
--- a/src/util/sss_nss.h
+++ b/src/util/sss_nss.h
@@ -25,8 +25,14 @@
 #include <sys/types.h>
 #include <talloc.h>
 
+struct sss_nss_homedir_ctx {
+    const char *username;
+    uint32_t uid;
+    const char *original;
+    const char *domain;
+    const char *flatname;
+};
+
 char *expand_homedir_template(TALLOC_CTX *mem_ctx, const char *template,
-                              const char *username, uint32_t uid,
-                              const char *original, const char *domain,
-                              const char *flatname);
+                              struct sss_nss_homedir_ctx *homedir_ctx);
 #endif
-- 
1.9.3

-------------- next part --------------
>From f0d08bedf2e9891595f579d473fe05ff0f2ae591 Mon Sep 17 00:00:00 2001
From: Lukas Slebodnik <lslebodn at redhat.com>
Date: Mon, 2 Dec 2013 14:19:09 +0100
Subject: [PATCH 3/4] NSS: Add option to expand homedir template format

LDAP server can contain template for home directory instead of plain string.
This patch adds new expand option "%H", which will be replaced with value
from configuration option homedir_substring (from sssd.conf)

Resolves:
https://fedorahosted.org/sssd/ticket/1853
---
 src/confdb/confdb.c                   | 10 ++++++++++
 src/confdb/confdb.h                   |  3 +++
 src/config/SSSDConfigTest.py          |  2 ++
 src/config/etc/sssd.api.conf          |  2 ++
 src/man/include/homedir_substring.xml | 18 ++++++++++++++++++
 src/man/include/override_homedir.xml  |  7 +++++++
 src/man/po/po4a.cfg                   |  1 +
 src/man/sssd-ad.5.xml                 |  1 +
 src/man/sssd.conf.5.xml               |  1 +
 src/providers/ipa/ipa_s2n_exop.c      |  1 +
 src/providers/ipa/ipa_subdomains_id.c |  1 +
 src/responder/nss/nsssrv.c            |  6 ++++++
 src/responder/nss/nsssrv.h            |  1 +
 src/responder/nss/nsssrv_cmd.c        | 11 +++++++++--
 src/responder/pac/pacsrv_utils.c      |  1 +
 src/util/domain_info_utils.c          |  1 +
 src/util/sss_nss.c                    | 11 +++++++++++
 src/util/sss_nss.h                    |  1 +
 18 files changed, 77 insertions(+), 2 deletions(-)
 create mode 100644 src/man/include/homedir_substring.xml

diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
index 6f36535fdb27c0b987968cf440c1dccb63f250d9..15de9616ffdddf640f29e6b816c246ee05da48c7 100644
--- a/src/confdb/confdb.c
+++ b/src/confdb/confdb.c
@@ -1118,6 +1118,16 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
     }
 
     tmp = ldb_msg_find_attr_as_string(res->msgs[0],
+                                      CONFDB_NSS_HOMEDIR_SUBSTRING, NULL);
+    if (tmp != NULL) {
+        domain->homedir_substr = talloc_strdup(domain, tmp);
+        if (domain->homedir_substr == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    tmp = ldb_msg_find_attr_as_string(res->msgs[0],
                                       CONFDB_NSS_OVERRIDE_SHELL, NULL);
     if (tmp != NULL) {
         domain->override_shell = talloc_strdup(domain, tmp);
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 2cebf24262b73a9007a1104604a6e11a21065cd7..ba33ea5d7123c6e799f13529b60c5938ad20e411 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -97,6 +97,8 @@
 #define CONFDB_NSS_SHELL_FALLBACK "shell_fallback"
 #define CONFDB_NSS_DEFAULT_SHELL "default_shell"
 #define CONFDB_MEMCACHE_TIMEOUT "memcache_timeout"
+#define CONFDB_NSS_HOMEDIR_SUBSTRING "homedir_substring"
+#define CONFDB_DEFAULT_HOMEDIR_SUBSTRING "/home"
 
 /* PAM */
 #define CONFDB_PAM_CONF_ENTRY "config/pam"
@@ -220,6 +222,7 @@ struct sss_domain_info {
     const char *override_homedir;
     const char *fallback_homedir;
     const char *subdomain_homedir;
+    const char *homedir_substr;
     const char *override_shell;
     const char *default_shell;
 
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index a3c25540d2a870eb02df51a416736736fde279d9..d4ed06ae85f23debb7a715eee5f338de8aad466c 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -522,6 +522,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'case_sensitive',
             'override_homedir',
             'fallback_homedir',
+            'homedir_substring',
             'override_shell',
             'default_shell',
             'pwd_expiration_warning',
@@ -879,6 +880,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'case_sensitive',
             'override_homedir',
             'fallback_homedir',
+            'homedir_substring',
             'override_shell',
             'default_shell',
             'pwd_expiration_warning',
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index c7c1232c378d7e641da678d853247ddfd31bb736..5e5a9284e9ad80d822fe1db4269353aeb7925682 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -36,6 +36,7 @@ filter_users_in_groups = bool, None, false
 pwfield = str, None, false
 override_homedir = str, None, false
 fallback_homedir = str, None, false
+homedir_substring = str, None, false, /home
 override_shell = str, None, false
 allowed_shells = list, str, false
 vetoed_shells = list, str, false
@@ -118,6 +119,7 @@ override_gid = int, None, false
 case_sensitive = bool, None, false
 override_homedir = str, None, false
 fallback_homedir = str, None, false
+homedir_substring = str, None, false
 override_shell = str, None, false
 default_shell = str, None, false
 description = str, None, false
diff --git a/src/man/include/homedir_substring.xml b/src/man/include/homedir_substring.xml
new file mode 100644
index 0000000000000000000000000000000000000000..54d9bc94b9d6ae90e4e450691e79805c1eb83ba7
--- /dev/null
+++ b/src/man/include/homedir_substring.xml
@@ -0,0 +1,18 @@
+<varlistentry>
+    <term>homedir_substring (string)</term>
+    <listitem>
+        <para>
+            The value of this option will be used in the expansion of the
+            <emphasis>override_homedir</emphasis> option if the template
+            contains the format string <emphasis>%H</emphasis>. An LDAP
+            directory entry can directly contain this template so that this
+            option can be used to expand the home directory path for each
+            client machine (or operating system). It can be set per-domain or
+            globally in the [nss] section. A value specified in a domain
+            section will override one set in the [nss] section.
+        </para>
+        <para>
+            Default: /home
+        </para>
+    </listitem>
+</varlistentry>
diff --git a/src/man/include/override_homedir.xml b/src/man/include/override_homedir.xml
index 773d0b6616858ab5c0139033255bc60076857651..552d7eb9d3c8c4ec01cface4e062723ef9c29e1f 100644
--- a/src/man/include/override_homedir.xml
+++ b/src/man/include/override_homedir.xml
@@ -31,6 +31,13 @@
                     </para></listitem>
             </varlistentry>
             <varlistentry>
+                <term>%H</term>
+                <listitem><para>
+                    The value of configure option
+                    <emphasis>homedir_substring</emphasis>.
+                    </para></listitem>
+            </varlistentry>
+            <varlistentry>
                 <term>%%</term>
                 <listitem><para>a literal '%'</para>
                 </listitem>
diff --git a/src/man/po/po4a.cfg b/src/man/po/po4a.cfg
index baad09d302de61f566a981d6deabdc59930ef204..70fa28c88888654f0d35496c363ffc7fb17f76ff 100644
--- a/src/man/po/po4a.cfg
+++ b/src/man/po/po4a.cfg
@@ -37,3 +37,4 @@
 [type:docbook] include/ldap_search_bases.xml $lang:$(builddir)/$lang/include/ldap_search_bases.xml opt:"-k 0"
 [type:docbook] include/autofs_restart.xml $lang:$(builddir)/$lang/include/autofs_restart.xml opt:"-k 0"
 [type:docbook] include/override_homedir.xml $lang:$(builddir)/$lang/include/override_homedir.xml opt:"-k 0"
+[type:docbook] include/homedir_substring.xml $lang:$(builddir)/$lang/include/homedir_substring.xml opt:"-k 0"
diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml
index 21f735e0a6c5c900723d82f13da30171f0ddeb9b..9070144ce4f478d20e93c8b7a4f732b56de98836 100644
--- a/src/man/sssd-ad.5.xml
+++ b/src/man/sssd-ad.5.xml
@@ -413,6 +413,7 @@ FOREST:EXAMPLE.COM:(memberOf=cn=admins,ou=groups,dc=example,dc=com)
                 </varlistentry>
 
                 <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/override_homedir.xml" />
+                <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/homedir_substring.xml" />
 
                 <varlistentry>
                     <term>krb5_use_enterprise_principal (boolean)</term>
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index c542b52562a9357b27c75c98aca8dd40fdd733e9..48fd2207086babfe09934b025d637f65c992624b 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -513,6 +513,7 @@
                     </listitem>
                 </varlistentry>
                 <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/override_homedir.xml" />
+                <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/homedir_substring.xml" />
                 <varlistentry>
                     <term>fallback_homedir (string)</term>
                     <listitem>
diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
index 82beb3658fa9b283d53abb9f5bbac39987a7e9b0..ccf4c23173a63c2dd62cc6b4ac6b38bde760bb94 100644
--- a/src/providers/ipa/ipa_s2n_exop.c
+++ b/src/providers/ipa/ipa_s2n_exop.c
@@ -744,6 +744,7 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
                 homedir_ctx.uid = attrs->a.user.pw_uid;
                 homedir_ctx.domain = state->dom->name;
                 homedir_ctx.flatname = state->dom->flat_name;
+                homedir_ctx.config_homedir_substr = state->dom->homedir_substr;
 
                 homedir = expand_homedir_template(state,
                                                   state->dom->subdomain_homedir,
diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
index 8657f90fa359513141446554e7efd616421c2264..1c4c315a732aa1e1127c63ca428f54c4621d5b94 100644
--- a/src/providers/ipa/ipa_subdomains_id.c
+++ b/src/providers/ipa/ipa_subdomains_id.c
@@ -382,6 +382,7 @@ get_subdomain_homedir_of_user(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
     homedir_ctx.uid = uid;
     homedir_ctx.domain = dom->name;
     homedir_ctx.flatname = dom->flat_name;
+    homedir_ctx.config_homedir_substr = dom->homedir_substr;
     ret = sss_parse_name_const(tmp_ctx, dom->names, fqname,
                                NULL, &name);
     if (ret != EOK) {
diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
index e4896a79dc8a15554cdb1a7616d5353650eca209..1f762cca83083c911e2619263c7d89e267a34d61 100644
--- a/src/responder/nss/nsssrv.c
+++ b/src/responder/nss/nsssrv.c
@@ -291,6 +291,12 @@ static int nss_get_config(struct nss_ctx *nctx,
                             &nctx->default_shell);
     if (ret != EOK) goto done;
 
+    ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
+                            CONFDB_NSS_HOMEDIR_SUBSTRING,
+                            CONFDB_DEFAULT_HOMEDIR_SUBSTRING,
+                            &nctx->homedir_substr);
+    if (ret != EOK) goto done;
+
     ret = 0;
 done:
     return ret;
diff --git a/src/responder/nss/nsssrv.h b/src/responder/nss/nsssrv.h
index a36589837529a7b61768845eb3493197b13df8cd..a5b946b7e4a38d7d8b35ec5df1b6644d01896470 100644
--- a/src/responder/nss/nsssrv.h
+++ b/src/responder/nss/nsssrv.h
@@ -62,6 +62,7 @@ struct nss_ctx {
 
     char *override_homedir;
     char *fallback_homedir;
+    char *homedir_substr;
     char **allowed_shells;
     char *override_shell;
     char **vetoed_shells;
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index b47a1ded5e409fe9e18298774c8fce00b3537e15..a168a3e5d6116a1f6ca2c358707650c0e6c9e514 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -190,6 +190,13 @@ static const char *get_homedir_override(TALLOC_CTX *mem_ctx,
         return NULL;
     }
 
+    /* Check to see which homedir_prefix to use. */
+    if (dom->homedir_substr != NULL) {
+        homedir_ctx->config_homedir_substr = dom->homedir_substr;
+    } else if (nctx->homedir_substr != NULL) {
+        homedir_ctx->config_homedir_substr = nctx->homedir_substr;
+    }
+
     /* Check whether we are unconditionally overriding the server
      * for home directory locations.
      */
@@ -214,8 +221,8 @@ static const char *get_homedir_override(TALLOC_CTX *mem_ctx,
         }
     }
 
-    /* Return the value we got from the provider */
-    return talloc_strdup(mem_ctx, homedir);
+    /* Provider can also return template, try to expand it.*/
+    return expand_homedir_template(mem_ctx, homedir, homedir_ctx);
 }
 
 static const char *get_shell_override(TALLOC_CTX *mem_ctx,
diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c
index fe7e889349963e63ecd90706fc918a252af044dd..7d37cda87ab4a5c233474beb67d91aa03c939b72 100644
--- a/src/responder/pac/pacsrv_utils.c
+++ b/src/responder/pac/pacsrv_utils.c
@@ -446,6 +446,7 @@ errno_t get_pwd_from_pac(TALLOC_CTX *mem_ctx,
         homedir_ctx.uid = pwd->pw_uid;
         homedir_ctx.domain = dom->name;
         homedir_ctx.flatname = dom->flat_name;
+        homedir_ctx.config_homedir_substr = dom->homedir_substr;
 
         pwd->pw_dir = expand_homedir_template(pwd, dom->subdomain_homedir,
                                               &homedir_ctx);
diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
index fc428707612d97a313f020154c3d19efb8fbb2b1..a0fb7b2abe720e798817636b3810d36b7bbc0d93 100644
--- a/src/util/domain_info_utils.c
+++ b/src/util/domain_info_utils.c
@@ -281,6 +281,7 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
     dom->subdomain_homedir = parent->subdomain_homedir;
     dom->override_shell = parent->override_shell;
     dom->default_shell = parent->default_shell;
+    dom->homedir_substr = parent->homedir_substr;
 
     if (parent->sysdb == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "Missing sysdb context in parent domain.\n");
diff --git a/src/util/sss_nss.c b/src/util/sss_nss.c
index 25a030c3f7f6d6cf6baff0ca9ad73f94b3cbae8e..8cc1416d714a2486e07fcc0c1c43f112428806c5 100644
--- a/src/util/sss_nss.c
+++ b/src/util/sss_nss.c
@@ -136,6 +136,17 @@ char *expand_homedir_template(TALLOC_CTX *mem_ctx, const char *template,
                                                 homedir_ctx->flatname);
                 break;
 
+            case 'H':
+                if (homedir_ctx->config_homedir_substr == NULL) {
+                    DEBUG(SSSDBG_CRIT_FAILURE,
+                          "Cannot expand home directory substring template "
+                          "substring is empty.\n");
+                    goto done;
+                }
+                result = talloc_asprintf_append(result, "%s%s", p,
+                                           homedir_ctx->config_homedir_substr);
+                break;
+
             case '%':
                 result = talloc_asprintf_append(result, "%s%%", p);
                 break;
diff --git a/src/util/sss_nss.h b/src/util/sss_nss.h
index 771b9ce58db858f8cef8cac6ae3bf9fb382ef0d0..19bf26589a9a5d76369f56ef9ecf7bedbb29e07b 100644
--- a/src/util/sss_nss.h
+++ b/src/util/sss_nss.h
@@ -31,6 +31,7 @@ struct sss_nss_homedir_ctx {
     const char *original;
     const char *domain;
     const char *flatname;
+    const char *config_homedir_substr;
 };
 
 char *expand_homedir_template(TALLOC_CTX *mem_ctx, const char *template,
-- 
1.9.3

-------------- next part --------------
>From c5de67a6e837877af27bbda2529b011a620942c7 Mon Sep 17 00:00:00 2001
From: Lukas Slebodnik <lslebodn at redhat.com>
Date: Wed, 28 May 2014 12:54:52 +0200
Subject: [PATCH 4/4] TEST: Add test for expand homedir

---
 src/tests/cmocka/test_utils.c | 190 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 190 insertions(+)

diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
index 30b87179191e5b9a09b1bec6722895defbf7df54..8b996c2fba8c6ba772dc5cb42eac621a3c4c7407 100644
--- a/src/tests/cmocka/test_utils.c
+++ b/src/tests/cmocka/test_utils.c
@@ -23,6 +23,7 @@
 #include <popt.h>
 
 #include "tests/cmocka/common_mock.h"
+#include "util/sss_nss.h"
 
 #define TESTS_PATH "tests_utils"
 #define TEST_CONF_DB "test_utils_conf.ldb"
@@ -33,6 +34,19 @@
 #define FLATNAME_TMPL "name_%zu"
 #define SID_TMPL "S-1-5-21-1-2-%zu"
 
+#define MACRO_EXPAND(tok) #tok
+#define STR(tok) MACRO_EXPAND(tok)
+
+#define USERNAME "sssduser"
+#define UID      1234
+#define DOMAIN   "sssddomain"
+#define ORIGINAL_HOME "/home/user"
+#define FLATNAME "flatname"
+#define HOMEDIR_SUBSTR "/mnt/home"
+
+#define DUMMY "dummy"
+#define DUMMY2 "dummy2"
+
 struct dom_list_test_ctx {
     size_t dom_count;
     struct sss_domain_info *dom_list;
@@ -687,6 +701,178 @@ void test_sss_filter_sanitize_for_dom(void **state)
     talloc_free(lc_sanitized);
 }
 
+void check_expanded_value(TALLOC_CTX *tmp_ctx,
+                          struct sss_nss_homedir_ctx *homedir_ctx,
+                          const char *template, const char *exp_val)
+{
+    char *homedir;
+
+    homedir = expand_homedir_template(tmp_ctx, template, homedir_ctx);
+    if (exp_val != NULL) {
+        assert_string_equal(homedir, exp_val);
+    } else {
+        assert_null(homedir);
+    }
+
+    talloc_free(homedir);
+}
+
+void setup_homedir_ctx(void **state)
+{
+    struct sss_nss_homedir_ctx *homedir_ctx;
+
+    assert_true(leak_check_setup());
+
+    homedir_ctx= talloc_zero(global_talloc_context,
+                             struct sss_nss_homedir_ctx);
+    assert_non_null(homedir_ctx);
+
+    homedir_ctx->username = USERNAME;
+    homedir_ctx->uid = UID;
+    homedir_ctx->original = ORIGINAL_HOME;
+    homedir_ctx->domain = DOMAIN;
+    homedir_ctx->flatname = FLATNAME;
+    homedir_ctx->config_homedir_substr = HOMEDIR_SUBSTR;
+
+    check_leaks_push(homedir_ctx);
+    *state = homedir_ctx;
+}
+
+void teardown_homedir_ctx(void **state)
+{
+    struct sss_nss_homedir_ctx *homedir_ctx = talloc_get_type(*state,
+                                                 struct sss_nss_homedir_ctx);
+    if (homedir_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Type mismatch\n");
+        return;
+    }
+
+    assert_true(check_leaks_pop(homedir_ctx) == true);
+    talloc_free(homedir_ctx);
+    assert_true(leak_check_teardown());
+}
+
+void test_expand_homedir_template_NULL(void **state)
+{
+    TALLOC_CTX *tmp_ctx;
+    char *homedir;
+    struct sss_nss_homedir_ctx *homedir_ctx;
+
+    /* following format strings requires data in homedir_ctx */
+    const char *format_strings[] = { "%u", "%U", "%d", "%f", "%F", "%H",
+                                     NULL };
+    int i;
+
+    tmp_ctx = talloc_new(NULL);
+    assert_non_null(tmp_ctx);
+
+    homedir_ctx = talloc_zero(tmp_ctx, struct sss_nss_homedir_ctx);
+    assert_non_null(homedir_ctx);
+
+    homedir = expand_homedir_template(tmp_ctx, NULL, NULL);
+    assert_null(homedir);
+
+    homedir = expand_homedir_template(tmp_ctx, "template", NULL);
+    assert_null(homedir);
+
+    /* missing data in homedir_ctx */
+    check_expanded_value(tmp_ctx, homedir_ctx, "%%", "%");
+    check_expanded_value(tmp_ctx, homedir_ctx, "%o", "");
+
+    for (i = 0; format_strings[i] != NULL; ++i) {
+        check_expanded_value(tmp_ctx, homedir_ctx, format_strings[i], NULL);
+    }
+
+    /* flatname requires domain and username */
+    homedir_ctx->username = DUMMY;
+    check_expanded_value(tmp_ctx, homedir_ctx, "%f", NULL);
+
+    homedir_ctx->username = NULL;
+    homedir_ctx->domain = DUMMY;
+    check_expanded_value(tmp_ctx, homedir_ctx, "%f", NULL);
+
+    /* test unknown format string */
+    check_expanded_value(tmp_ctx, homedir_ctx, "%x", NULL);
+
+    /* test malformed format string */
+    check_expanded_value(tmp_ctx, homedir_ctx, "%", NULL);
+
+    talloc_free(tmp_ctx);
+}
+
+void test_expand_homedir_template(void **state)
+{
+    struct sss_nss_homedir_ctx *homedir_ctx = talloc_get_type(*state,
+                                                 struct sss_nss_homedir_ctx);
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    assert_non_null(tmp_ctx);
+
+    /* string without template */
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY, DUMMY);
+
+    check_expanded_value(tmp_ctx, homedir_ctx, "%u", USERNAME);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%u", DUMMY USERNAME);
+    check_expanded_value(tmp_ctx, homedir_ctx, "%u"DUMMY, USERNAME DUMMY);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%u"DUMMY2,
+                                               DUMMY USERNAME DUMMY2);
+
+    check_expanded_value(tmp_ctx, homedir_ctx, "%U", STR(UID));
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%U", DUMMY STR(UID));
+    check_expanded_value(tmp_ctx, homedir_ctx, "%U"DUMMY, STR(UID) DUMMY);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%U"DUMMY2,
+                                               DUMMY STR(UID) DUMMY2);
+
+    check_expanded_value(tmp_ctx, homedir_ctx, "%d", DOMAIN);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%d", DUMMY DOMAIN);
+    check_expanded_value(tmp_ctx, homedir_ctx, "%d"DUMMY, DOMAIN DUMMY);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%d"DUMMY2,
+                                               DUMMY DOMAIN DUMMY2);
+
+    check_expanded_value(tmp_ctx, homedir_ctx, "%f", USERNAME"@"DOMAIN);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%f",
+                                               DUMMY USERNAME"@"DOMAIN);
+    check_expanded_value(tmp_ctx, homedir_ctx, "%f"DUMMY,
+                                               USERNAME"@"DOMAIN DUMMY);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%f"DUMMY2,
+                                               DUMMY USERNAME"@"DOMAIN DUMMY2);
+
+    check_expanded_value(tmp_ctx, homedir_ctx, "%o", ORIGINAL_HOME);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%o", DUMMY ORIGINAL_HOME);
+    check_expanded_value(tmp_ctx, homedir_ctx, "%o"DUMMY, ORIGINAL_HOME DUMMY);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%o"DUMMY2,
+                                               DUMMY ORIGINAL_HOME DUMMY2);
+
+    check_expanded_value(tmp_ctx, homedir_ctx, "%F", FLATNAME);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%F", DUMMY FLATNAME);
+    check_expanded_value(tmp_ctx, homedir_ctx, "%F"DUMMY, FLATNAME DUMMY);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%F"DUMMY2,
+                                               DUMMY FLATNAME DUMMY2);
+
+    check_expanded_value(tmp_ctx, homedir_ctx, "%H", HOMEDIR_SUBSTR);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%H",
+                                               DUMMY HOMEDIR_SUBSTR);
+    check_expanded_value(tmp_ctx, homedir_ctx, "%H"DUMMY,
+                                               HOMEDIR_SUBSTR DUMMY);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%H"DUMMY2,
+                                               DUMMY HOMEDIR_SUBSTR DUMMY2);
+
+    check_expanded_value(tmp_ctx, homedir_ctx, "%%", "%");
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%%", DUMMY"%");
+    check_expanded_value(tmp_ctx, homedir_ctx, "%%"DUMMY, "%"DUMMY);
+    check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%%"DUMMY2,
+                                               DUMMY"%"DUMMY2);
+
+    /* test all format strings */
+    check_expanded_value(tmp_ctx, homedir_ctx,
+                         DUMMY"/%u/%U/%d/%f/%o/%F/%%/%H/"DUMMY2,
+                         DUMMY"/"USERNAME"/" STR(UID) "/"DOMAIN"/"
+                         USERNAME"@"DOMAIN"/"ORIGINAL_HOME"/"FLATNAME"/%/"
+                         HOMEDIR_SUBSTR"/"DUMMY2);
+    talloc_free(tmp_ctx);
+}
+
 int main(int argc, const char *argv[])
 {
     poptContext pc;
@@ -724,6 +910,10 @@ int main(int argc, const char *argv[])
 
         unit_test_setup_teardown(test_sss_filter_sanitize_for_dom,
                                  setup_dom_list, teardown_dom_list),
+
+        unit_test(test_expand_homedir_template_NULL),
+        unit_test_setup_teardown(test_expand_homedir_template,
+                                 setup_homedir_ctx, teardown_homedir_ctx),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
-- 
1.9.3



More information about the sssd-devel mailing list