[SSSD] [PATCH] Support flat name in the full_name_format

Jakub Hrozek jhrozek at redhat.com
Sat May 11 18:45:40 UTC 2013


Hi,

the attached patches add support for using flat name in the FQDN output
specifier.

The problem with this RFE was that the format is a printf-like format
specifier and backwards compatibility must be maintained. I used an idea
that originally came from Sumit to add a new expansion to the existing
name and domain ones. But in order to avoid compile-time warnings, I had
to make sure the format specifier and the arguments match 1:1 -- so the
code adds the unused format specifier and defaults the unused arguments
to "".

[PATCH 1/3] Add utility functions for formatting fully-qualified names
Instead of using printf-like functions directly, provide two wrappers
that would encapsulate formatting the fully-qualified names. No
functional change is present in this patch.

[PATCH 2/3] Check the validity of FQname format prior to using it
Adds a sanity check of the fqname pattern. Fails if the username pattern
is not specified at all and warns if the domain pattern is not
specified.

[PATCH 3/3] Allow flat name in the FQname format
https://fedorahosted.org/sssd/ticket/1648

Adds another expansion in the printf format that allows the user to use
the domain flat name in the format. Includes a unit test.
-------------- next part --------------
>From 695b233d2aaba1c61ecfa0f14d5e58b69f96cae5 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 10 May 2013 18:19:12 +0200
Subject: [PATCH 1/3] Add utility functions for formatting fully-qualified
 names

Instead of using printf-like functions directly, provide two wrappers
that would encapsulate formatting the fully-qualified names. No
functional change is present in this patch.
---
 src/db/sysdb_search.c            |  6 ++---
 src/providers/ipa/ipa_s2n_exop.c |  8 +++----
 src/responder/nss/nsssrv_cmd.c   | 49 ++++++++++++++++------------------------
 src/responder/pac/pacsrv_cmd.c   |  4 ++--
 src/responder/pac/pacsrv_utils.c |  3 +--
 src/tools/sss_cache.c            |  3 +--
 src/util/usertools.c             | 25 ++++++++++++++++++++
 src/util/util.h                  | 16 +++++++++++++
 8 files changed, 70 insertions(+), 44 deletions(-)

diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
index 843c746d2a724aa63d885ff40d025633ba401833..344ff6fae476c984132acb19064958135d750b1f 100644
--- a/src/db/sysdb_search.c
+++ b/src/db/sysdb_search.c
@@ -57,8 +57,7 @@ int sysdb_getpwnam(TALLOC_CTX *mem_ctx,
      * search as well by default */
     if (IS_SUBDOMAIN(domain) && domain->fqnames) {
         ret = ENOMEM;
-        src_name = talloc_asprintf(tmp_ctx, domain->names->fq_fmt,
-                                   name, domain->name);
+        src_name = sss_tc_fqname(tmp_ctx, domain->names, domain, name);
     } else {
         ret = EINVAL;
         src_name = name;
@@ -245,8 +244,7 @@ int sysdb_getgrnam(TALLOC_CTX *mem_ctx,
      * search as well by default */
     if (IS_SUBDOMAIN(domain) && domain->fqnames) {
         ret = ENOMEM;
-        src_name = talloc_asprintf(tmp_ctx, domain->names->fq_fmt,
-                                   name, domain->name);
+        src_name = sss_tc_fqname(tmp_ctx, domain->names, domain, name);
     } else {
         ret = EINVAL;
         src_name = name;
diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
index 02a7bc2298e01adb0972fef588d002ee2328b869..f589a06fe12ff3bb02129b3fa5ad78feddcb7724 100644
--- a/src/providers/ipa/ipa_s2n_exop.c
+++ b/src/providers/ipa/ipa_s2n_exop.c
@@ -757,8 +757,8 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
             }
 
             /* we always use the fully qualified name for subdomain users */
-            name = talloc_asprintf(state, state->dom->names->fq_fmt,
-                                   attrs->a.user.pw_name, state->dom->name);
+            name = sss_tc_fqname(state, state->dom->names, state->dom,
+                                 attrs->a.user.pw_name);
             if (!name) {
                 DEBUG(SSSDBG_OP_FAILURE, ("failed to format user name.\n"));
                 ret = ENOMEM;
@@ -822,8 +822,8 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
             break;
         case RESP_GROUP:
             /* we always use the fully qualified name for subdomain users */
-            name = talloc_asprintf(state, state->dom->names->fq_fmt,
-                                   attrs->a.group.gr_name, state->dom->name);
+            name = sss_tc_fqname(state, state->dom->names, state->dom,
+                                 attrs->a.group.gr_name);
             if (!name) {
                 DEBUG(SSSDBG_OP_FAILURE, ("failed to format user name,\n"));
                 ret = ENOMEM;
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index c409e794716c8763ea78b029d777be96cd4ef3a5..4f175dc50e834b3eb36da0af01f59c2864ef01a8 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -303,13 +303,10 @@ static int fill_pwent(struct sss_packet *packet,
     int i, ret, num, t;
     bool add_domain = (!IS_SUBDOMAIN(dom) && dom->fqnames);
     const char *domain = dom->name;
-    const char *namefmt;
     bool packet_initialized = false;
     int ncret;
     TALLOC_CTX *tmp_ctx = NULL;
 
-    namefmt = dom->names->fq_fmt;
-
     if (add_domain) dom_len = strlen(domain);
 
     to_sized_string(&pwfield, nctx->pwfield);
@@ -394,9 +391,8 @@ static int fill_pwent(struct sss_packet *packet,
         SAFEALIGN_SET_UINT32(&body[rp], gid, &rp);
 
         if (add_domain) {
-            ret = snprintf((char *)&body[rp],
-                            name.len + delim + dom_len,
-                            namefmt, name.str, domain);
+            ret = sss_fqname((char *) &body[rp], name.len + delim + dom_len,
+                             dom->names, dom, name.str);
             if (ret >= (name.len + delim + dom_len)) {
                 /* need more space, got creative with the print format ? */
                 t = ret - (name.len + delim + dom_len) + 1;
@@ -409,9 +405,8 @@ static int fill_pwent(struct sss_packet *packet,
                 sss_packet_get_body(packet, &body, &blen);
 
                 /* retry */
-                ret = snprintf((char *)&body[rp],
-                            name.len + delim + dom_len,
-                            namefmt, name.str, domain);
+                ret = sss_fqname((char *) &body[rp], name.len + delim + dom_len,
+                                 dom->names, dom, name.str);
             }
 
             if (ret != name.len + delim + dom_len - 1) {
@@ -665,7 +660,7 @@ static int delete_entry_from_memcache(struct sss_domain_info *dom, char *name,
     }
 
     if (dom->fqnames) {
-        fqdn = talloc_asprintf(tmp_ctx, dom->names->fq_fmt, name, dom->name);
+        fqdn = sss_tc_fqname(tmp_ctx, dom->names, dom, name);
         if (fqdn == NULL) {
             DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory.\n"));
             ret = ENOMEM;
@@ -2148,7 +2143,6 @@ static int fill_members(struct sss_packet *packet,
     size_t rsize = *_rsize;
     char *tmpstr;
     struct sized_string name;
-    const char *namefmt = dom->names->fq_fmt;
     TALLOC_CTX *tmp_ctx = NULL;
 
     size_t delim;
@@ -2209,9 +2203,9 @@ static int fill_members(struct sss_packet *packet,
         sss_packet_get_body(packet, &body, &blen);
 
         if (add_domain) {
-            ret = snprintf((char *)&body[rzero + rsize],
-                           name.len + delim + dom_len,
-                           namefmt, name.str, domain);
+            ret = sss_fqname((char *)&body[rzero + rsize],
+                             name.len + delim + dom_len,
+                             dom->names, dom, name.str);
             if (ret >= (name.len + delim + dom_len)) {
                 /* need more space,
                  * got creative with the print format ? */
@@ -2224,9 +2218,9 @@ static int fill_members(struct sss_packet *packet,
                 delim += t;
 
                 /* retry */
-                ret = snprintf((char *)&body[rzero + rsize],
-                               name.len + delim + dom_len,
-                               namefmt, name.str, domain);
+                ret = sss_fqname((char *)&body[rzero + rsize],
+                                name.len + delim + dom_len,
+                                dom->names, dom, name.str);
             }
 
             if (ret != name.len + delim + dom_len - 1) {
@@ -2289,11 +2283,8 @@ static int fill_grent(struct sss_packet *packet,
     size_t rzero, rsize;
     bool add_domain = (!IS_SUBDOMAIN(dom) && dom->fqnames);
     const char *domain = dom->name;
-    const char *namefmt;
     TALLOC_CTX *tmp_ctx = NULL;
 
-    namefmt = dom->names->fq_fmt;
-
     if (add_domain) {
         delim = 1;
         dom_len = strlen(domain);
@@ -2379,9 +2370,9 @@ static int fill_grent(struct sss_packet *packet,
 
         /*  8-X: sequence of strings (name, passwd, mem..) */
         if (add_domain) {
-            ret = snprintf((char *)&body[rzero+STRS_ROFFSET],
-                            name.len + delim + dom_len,
-                            namefmt, name.str, domain);
+            ret = sss_fqname((char *)&body[rzero+STRS_ROFFSET],
+                             name.len + delim + dom_len,
+                             dom->names, dom, name.str);
             if (ret >= (name.len + delim + dom_len)) {
                 /* need more space, got creative with the print format ? */
                 int t = ret - (name.len + delim + dom_len) + 1;
@@ -2395,9 +2386,9 @@ static int fill_grent(struct sss_packet *packet,
                 delim += t;
 
                 /* retry */
-                ret = snprintf((char *)&body[rzero+STRS_ROFFSET],
-                                name.len + delim + dom_len,
-                                namefmt, name.str, domain);
+                ret = sss_fqname((char *)&body[rzero+STRS_ROFFSET],
+                                 name.len + delim + dom_len,
+                                 dom->names, dom, name.str);
             }
 
             if (ret != name.len + delim + dom_len - 1) {
@@ -3670,8 +3661,7 @@ static errno_t nss_cmd_getsidby_search(struct nss_dom_ctx *dctx)
             /* For subdomains a fully qualified name is needed for
              * sysdb_search_user_by_name and sysdb_search_group_by_name. */
             if (IS_SUBDOMAIN(dom)) {
-                sysdb_name = talloc_asprintf(cmdctx, dom->names->fq_fmt,
-                                             name, dom->name);
+                sysdb_name = sss_tc_fqname(cmdctx, dom->names, dom, name);
                 if (sysdb_name == NULL) {
                     DEBUG(SSSDBG_OP_FAILURE, ("talloc_asprintf failed.\n"));
                     return ENOMEM;
@@ -4040,8 +4030,7 @@ static errno_t fill_name(struct sss_packet *packet,
     }
 
     if (add_domain) {
-        fq_name = talloc_asprintf(tmp_ctx, dom->names->fq_fmt, cased_name,
-                                  dom->name);
+        fq_name = sss_tc_fqname(tmp_ctx, dom->names, dom, cased_name);
         if (fq_name == NULL) {
             DEBUG(SSSDBG_OP_FAILURE, ("talloc_asprintf failed.\n"));
             ret = ENOMEM;
diff --git a/src/responder/pac/pacsrv_cmd.c b/src/responder/pac/pacsrv_cmd.c
index 2b6cd965ea06288cab2c6bc22d9d2c4f1774e075..8f5617c7f63871ee897db89f382c7d0133009d79 100644
--- a/src/responder/pac/pacsrv_cmd.c
+++ b/src/responder/pac/pacsrv_cmd.c
@@ -204,8 +204,8 @@ static errno_t pac_add_user_next(struct pac_req_ctx *pr_ctx)
 
     /* this is a subdomain so we need to search for the fully qualified
      * name in the database */
-    pr_ctx->fq_name = talloc_asprintf(pr_ctx, pr_ctx->dom->names->fq_fmt,
-                                      pr_ctx->user_name, pr_ctx->dom->name);
+    pr_ctx->fq_name= sss_tc_fqname(pr_ctx, pr_ctx->dom->names,
+                                   pr_ctx->dom, pr_ctx->user_name);
     if (!pr_ctx->fq_name) {
         ret = ENOMEM;
         DEBUG(SSSDBG_OP_FAILURE, ("talloc_sprintf failed.\n"));
diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c
index cb480d3ec081035166423d59e3835d41ff6070a9..bcea640b88d0aeea86f69a58be2bc34cd9a42f59 100644
--- a/src/responder/pac/pacsrv_utils.c
+++ b/src/responder/pac/pacsrv_utils.c
@@ -765,8 +765,7 @@ errno_t get_pwd_from_pac(TALLOC_CTX *mem_ctx,
         ret = ENOMEM;
         goto done;
     }
-    pwd->pw_name = talloc_asprintf(pwd, dom->names->fq_fmt,
-                                   lname, dom->name);
+    pwd->pw_name = sss_tc_fqname(pwd, dom->names, dom, lname);
     if (!pwd->pw_name) {
         DEBUG(SSSDBG_OP_FAILURE, ("talloc_sprintf failed.\n"));
         ret = ENOMEM;
diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c
index 9942e6a7f4a252f55bf2a01ec7c80b202db72f51..f888608a0aef5d19cf13501003991a02c12e05b0 100644
--- a/src/tools/sss_cache.c
+++ b/src/tools/sss_cache.c
@@ -221,8 +221,7 @@ static errno_t update_filter(struct cache_tool_ctx *tctx,
     if (parsed_domain) {
         if (IS_SUBDOMAIN(dinfo)) {
             /* Use fqdn for subdomains */
-            use_name = talloc_asprintf(tmp_ctx, tctx->nctx->fq_fmt, use_name,
-                                       dinfo->name);
+            use_name = sss_tc_fqname(tmp_ctx, tctx->nctx, dinfo, name);
             if (use_name == NULL) {
                 DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
                 ret = ENOMEM;
diff --git a/src/util/usertools.c b/src/util/usertools.c
index 91110f263657de9ba53ed305e7c4710eb006bec6..20ca407f333dc94335c5b2229085c2d52b82eb42 100644
--- a/src/util/usertools.c
+++ b/src/util/usertools.c
@@ -507,3 +507,28 @@ sss_get_cased_name_list(TALLOC_CTX *mem_ctx, const char * const *orig,
     *_cased = out;
     return EOK;
 }
+
+static inline const char *
+safe_fq_str(struct sss_names_ctx *nctx, uint8_t part, const char *str)
+{
+
+    return nctx->fq_flags & part ? str : "";
+}
+
+char *
+sss_tc_fqname(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
+              struct sss_domain_info *domain, const char *name)
+{
+    if (domain == NULL || nctx == NULL) return NULL;
+
+    return talloc_asprintf(mem_ctx, nctx->fq_fmt, name, domain->name);
+}
+
+int
+sss_fqname(char *str, size_t size, struct sss_names_ctx *nctx,
+           struct sss_domain_info *domain, const char *name)
+{
+    if (domain == NULL || nctx == NULL) return -EINVAL;
+
+    return snprintf(str, size, nctx->fq_fmt, name, domain->name);
+}
diff --git a/src/util/util.h b/src/util/util.h
index 49dc850c36cedd83755034367357fab41bd32ec6..8c46a643d38845f13002e75f6c9af9f179a8f708 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -43,6 +43,7 @@
 #include <ldb.h>
 #include <dhash.h>
 
+#include "confdb/confdb.h"
 #include "util/atomic_io.h"
 #include "util/util_errors.h"
 
@@ -425,6 +426,21 @@ errno_t
 sss_get_cased_name_list(TALLOC_CTX *mem_ctx, const char * const *orig,
                         bool case_sensitive, const char ***_cased);
 
+/* Return fully-qualified name according to the fq_fmt. The name is allocated using
+ * talloc on top of mem_ctx
+ */
+char *
+sss_tc_fqname(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
+              struct sss_domain_info *domain, const char *name);
+
+/* Return fully-qualified name formatted according to the fq_fmt. The buffer in "str" is
+ * "size" bytes long. Returns the number of bytes written on success or a negative
+ * value of failure.
+ */
+int
+sss_fqname(char *str, size_t size, struct sss_names_ctx *nctx,
+           struct sss_domain_info *domain, const char *name);
+
 /* from backup-file.c */
 int backup_file(const char *src, int dbglvl);
 
-- 
1.8.2.1

-------------- next part --------------
>From 62fa93fe1e439b5d9b530cce37c4a3d035bd8bca Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 9 May 2013 16:24:03 +0200
Subject: [PATCH 2/3] Check the validity of FQname format prior to using it

Adds a sanity check of the fqname pattern. Fails if the username pattern
is not specified at all and warns if the domain pattern is not
specified.
---
 src/util/usertools.c | 46 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 43 insertions(+), 3 deletions(-)

diff --git a/src/util/usertools.c b/src/util/usertools.c
index 20ca407f333dc94335c5b2229085c2d52b82eb42..835b9e3467c9345fc50cd96cbadd8c99b69d9e0a 100644
--- a/src/util/usertools.c
+++ b/src/util/usertools.c
@@ -135,6 +135,45 @@ done:
 #endif
 }
 
+static errno_t sss_fqnames_init(struct sss_names_ctx *nctx, const char *fq_fmt)
+{
+    struct pattern_desc {
+        const char *pattern;
+        const char *desc;
+    };
+
+    struct pattern_desc fqname_patterns[] = {
+        { "%1$s", "user name" },
+        { "%2$s", "domain name" },
+        { NULL, NULL }
+    };
+
+    nctx->fq_fmt = talloc_strdup(nctx, fq_fmt);
+    if (nctx->fq_fmt == NULL) {
+        return ENOMEM;
+    }
+
+    DEBUG(SSSDBG_CONF_SETTINGS, ("Using fq format [%s].\n", nctx->fq_fmt));
+
+    /* Fail if the name specifier is missing and warn if the domain
+     * specifier is missing
+     */
+    if (strstr(fq_fmt, fqname_patterns[0].pattern) == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Username pattern not found in [%s]\n", nctx->fq_fmt));
+        return ENOENT;
+    }
+
+    if (strstr(fq_fmt, fqname_patterns[1].pattern) == NULL) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("The pattern for %s was not found, fully-qualified names "
+               "might not work as expected\n", fqname_patterns[1].desc));
+        /* Ignore this error */
+    }
+
+    return EOK;
+}
+
 int sss_names_init_from_args(TALLOC_CTX *mem_ctx, const char *re_pattern,
                              const char *fq_fmt, struct sss_names_ctx **out)
 {
@@ -156,9 +195,10 @@ int sss_names_init_from_args(TALLOC_CTX *mem_ctx, const char *re_pattern,
 
     DEBUG(SSSDBG_CONF_SETTINGS, ("Using re [%s].\n", ctx->re_pattern));
 
-    ctx->fq_fmt = talloc_strdup(ctx, fq_fmt);
-    if (ctx->fq_fmt == NULL) {
-        ret = ENOMEM;
+    ret = sss_fqnames_init(ctx, fq_fmt);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Could not check the FQ names format"
+              "[%d]: %s\n", ret, sss_strerror(ret)));
         goto done;
     }
 
-- 
1.8.2.1

-------------- next part --------------
>From 7ee9eddb67626c016422ab0cc1bf5e3eb36535d0 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sat, 11 May 2013 10:53:12 +0200
Subject: [PATCH 3/3] Allow flat name in the FQname format

https://fedorahosted.org/sssd/ticket/1648

Adds another expansion in the printf format that allows the user to use
the domain flat name in the format.
---
 Makefile.am                     |  13 +-
 src/man/sssd.conf.5.xml         |  58 ++++++++-
 src/tests/cmocka/test_fqnames.c | 271 ++++++++++++++++++++++++++++++++++++++++
 src/util/usertools.c            |  56 +++++++--
 src/util/util.h                 |   5 +
 5 files changed, 386 insertions(+), 17 deletions(-)
 create mode 100644 src/tests/cmocka/test_fqnames.c

diff --git a/Makefile.am b/Makefile.am
index b72384a77fe5bb3d2d40229026c463fefabc1387..22984445d3f765ef6b29ee15e3620173c9fcf284 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -151,7 +151,8 @@ if HAVE_CMOCKA
         test-io \
         sss_nss_idmap-tests \
         test-io	      \
-        dyndns-tests
+        dyndns-tests \
+        fqnames-tests
 endif
 
 check_PROGRAMS = \
@@ -1303,6 +1304,16 @@ dyndns_tests_LDADD = \
     $(CARES_LIBS) \
     $(CMOCKA_LIBS) \
     libsss_util.la
+
+fqnames_tests_SOURCES = \
+    $(TEST_MOCK_OBJ) \
+    src/tests/cmocka/test_fqnames.c
+fqnames_tests_CFLAGS = \
+    $(AM_CFLAGS)
+fqnames_tests_LDADD = \
+    $(CMOCKA_LIBS) \
+    libsss_util.la
+
 endif
 
 noinst_PROGRAMS = pam_test_client
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 99337fbba9fb8d39a62eb84313c5b89761ee950d..85ed1fe55f2d14dddcf960795ab4469f82a9525b 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -143,12 +143,36 @@
                         <term>full_name_format (string)</term>
                         <listitem>
                             <para>
-                                The default <citerefentry>
+                                A <citerefentry>
                                     <refentrytitle>printf</refentrytitle>
                                     <manvolnum>3</manvolnum>
                                 </citerefentry>-compatible format that describes how to
-                                translate a (name, domain) tuple into a fully qualified
-                                name.
+                                compose a fully qualified name from user name
+                                and domain name components.
+                            </para>
+                            <para>
+                                The following expansions are supported :
+                                <variablelist>
+                                    <varlistentry>
+                                        <term>%1$s</term>
+                                        <listitem><para>user name</para></listitem>
+                                    </varlistentry>
+                                    <varlistentry>
+                                        <term>%2$s</term>
+                                        <listitem><para>domain name</para></listitem>
+                                    </varlistentry>
+                                    <varlistentry>
+                                        <term>%3$s</term>
+                                        <listitem>
+                                            <para>
+                                                domain flat name. Mostly usable
+                                                for Active Directory domains, both
+                                                directly configured or disovered
+                                                via IPA trusts.
+                                            </para>
+                                        </listitem>
+                                    </varlistentry>
+                                </variablelist>
                             </para>
                             <para>
                                 Each domain can have an individual format string configured.
@@ -1533,8 +1557,32 @@ override_homedir = /home/%u
                                 <refentrytitle>printf</refentrytitle>
                                 <manvolnum>3</manvolnum>
                             </citerefentry>-compatible format that describes how to
-                             translate a (name, domain) tuple for this domain into a fully
-                             qualified name.
+                            compose a fully qualified name from user name
+                            and domain name components.
+                        </para>
+                        <para>
+                            The following expansions are supported :
+                            <variablelist>
+                                <varlistentry>
+                                    <term>%1$s</term>
+                                    <listitem><para>user name</para></listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%2$s</term>
+                                    <listitem><para>domain name</para></listitem>
+                                </varlistentry>
+                                <varlistentry>
+                                    <term>%3$s</term>
+                                    <listitem>
+                                        <para>
+                                            domain flat name. Mostly usable
+                                            for Active Directory domains, both
+                                            directly configured or disovered
+                                            via IPA trusts.
+                                        </para>
+                                    </listitem>
+                                </varlistentry>
+                            </variablelist>
                         </para>
                         <para>
                             Default: <quote>%1$s@%2$s</quote>.
diff --git a/src/tests/cmocka/test_fqnames.c b/src/tests/cmocka/test_fqnames.c
new file mode 100644
index 0000000000000000000000000000000000000000..c0f36389e1b84748ac9ac6d6a6ca6cd24b20df0e
--- /dev/null
+++ b/src/tests/cmocka/test_fqnames.c
@@ -0,0 +1,271 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    SSSD tests: Fully Qualified Names Tests
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <popt.h>
+
+#include "tests/cmocka/common_mock.h"
+
+#define NAME        "name"
+#define DOMNAME     "domname"
+#define FLATNAME    "flatname"
+
+struct fqdn_test_ctx {
+    struct sss_domain_info *dom;
+
+    struct sss_names_ctx *nctx;
+};
+
+void fqdn_test_setup(void **state)
+{
+    struct fqdn_test_ctx *test_ctx;
+
+    assert_true(leak_check_setup());
+
+    test_ctx = talloc_zero(global_talloc_context, struct fqdn_test_ctx);
+    assert_non_null(test_ctx);
+
+    test_ctx->dom = talloc_zero(test_ctx, struct sss_domain_info);
+    assert_non_null(test_ctx->dom);
+    test_ctx->dom->name = discard_const(DOMNAME);
+    test_ctx->dom->flat_name = discard_const(FLATNAME);
+
+    check_leaks_push(test_ctx);
+    *state = test_ctx;
+}
+
+void fqdn_test_teardown(void **state)
+{
+    struct fqdn_test_ctx *test_ctx = talloc_get_type(*state,
+                                                     struct fqdn_test_ctx);
+
+    if (test_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Type mismatch\n"));
+        return;
+    }
+
+    assert_true(check_leaks_pop(test_ctx) == true);
+    talloc_free(test_ctx);
+    assert_true(leak_check_teardown());
+}
+
+void test_default(void **state)
+{
+    struct fqdn_test_ctx *test_ctx = talloc_get_type(*state,
+                                                     struct fqdn_test_ctx);
+    errno_t ret;
+
+    char *fqdn;
+    const int fqdn_size = 255;
+    char fqdn_s[fqdn_size];
+
+    if (test_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Type mismatch\n"));
+        return;
+    }
+
+    ret = sss_names_init_from_args(test_ctx,
+                                   "(?P<name>[^@]+)@?(?P<domain>[^@]*$)",
+                                   "%1$s@%2$s", &test_ctx->nctx);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(test_ctx->nctx->fq_flags, FQ_FMT_NAME | FQ_FMT_DOMAIN);
+
+    fqdn = sss_tc_fqname(test_ctx, test_ctx->nctx, test_ctx->dom, NAME);
+    assert_non_null(fqdn);
+    assert_string_equal(fqdn, NAME"@"DOMNAME);
+    talloc_free(fqdn);
+
+    ret = sss_fqname(fqdn_s, fqdn_size, test_ctx->nctx, test_ctx->dom, NAME);
+    assert_int_equal(ret + 1, sizeof(NAME"@"DOMNAME));
+    assert_string_equal(fqdn_s, NAME"@"DOMNAME);
+
+    talloc_free(test_ctx->nctx);
+}
+
+void test_all(void **state)
+{
+    struct fqdn_test_ctx *test_ctx = talloc_get_type(*state,
+                                                     struct fqdn_test_ctx);
+    errno_t ret;
+
+    char *fqdn;
+    const int fqdn_size = 255;
+    char fqdn_s[fqdn_size];
+
+    if (test_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Type mismatch\n"));
+        return;
+    }
+
+    ret = sss_names_init_from_args(test_ctx,
+                                   "(?P<name>[^@]+)@?(?P<domain>[^@]*$)",
+                                   "%1$s@%2$s@%3$s", &test_ctx->nctx);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(test_ctx->nctx->fq_flags,
+                     FQ_FMT_NAME | FQ_FMT_DOMAIN | FQ_FMT_FLAT_NAME);
+
+    fqdn = sss_tc_fqname(test_ctx, test_ctx->nctx, test_ctx->dom, NAME);
+    assert_non_null(fqdn);
+    assert_string_equal(fqdn, NAME"@"DOMNAME"@"FLATNAME);
+    talloc_free(fqdn);
+
+    ret = sss_fqname(fqdn_s, fqdn_size, test_ctx->nctx, test_ctx->dom, NAME);
+    assert_int_equal(ret + 1, sizeof(NAME"@"DOMNAME"@"FLATNAME));
+    assert_string_equal(fqdn_s, NAME"@"DOMNAME"@"FLATNAME);
+
+    talloc_free(test_ctx->nctx);
+}
+
+void test_flat(void **state)
+{
+    struct fqdn_test_ctx *test_ctx = talloc_get_type(*state,
+                                                     struct fqdn_test_ctx);
+    errno_t ret;
+
+    char *fqdn;
+    const int fqdn_size = 255;
+    char fqdn_s[fqdn_size];
+
+    if (test_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Type mismatch\n"));
+        return;
+    }
+
+    ret = sss_names_init_from_args(test_ctx,
+                                   "(?P<name>[^@]+)@?(?P<domain>[^@]*$)",
+                                   "%1$s@%3$s", &test_ctx->nctx);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(test_ctx->nctx->fq_flags, FQ_FMT_NAME | FQ_FMT_FLAT_NAME);
+
+    fqdn = sss_tc_fqname(test_ctx, test_ctx->nctx, test_ctx->dom, NAME);
+    assert_non_null(fqdn);
+    assert_string_equal(fqdn, NAME"@"FLATNAME);
+    talloc_free(fqdn);
+
+    ret = sss_fqname(fqdn_s, fqdn_size, test_ctx->nctx, test_ctx->dom, NAME);
+    assert_int_equal(ret + 1, sizeof(NAME"@"FLATNAME));
+    assert_string_equal(fqdn_s, NAME"@"FLATNAME);
+
+    talloc_free(test_ctx->nctx);
+}
+
+void test_flat_fallback(void **state)
+{
+    struct fqdn_test_ctx *test_ctx = talloc_get_type(*state,
+                                                     struct fqdn_test_ctx);
+    errno_t ret;
+
+    char *fqdn;
+    const int fqdn_size = 255;
+    char fqdn_s[fqdn_size];
+
+    if (test_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Type mismatch\n"));
+        return;
+    }
+
+    ret = sss_names_init_from_args(test_ctx,
+                                   "(?P<name>[^@]+)@?(?P<domain>[^@]*$)",
+                                   "%1$s@%3$s", &test_ctx->nctx);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(test_ctx->nctx->fq_flags, FQ_FMT_NAME | FQ_FMT_FLAT_NAME);
+
+    test_ctx->dom->flat_name = NULL;
+
+    /* If flat name is requested but does not exist, the code falls back to domain
+     * name
+     */
+    fqdn = sss_tc_fqname(test_ctx, test_ctx->nctx, test_ctx->dom, NAME);
+    assert_non_null(fqdn);
+    assert_string_equal(fqdn, NAME"@"DOMNAME);
+    talloc_free(fqdn);
+
+    ret = sss_fqname(fqdn_s, fqdn_size, test_ctx->nctx, test_ctx->dom, NAME);
+    assert_int_equal(ret + 1, sizeof(NAME"@"DOMNAME));
+    assert_string_equal(fqdn_s, NAME"@"DOMNAME);
+
+    talloc_free(test_ctx->nctx);
+}
+
+void test_init_nouser(void **state)
+{
+    struct fqdn_test_ctx *test_ctx = talloc_get_type(*state,
+                                                     struct fqdn_test_ctx);
+    errno_t ret;
+
+    if (test_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Type mismatch\n"));
+        return;
+    }
+
+    ret = sss_names_init_from_args(test_ctx,
+                                   "(?P<name>[^@]+)@?(?P<domain>[^@]*$)",
+                                   "%2$s@%3$s", &test_ctx->nctx);
+    /* Initialization with no user name must fail */
+    assert_int_not_equal(ret, EOK);
+}
+
+int main(int argc, const char *argv[])
+{
+    poptContext pc;
+    int opt;
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_DEBUG_OPTS
+        POPT_TABLEEND
+    };
+
+    const UnitTest tests[] = {
+        unit_test_setup_teardown(test_default,
+                                 fqdn_test_setup, fqdn_test_teardown),
+        unit_test_setup_teardown(test_all,
+                                 fqdn_test_setup, fqdn_test_teardown),
+        unit_test_setup_teardown(test_flat,
+                                 fqdn_test_setup, fqdn_test_teardown),
+        unit_test_setup_teardown(test_flat_fallback,
+                                 fqdn_test_setup, fqdn_test_teardown),
+        unit_test_setup_teardown(test_init_nouser,
+                                 fqdn_test_setup, fqdn_test_teardown),
+    };
+
+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                    poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+    poptFreeContext(pc);
+
+    DEBUG_INIT(debug_level);
+
+    /* Even though normally the tests should clean up after themselves
+     * they might not after a failed run. Remove the old db to be sure */
+    tests_set_cwd();
+
+    return run_tests(tests);
+}
diff --git a/src/util/usertools.c b/src/util/usertools.c
index 835b9e3467c9345fc50cd96cbadd8c99b69d9e0a..991975cbb0867a10c258447cfd073250c6a7b801 100644
--- a/src/util/usertools.c
+++ b/src/util/usertools.c
@@ -140,12 +140,14 @@ static errno_t sss_fqnames_init(struct sss_names_ctx *nctx, const char *fq_fmt)
     struct pattern_desc {
         const char *pattern;
         const char *desc;
+        int flag;
     };
 
     struct pattern_desc fqname_patterns[] = {
-        { "%1$s", "user name" },
-        { "%2$s", "domain name" },
-        { NULL, NULL }
+        { "%1$s", "user name", FQ_FMT_NAME },
+        { "%2$s", "domain name", FQ_FMT_DOMAIN },
+        { "%3$s", "domain flat name", FQ_FMT_FLAT_NAME },
+        { NULL, NULL, 0 }
     };
 
     nctx->fq_fmt = talloc_strdup(nctx, fq_fmt);
@@ -163,12 +165,24 @@ static errno_t sss_fqnames_init(struct sss_names_ctx *nctx, const char *fq_fmt)
               ("Username pattern not found in [%s]\n", nctx->fq_fmt));
         return ENOENT;
     }
+    nctx->fq_flags = FQ_FMT_NAME;
 
-    if (strstr(fq_fmt, fqname_patterns[1].pattern) == NULL) {
-        DEBUG(SSSDBG_MINOR_FAILURE,
-              ("The pattern for %s was not found, fully-qualified names "
-               "might not work as expected\n", fqname_patterns[1].desc));
-        /* Ignore this error */
+    for (int i = 1; fqname_patterns[i].pattern; i++) {
+        char *s;
+        s = strstr(fq_fmt, fqname_patterns[i].pattern);
+        if (s == NULL) {
+            /* Append the format specifier */
+            nctx->fq_fmt = talloc_strdup_append(nctx->fq_fmt,
+                                                fqname_patterns[i].pattern);
+            if (nctx->fq_fmt == NULL) {
+                return ENOMEM;
+            }
+            continue;
+        }
+
+        DEBUG(SSSDBG_CONF_SETTINGS,
+              ("Found the pattern for %s\n", fqname_patterns[i].desc));
+        nctx->fq_flags |= fqname_patterns[i].flag;
     }
 
     return EOK;
@@ -551,17 +565,34 @@ sss_get_cased_name_list(TALLOC_CTX *mem_ctx, const char * const *orig,
 static inline const char *
 safe_fq_str(struct sss_names_ctx *nctx, uint8_t part, const char *str)
 {
-
     return nctx->fq_flags & part ? str : "";
 }
 
+static inline const char *
+safe_flat_name(struct sss_names_ctx *nctx, struct sss_domain_info *domain)
+{
+    const char *s;
+
+    s = safe_fq_str(nctx, FQ_FMT_FLAT_NAME, domain->flat_name);
+    if (s == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Flat name requested but domain has no"
+              "flat name set, falling back to domain name\n"));
+        s = domain->name;
+    }
+
+    return s;
+}
+
 char *
 sss_tc_fqname(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
               struct sss_domain_info *domain, const char *name)
 {
     if (domain == NULL || nctx == NULL) return NULL;
 
-    return talloc_asprintf(mem_ctx, nctx->fq_fmt, name, domain->name);
+    return talloc_asprintf(mem_ctx, nctx->fq_fmt,
+                           safe_fq_str(nctx, FQ_FMT_NAME, name),
+                           safe_fq_str(nctx, FQ_FMT_DOMAIN, domain->name),
+                           safe_flat_name(nctx, domain));
 }
 
 int
@@ -570,5 +601,8 @@ sss_fqname(char *str, size_t size, struct sss_names_ctx *nctx,
 {
     if (domain == NULL || nctx == NULL) return -EINVAL;
 
-    return snprintf(str, size, nctx->fq_fmt, name, domain->name);
+    return snprintf(str, size, nctx->fq_fmt,
+                    safe_fq_str(nctx, FQ_FMT_NAME, name),
+                    safe_fq_str(nctx, FQ_FMT_DOMAIN, domain->name),
+                    safe_flat_name(nctx, domain));
 }
diff --git a/src/util/util.h b/src/util/util.h
index 8c46a643d38845f13002e75f6c9af9f179a8f708..bc81ffb6619699bfbba9370bb16d585c9b1c80c4 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -395,9 +395,14 @@ char *get_username_from_uid(TALLOC_CTX *mem_ctx, uid_t uid);
 
 char *get_uppercase_realm(TALLOC_CTX *memctx, const char *name);
 
+#define FQ_FMT_NAME         0x01
+#define FQ_FMT_DOMAIN       0x02
+#define FQ_FMT_FLAT_NAME    0x04
+
 struct sss_names_ctx {
     char *re_pattern;
     char *fq_fmt;
+    uint8_t fq_flags;
 
     pcre *re;
 };
-- 
1.8.2.1



More information about the sssd-devel mailing list