On 07.01.2014 14:57, Stef Walter wrote:
> On 07.01.2014 14:07, Stef Walter wrote:
>> Anyhow, here's a patch which aims to make the full_name_format printf
>> handling both correct and safe.
> 
> ...
> 
>> I'll be happy to split the patch into two, if desired. One which adds
>> safe-printf.[ch] + tests, and the second which fixes the fully qualified
>> names.
> 
> Now split for easier review.

Stephen suggested I put the safe-printf part of this into its own
library. Not my first choice, but here we go. The first patch is for
ding-libs, and the second for sssd.

Cheers,

Stef

>From 1907012f700267f5f0c70b2f82684cb02e3dabf8 Mon Sep 17 00:00:00 2001
From: Stef Walter <st...@redhat.com>
Date: Tue, 7 Jan 2014 15:51:35 +0100
Subject: [PATCH] string: Add ding-string library with safe-printf code

As suggested by Stephen Gallagher, put the safe-printf.c code
into its own library here in the ding project.
---
 Makefile.am               |  18 +++
 configure.ac              |   4 +-
 contrib/ding-libs.spec.in |  46 +++++-
 string/ding-string.pc.in  |  11 ++
 string/safe-printf.c      | 348 ++++++++++++++++++++++++++++++++++++++++++++++
 string/safe-printf.h      |  45 ++++++
 string/safe-printf_ut.c   | 220 +++++++++++++++++++++++++++++
 version.m4                |   1 +
 8 files changed, 690 insertions(+), 3 deletions(-)
 create mode 100644 string/ding-string.pc.in
 create mode 100644 string/safe-printf.c
 create mode 100644 string/safe-printf.h
 create mode 100644 string/safe-printf_ut.c

diff --git a/Makefile.am b/Makefile.am
index 74aff05..e75ef92 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -299,6 +299,24 @@ clean-local-ini_config:
 	rm -f ./foo.conf #From ini_parse_ut
 
 ##############################################################################
+# ding-string
+##############################################################################
+
+lib_LTLIBRARIES += libding-string.la
+dist_pkgconfig_DATA += string/ding-string.pc
+dist_include_HEADERS += string/safe-printf.h
+
+libding_string_la_SOURCES = \
+    string/safe-printf.c
+libding_string_la_LDFLAGS = \
+    -version-info 0:0:0
+
+check_PROGRAMS += safe-printf_ut
+TESTS += safe-printf_ut
+safe_printf_ut_SOURCES = string/safe-printf_ut.c
+safe_printf_ut_LDADD = libding-string.la
+
+##############################################################################
 # Additional rules
 ##############################################################################
 
diff --git a/configure.ac b/configure.ac
index 021cb34..7f41809 100644
--- a/configure.ac
+++ b/configure.ac
@@ -35,6 +35,7 @@ AC_SUBST([COLLECTION_VERSION], COLLECTION_VERSION_NUMBER)
 AC_SUBST([REF_ARRAY_VERSION], REF_ARRAY_VERSION_NUMBER)
 AC_SUBST([BASICOBJECTS_VERSION], BASICOBJECTS_VERSION_NUMBER)
 AC_SUBST([INI_CONFIG_VERSION], INI_CONFIG_VERSION_NUMBER)
+AC_SUBST([DING_STRING_VERSION_NUMBER], DING_STRING_VERSION_NUMBER)
 
 AM_CONDITIONAL([GIT_CHECKOUT], [git log -1 &>/dev/null])
 
@@ -80,5 +81,6 @@ AC_CONFIG_FILES([Makefile contrib/ding-libs.spec
                  refarray/ref_array.cfg.doxy refarray/ref_array.pc
                  basicobjects/basicobjects.cfg.doxy
                  basicobjects/basicobjects.pc
-                 ini/ini_config.cfg.doxy ini/ini_config.pc])
+                 ini/ini_config.cfg.doxy ini/ini_config.pc
+                 string/ding-string.pc])
 AC_OUTPUT
diff --git a/contrib/ding-libs.spec.in b/contrib/ding-libs.spec.in
index 14ff1f2..8155656 100644
--- a/contrib/ding-libs.spec.in
+++ b/contrib/ding-libs.spec.in
@@ -14,6 +14,7 @@ BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
 %global ref_array_version @REF_ARRAY_VERSION@
 %global basicobjects_version @BASICOBJECTS_VERSION@
 %global ini_config_version @INI_CONFIG_VERSION@
+%global ding_string_version @DING_STRING_VERSION@
 
 ### Patches ###
 
@@ -25,6 +26,7 @@ Requires: libdhash = %{dhash_version}-%{release}
 Requires: libcollection = %{collection_version}-%{release}
 Requires: libref_array = %{ref_array_version}-%{release}
 Requires: libini_config = %{ini_config_version}-%{release}
+Requires: ding-string = %{ding_string_version}-%{release}
 
 ### Build Dependencies ###
 
@@ -36,7 +38,7 @@ BuildRequires: doxygen
 
 %description
 A meta-package that pulls in libcollection, libdhash, libini_config,
-librefarray and libpath_utils.
+librefarray ding-string and libpath_utils.
 
 %package devel
 Summary: Development packages for ding-libs
@@ -50,10 +52,11 @@ Requires: libdhash-devel = %{dhash_version}-%{release}
 Requires: libcollection-devel = %{collection_version}-%{release}
 Requires: libref_array-devel = %{ref_array_version}-%{release}
 Requires: libini_config-devel = %{ini_config_version}-%{release}
+Requires: ding-string-devel = %{ding_string_version}-%{release}
 
 %description devel
 A meta-package that pulls in development libraries for libcollection,
-libdhash, libini_config, librefarray and libpath_utils.
+libdhash, libini_config, librefarray ding-string and libpath_utils.
 
 ##############################################################################
 # Path Utils
@@ -315,6 +318,45 @@ structure
 
 
 ##############################################################################
+# ding-string
+##############################################################################
+
+%package -n ding-string
+Summary: String utility functions
+Group: Development/Libraries
+License: LGPLv2+
+Version: %{ding_string_version}
+
+%description -n ding-string
+String utility functions
+
+%package -n ding-string-devel
+Summary: Development files for ding-string
+Group: Development/Libraries
+License: LGPLv2+
+Version: %{ding_string_version}
+Requires: ding-string = %{ding_string_version}-%{release}
+
+%description -n ding-string-devel
+String utility functions
+
+%post -n ding-string -p /sbin/ldconfig
+%postun -n ding-string -p /sbin/ldconfig
+
+%files -n ding-string
+%defattr(-,root,root,-)
+%doc COPYING.LESSER.V2
+%{_libdir}/libding-string.so.0
+%{_libdir}/libding-string.so.0.0.0
+
+%files -n libding-string-devel
+%defattr(-,root,root,-)
+%{_includedir}/safe-printf.h
+%{_libdir}/libding-string.so
+%{_libdir}/pkgconfig/ding-string.pc
+
+
+##############################################################################
 # Build steps
 ##############################################################################
 
diff --git a/string/ding-string.pc.in b/string/ding-string.pc.in
new file mode 100644
index 0000000..5eed498
--- /dev/null
+++ b/string/ding-string.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: ding-string
+Description: Library contains string utilities
+Version: @BASICOBJECTS_VERSION@
+Libs: -L${libdir} -lding-string
+Cflags: -I${includedir}
+URL: http://fedorahosted.org/sssd/
diff --git a/string/safe-printf.c b/string/safe-printf.c
new file mode 100644
index 0000000..4f6ff4c
--- /dev/null
+++ b/string/safe-printf.c
@@ -0,0 +1,348 @@
+/* realmd -- Realm configuration service
+ *
+ * Copyright 2013 Red Hat Inc
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Stef Walter <st...@redhat.com>
+ */
+
+#include "config.h"
+
+#include "safe-printf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+#ifndef MIN
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
+#endif
+
+static void
+safe_padding (int count,
+              int *total,
+              void (* callback) (void *, const char *, size_t),
+              void *data)
+{
+    char eight[] = "        ";
+    int num;
+
+    while (count > 0) {
+        num = MIN (count, 8);
+        callback (data, eight, num);
+        count -= num;
+        *total += num;
+    }
+}
+
+static void
+dummy_callback (void *data,
+                const char *piece,
+                size_t len)
+{
+
+}
+
+int
+safe_printf_cb (void (* callback) (void *, const char *, size_t),
+                void *data,
+                const char *format,
+                const char * const args[],
+                int num_args)
+{
+    int at_arg = 0;
+    const char *cp;
+    int precision;
+    int width;
+    int len;
+    const char *value;
+    int total;
+    int left;
+
+    if (!callback)
+        callback = dummy_callback;
+
+    total = 0;
+    cp = format;
+
+    while (*cp) {
+
+        /* Piece of raw string */
+        if (*cp != '%') {
+            len = strcspn (cp, "%");
+            callback (data, cp, len);
+            total += len;
+            cp += len;
+            continue;
+        }
+
+        cp++;
+
+        /* An literal percent sign? */
+        if (*cp == '%') {
+            callback (data, "%", 1);
+            total++;
+            cp++;
+            continue;
+        }
+
+        value = NULL;
+        left = 0;
+        precision = -1;
+        width = -1;
+
+        /* Test for positional argument.  */
+        if (*cp >= '0' && *cp <= '9') {
+            /* Look-ahead parsing, otherwise skipped */
+            if (cp[strspn (cp, "0123456789")] == '$') {
+                unsigned int n = 0;
+                for (; *cp >= '0' && *cp <= '9'; cp++)
+                    n = 10 * n + (*cp - '0');
+                /* Positional argument 0 is invalid. */
+                if (n == 0)
+                    return -1;
+                /* Positional argument N too high */
+                if (n > num_args)
+                    return -1;
+                value = args[n - 1];
+                cp++; /* $ */
+            }
+        }
+
+        /* Read the supported flags. */
+        for (; ; cp++) {
+            if (*cp == '-')
+                left = 1;
+            /* Supported but ignored */
+            else if (*cp != ' ')
+                break;
+        }
+
+        /* Parse the width. */
+        if (*cp >= '0' && *cp <= '9') {
+            width = 0;
+            for (; *cp >= '0' && *cp <= '9'; cp++)
+                width = 10 * width + (*cp - '0');
+        }
+
+        /* Parse the precision. */
+        if (*cp == '.') {
+            precision = 0;
+            for (cp++; *cp >= '0' && *cp <= '9'; cp++)
+                precision = 10 * precision + (*cp - '0');
+        }
+
+        /* Read the conversion character.  */
+        switch (*cp++) {
+        case 's':
+            /* Non-positional argument */
+            if (value == NULL) {
+                /* Too many arguments used */
+                if (at_arg == num_args)
+                    return -1;
+                value = args[at_arg++];
+            }
+            break;
+
+        /* No other conversion characters are supported */
+        default:
+            return -1;
+        }
+
+        /* How many characters are we printing? */
+        len = strlen (value);
+        if (precision >= 0)
+            len = MIN (precision, len);
+
+        /* Do we need padding? */
+        safe_padding (left ? 0 : width - len, &total, callback, data);
+
+        /* The actual data */;
+        callback (data, value, len);
+        total += len;
+
+        /* Do we need padding? */
+        safe_padding (left ? width - len : 0, &total, callback, data);
+    }
+
+    return total;
+}
+
+struct sprintf_ctx {
+    char *data;
+    size_t length;
+    size_t alloc;
+};
+
+static void
+asprintf_callback (void *data,
+                   const char *piece,
+                   size_t length)
+{
+    struct sprintf_ctx *cx = data;
+    void *mem;
+
+    if (!cx->data)
+        return;
+
+    /* Reallocate if necessary */
+    if (cx->length + length + 1 > cx->alloc) {
+        cx->alloc += MAX (length + 1, 1024);
+        mem = realloc (cx->data, cx->alloc);
+        if (mem == NULL) {
+            free (cx->data);
+            cx->data = NULL;
+            return;
+        }
+        cx->data = mem;
+    }
+
+    memcpy (cx->data + cx->length, piece, length);
+    cx->length += length;
+    cx->data[cx->length] = 0;
+}
+
+static const char **
+valist_to_args (va_list va,
+                int *num_args)
+{
+    int alo_args;
+    const char **args;
+    const char *arg;
+    void *mem;
+
+    *num_args = alo_args = 0;
+    args = NULL;
+
+    for (;;) {
+        arg = va_arg (va, const char *);
+        if (arg == NULL)
+            break;
+        if (*num_args == alo_args) {
+            alo_args += 8;
+            mem = realloc (args, sizeof (const char *) * alo_args);
+            if (!mem) {
+                free (args);
+                return NULL;
+            }
+            args = mem;
+        }
+        args[(*num_args)++] = arg;
+    }
+
+    return args;
+}
+
+int
+safe_asprintf (char **strp,
+               const char *format,
+               ...)
+{
+    struct sprintf_ctx cx;
+    const char **args;
+    int num_args;
+    va_list va;
+    int ret;
+    int i;
+
+    va_start (va, format);
+    args = valist_to_args (va, &num_args);
+    va_end (va);
+
+    if (args == NULL)
+        return -1;
+
+    /* Preallocate a pretty good guess */
+    cx.alloc = strlen (format) + 1;
+    for (i = 0; i < num_args; i++)
+        cx.alloc += strlen (args[i]);
+
+    cx.data = malloc (cx.alloc);
+    if (!cx.data) {
+        free (args);
+        return -1;
+    }
+
+    cx.data[0] = '\0';
+    cx.length = 0;
+
+    ret = safe_printf_cb (asprintf_callback, &cx, format, args, num_args);
+    if (cx.data == NULL)
+        ret = -1;
+    if (ret < 0)
+        free (cx.data);
+    else
+        *strp = cx.data;
+
+    free (args);
+    return ret;
+}
+
+static void
+snprintf_callback (void *data,
+                   const char *piece,
+                   size_t length)
+{
+    struct sprintf_ctx *cx = data;
+
+    /* Don't copy if too much data */
+    if (cx->length > cx->alloc)
+        length = 0;
+    else if (cx->length + length > cx->alloc)
+        length = cx->alloc - cx->length;
+    else
+        length = length;
+
+    if (length > 0)
+        memcpy (cx->data + cx->length, piece, length);
+
+    /* Null termination happens later */
+    cx->length += length;
+}
+
+int
+safe_snprintf (char *str,
+               size_t len,
+               const char *format,
+               ...)
+{
+    struct sprintf_ctx cx;
+    int num_args;
+    va_list va;
+    const char **args;
+    int ret;
+
+    cx.data = str;
+    cx.length = 0;
+    cx.alloc = len;
+
+    va_start (va, format);
+    args = valist_to_args (va, &num_args);
+    va_end (va);
+
+    if (args == NULL)
+        return -1;
+
+    if (len)
+        cx.data[0] = '\0';
+
+    ret = safe_printf_cb (snprintf_callback, &cx, format, args, num_args);
+    if (ret < 0)
+        return ret;
+
+    /* Null terminate appropriately */
+    if (len > 0)
+        cx.data[MIN(cx.length, len - 1)] = '\0';
+
+    free (args);
+    return ret;
+}
diff --git a/string/safe-printf.h b/string/safe-printf.h
new file mode 100644
index 0000000..8881b91
--- /dev/null
+++ b/string/safe-printf.h
@@ -0,0 +1,45 @@
+/* realmd -- Realm configuration service
+ *
+ * Copyright 2013 Red Hat Inc
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Stef Walter <st...@redhat.com>
+ */
+
+#include "config.h"
+
+#ifndef __SAFE_PRINTF_H__
+#define __SAFE_PRINTF_H__
+
+#include <stdlib.h>
+
+#ifndef GNUC_NULL_TERMINATED
+#if __GNUC__ >= 4
+#define GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+#else
+#define GNUC_NULL_TERMINATED
+#endif
+#endif
+
+int        safe_asprintf     (char **strp,
+                              const char *format,
+                              ...) GNUC_NULL_TERMINATED;
+
+int        safe_snprintf     (char *str,
+                              size_t len,
+                              const char *format,
+                              ...) GNUC_NULL_TERMINATED;
+
+int        safe_printf_cb    (void (* callback) (void *data, const char *piece, size_t len),
+                              void *data,
+                              const char *format,
+                              const char * const args[],
+                              int num_args);
+
+#endif /* __SAFE_PRINTF_H__ */
diff --git a/string/safe-printf_ut.c b/string/safe-printf_ut.c
new file mode 100644
index 0000000..38be9fd
--- /dev/null
+++ b/string/safe-printf_ut.c
@@ -0,0 +1,220 @@
+/*
+ * This file originated in realmd
+ *
+ * Copyright 2012 Red Hat Inc
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Stef Walter <st...@redhat.com>
+ */
+
+#include "config.h"
+
+#include "safe-printf.h"
+
+#include <assert.h>
+#include <string.h>
+
+typedef struct {
+    const char *format;
+    const char *args[8];
+    const char *result;
+} Fixture;
+
+static const Fixture fixtures[] = {
+    {
+      /* Just a bog standard string */
+      "%s", { "blah", NULL, },
+      "blah"
+    },
+    {
+      /* Empty to print */
+      "%s", { "", NULL, },
+      ""
+    },
+    {
+      /* Nothing to print */
+      "", { "blah", NULL, },
+      ""
+    },
+    {
+      /* Width right aligned */
+      "%8s", { "blah", NULL, },
+      "    blah"
+    },
+    {
+      /* Width left aligned */
+      "whoop %-8s doo", { "dee", NULL, },
+      "whoop dee      doo"
+    },
+    {
+      /* Width space aligned (ignored) */
+      "whoop % 8s doo", { "dee", NULL, },
+      "whoop      dee doo"
+    },
+    {
+      /* Width left space aligned (ignored) */
+      "whoop % -8s doo", { "dee", NULL, },
+      "whoop dee      doo"
+    },
+    {
+      /* Precision 1 digit */
+      "whoop %.3s doo", { "deedle-dee", NULL, },
+      "whoop dee doo"
+    },
+    {
+      /* Precision, N digits */
+      "whoop %.10s doo", { "deedle-dee-deedle-do-deedle-dum", NULL, },
+      "whoop deedle-dee doo"
+    },
+    {
+      /* Precision, zero digits */
+      "whoop %.s doo", { "deedle", NULL, },
+      "whoop  doo"
+    },
+    {
+      /* Multiple simple arguments */
+      "space %s %s", { "man", "dances", NULL, },
+      "space man dances"
+    },
+    {
+      /* Literal percent */
+      "100%% of space folk dance", { NULL, },
+      "100% of space folk dance"
+    },
+    {
+      /* Multiple simple arguments */
+      "space %2$s %1$s", { "dances", "man", NULL, },
+      "space man dances"
+    },
+    {
+      /* Skipping an argument (not supported by standard printf) */
+      "space %2$s dances", { "dances", "man", NULL, },
+      "space man dances"
+    },
+
+    /* Failures start here */
+
+    {
+      /* Unsupported conversion */
+      "%x", { "blah", NULL, },
+      NULL
+    },
+    {
+      /* Bad positional argument */
+      "space %55$s dances", { "dances", "man", NULL, },
+      NULL
+    },
+    {
+      /* Zero positional argument */
+      "space %0$s dances", { "dances", "man", NULL, },
+      NULL
+    },
+    {
+      /* Too many args used */
+      "%s %s dances", { "space", NULL, },
+      NULL
+    },
+};
+
+
+static void
+callback (void *data, const char *piece, size_t len)
+{
+    assert (strlen (data) + len < 1024);
+    strncat (data, piece, len);
+}
+
+static void
+test_safe_printf(const Fixture *fixture)
+{
+    char buffer[1024] = { 0, };
+    char *out;
+    int num_args;
+    int ret;
+
+    for (num_args = 0; fixture->args[num_args] != NULL; )
+        num_args++;
+
+    ret = safe_printf_cb(callback, buffer, fixture->format, (const char * const*)fixture->args, num_args);
+    if (fixture->result) {
+        assert(ret >= 0);
+        assert(strcmp (buffer, fixture->result) == 0);
+        assert(ret == strlen(buffer));
+    } else {
+        assert(ret < 0);
+    }
+}
+
+static void
+test_safe_snprintf(void)
+{
+    char buffer[8];
+    int ret;
+
+    ret = safe_snprintf(buffer, 8, "%s", "space", "man", NULL);
+    assert(ret == 5);
+    assert(strcmp(buffer, "space") == 0);
+
+    ret = safe_snprintf(buffer, 8, "", "space", "man", NULL);
+    assert(ret == 0);
+    assert(strcmp(buffer, "") == 0);
+
+    ret = safe_snprintf(buffer, 8, "the %s %s dances away", "space", "man", NULL);
+    assert(ret == 25);
+    assert(strcmp(buffer, "the spa") == 0);
+
+    ret = safe_snprintf(NULL, 0, "the %s %s dances away", "space", "man", NULL);
+    assert(ret == 25);
+
+    ret = safe_snprintf(buffer, 8, "%5$s", NULL);
+    assert(ret < 0);
+}
+
+static void
+test_safe_asprintf(void)
+{
+    char *buffer;
+    int ret;
+
+    ret = safe_asprintf(&buffer, "%s", "space", "man", NULL);
+    assert(ret == 5);
+    assert(strcmp(buffer, "space") == 0);
+    free (buffer);
+
+    ret = safe_asprintf(&buffer, "", "space", "man", NULL);
+    assert(ret == 0);
+    assert(strcmp(buffer, "") == 0);
+    free (buffer);
+
+    ret = safe_asprintf(&buffer, "the %s %s dances away", "space", "man", NULL);
+    assert(ret == 25);
+    assert(strcmp(buffer, "the space man dances away") == 0);
+    free (buffer);
+
+    ret = safe_asprintf(&buffer, "%1$s %1$s %1$s %1$s %1$s %1$s", "space man", NULL);
+    assert(ret == 59);
+    assert(strcmp(buffer, "space man space man space man space man space man space man") == 0);
+    free (buffer);
+
+    ret = safe_asprintf(&buffer, "%5$s", NULL);
+    assert(ret < 0);
+}
+
+int
+main(void)
+{
+    int i;
+
+    for (i = 0; i < (sizeof (fixtures) / sizeof (fixtures[0])); i++)
+        test_safe_printf (fixtures + i);
+    test_safe_snprintf ();
+    test_safe_asprintf ();
+
+    return 0;
+}
diff --git a/version.m4 b/version.m4
index 76b8a0d..6625f16 100644
--- a/version.m4
+++ b/version.m4
@@ -16,3 +16,4 @@ m4_define([COLLECTION_VERSION_NUMBER], [0.6.2])
 m4_define([REF_ARRAY_VERSION_NUMBER], [0.1.3])
 m4_define([BASICOBJECTS_VERSION_NUMBER], [0.1.0])
 m4_define([INI_CONFIG_VERSION_NUMBER], [1.0.0.1])
+m4_define([DING_STRING_VERSION_NUMBER], [0.1])
-- 
1.8.4.2

>From 45babccd4494ac1b1b88f183bf4139c158f35fce Mon Sep 17 00:00:00 2001
From: Stef Walter <st...@redhat.com>
Date: Tue, 7 Jan 2014 14:49:01 +0100
Subject: [PATCH] NSS: Don't use printf(3) on user provided strings.

This also fixes several corner cases and crashers.

It's not prudent to pass user input to (even admin) input as a
format string to printf, and various distros now check for this.
This can cause accessing memory incorrectly, and various also
various libc abort()'s.

In addition various assumptions were made about full_name_format
that aren't necessarily the case if the user uses a more complex
format.

Use ding-string safe-printf.c implementation for formatting
full_name_format.

Adapt the NSS resolver so it doesn't barf on formatted strings that
are shorter than expected given a full_name_format.

Tests added and updated appropriately.
---
 Makefile.am                     |   3 +-
 configure.ac                    |   1 +
 contrib/sssd.spec.in            |   1 +
 src/external/ding-string.m4     |   5 ++
 src/responder/nss/nsssrv_cmd.c  | 132 ++++++++++++++--------------------------
 src/tests/cmocka/test_fqnames.c |  21 -------
 src/tests/cmocka/test_nss_srv.c |  21 +++----
 src/tests/cmocka/test_utils.c   |   6 +-
 src/util/usertools.c            | 113 +++++++++++-----------------------
 src/util/util.h                 |  12 +---
 10 files changed, 105 insertions(+), 210 deletions(-)
 create mode 100644 src/external/ding-string.m4

diff --git a/Makefile.am b/Makefile.am
index 4015dad..f7e04ec 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -595,7 +595,8 @@ libsss_util_la_SOURCES = \
     src/util/well_known_sids.c
 libsss_util_la_LIBADD = \
     $(SSSD_LIBS) \
-    $(UNICODE_LIBS)
+    $(UNICODE_LIBS) \
+    $(DING_STRING_LIBS)
 if BUILD_SUDO
     libsss_util_la_SOURCES += src/db/sysdb_sudo.c
 endif
diff --git a/configure.ac b/configure.ac
index 9303820..ca1978c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -137,6 +137,7 @@ m4_include([src/external/libldb.m4])
 m4_include([src/external/libdhash.m4])
 m4_include([src/external/libcollection.m4])
 m4_include([src/external/libini_config.m4])
+m4_include([src/external/ding-string.m4])
 m4_include([src/external/pam.m4])
 m4_include([src/external/ldap.m4])
 m4_include([src/external/libpcre.m4])
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 6bc2aa2..f04c110 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -97,6 +97,7 @@ BuildRequires: libldb-devel
 BuildRequires: libdhash-devel >= 0.4.2
 BuildRequires: libcollection-devel
 BuildRequires: libini_config-devel
+BuildRequires: libding-string-devel
 BuildRequires: dbus-devel
 BuildRequires: dbus-libs
 %if 0%{?rhel5_minor} >= 7
diff --git a/src/external/ding-string.m4 b/src/external/ding-string.m4
new file mode 100644
index 0000000..0559c57
--- /dev/null
+++ b/src/external/ding-string.m4
@@ -0,0 +1,5 @@
+PKG_CHECK_MODULES(DING_STRING, [ding-string >= 0.1.0])
+
+AC_SUBST(DING_STRING_OBJ)
+AC_SUBST(DING_STRING_CFLAGS)
+AC_SUBST(DING_STRING_LIBS)
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 715313f..137f14c 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -298,20 +298,14 @@ static int fill_pwent(struct sss_packet *packet,
     uint32_t uid;
     uint32_t gid;
     size_t rsize, rp, blen;
-    size_t dom_len = 0;
-    int delim = 0;
-    int i, ret, num, t;
+    size_t fq_len = 0;
+    int i, ret, num;
     bool add_domain = (!IS_SUBDOMAIN(dom) && dom->fqnames);
     const char *domain = dom->name;
     bool packet_initialized = false;
     int ncret;
     TALLOC_CTX *tmp_ctx = NULL;
 
-    if (add_domain) {
-        delim = 1;
-        dom_len = sss_fqdom_len(dom->names, dom);
-    }
-
     to_sized_string(&pwfield, nctx->pwfield);
 
     rp = 2*sizeof(uint32_t);
@@ -381,7 +375,17 @@ static int fill_pwent(struct sss_packet *packet,
 
         rsize = 2 * sizeof(uint32_t) + name.len + gecos.len +
                                        homedir.len + shell.len + pwfield.len;
-        if (add_domain) rsize += delim + dom_len;
+
+        if (add_domain) {
+            fq_len = sss_fqname(NULL, 0, dom->names, dom, name.str);
+            if (fq_len >= 0) {
+                fq_len += 1;
+                rsize -= name.len;
+                rsize += fq_len;
+            } else {
+                fq_len = 0;
+            }
+        }
 
         ret = sss_packet_grow(packet, rsize);
         if (ret != EOK) {
@@ -394,25 +398,8 @@ static int fill_pwent(struct sss_packet *packet,
         SAFEALIGN_SET_UINT32(&body[rp], gid, &rp);
 
         if (add_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;
-                ret = sss_packet_grow(packet, t);
-                if (ret != EOK) {
-                    num = 0;
-                    goto done;
-                }
-                delim += t;
-                sss_packet_get_body(packet, &body, &blen);
-
-                /* retry */
-                ret = sss_fqname((char *) &body[rp], name.len + delim + dom_len,
-                                 dom->names, dom, name.str);
-            }
-
-            if (ret != name.len + delim + dom_len - 1) {
+            ret = sss_fqname((char *) &body[rp], fq_len, dom->names, dom, name.str);
+            if (ret < 0 || ret != fq_len - 1) {
                 DEBUG(1, ("Failed to generate a fully qualified name for user "
                           "[%s] in [%s]! Skipping user.\n", name.str, domain));
                 continue;
@@ -2281,8 +2268,7 @@ static int fill_members(struct sss_packet *packet,
     struct sized_string name;
     TALLOC_CTX *tmp_ctx = NULL;
 
-    size_t delim = 0;
-    size_t dom_len = 0;
+    size_t nlen = 0;
 
     uint8_t *body;
     size_t blen;
@@ -2319,9 +2305,6 @@ static int fill_members(struct sss_packet *packet,
             }
         }
 
-        delim = 0;
-        dom_len = 0;
-
         ret = parse_member(tmp_ctx, dom, tmpstr, &member_dom, &name, &add_domain);
         if (ret != EOK) {
             DEBUG(SSSDBG_MINOR_FAILURE,
@@ -2330,44 +2313,33 @@ static int fill_members(struct sss_packet *packet,
         }
 
         if (add_domain) {
-            delim = 1;
-            dom_len = sss_fqdom_len(member_dom->names, member_dom);
+            nlen = sss_fqname(NULL, 0, dom->names, dom, name.str);
+            if (nlen >= 0) {
+                nlen += 1;
+            } else {
+                /* Other failures caught below */
+                nlen = 0;
+            }
+        } else {
+            nlen = name.len;
         }
 
-        ret = sss_packet_grow(packet, name.len + delim + dom_len);
+        ret = sss_packet_grow(packet, nlen);
         if (ret != EOK) {
             goto done;
         }
         sss_packet_get_body(packet, &body, &blen);
 
         if (add_domain) {
-            ret = sss_fqname((char *)&body[rzero + rsize],
-                             name.len + delim + dom_len,
+            ret = sss_fqname((char *)&body[rzero + rsize], nlen,
                              member_dom->names, member_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;
-                ret = sss_packet_grow(packet, t);
-                if (ret != EOK) {
-                    goto done;
-                }
-                sss_packet_get_body(packet, &body, &blen);
-                delim += t;
-
-                /* retry */
-                ret = sss_fqname((char *)&body[rzero + rsize],
-                                name.len + delim + dom_len,
-                                member_dom->names, member_dom, name.str);
-            }
-
-            if (ret != name.len + delim + dom_len - 1) {
+            if (ret < 0 || ret != nlen - 1) {
                 DEBUG(SSSDBG_OP_FAILURE, ("Failed to generate a fully qualified name"
                                           " for member [%s@%s] of group [%s]!"
                                           " Skipping\n", name.str, domain,
                                           (char *)&body[rzero+STRS_ROFFSET]));
                 /* reclaim space */
-                ret = sss_packet_shrink(packet, name.len + delim + dom_len);
+                ret = sss_packet_shrink(packet, nlen);
                 if (ret != EOK) {
                     goto done;
                 }
@@ -2378,7 +2350,7 @@ static int fill_members(struct sss_packet *packet,
             memcpy(&body[rzero + rsize], name.str, name.len);
         }
 
-        rsize += name.len + delim + dom_len;
+        rsize += nlen;
         memnum++;
     }
 
@@ -2409,8 +2381,7 @@ static int fill_grent(struct sss_packet *packet,
     struct sized_string name;
     struct sized_string pwfield;
     struct sized_string fullname;
-    size_t delim = 0;
-    size_t dom_len = 0;
+    size_t fq_len = 0;
     int i = 0;
     int ret, num, memnum;
     size_t rzero, rsize;
@@ -2418,11 +2389,6 @@ static int fill_grent(struct sss_packet *packet,
     const char *domain = dom->name;
     TALLOC_CTX *tmp_ctx = NULL;
 
-    if (add_domain) {
-        delim = 1;
-        dom_len = sss_fqdom_len(dom->names, dom);
-    }
-
     to_sized_string(&pwfield, nctx->pwfield);
 
     num = 0;
@@ -2483,7 +2449,18 @@ static int fill_grent(struct sss_packet *packet,
 
         /* fill in gid and name and set pointer for number of members */
         rsize = STRS_ROFFSET + name.len + pwfield.len; /* name\0x\0 */
-        if (add_domain) rsize += delim + dom_len;
+
+        if (add_domain) {
+            fq_len = sss_fqname(NULL, 0, dom->names, dom, name.str);
+            if (fq_len >= 0) {
+                fq_len += 1;
+                rsize -= name.len;
+                rsize += fq_len;
+            } else {
+                /* Other failures caught below */
+                fq_len = 0;
+            }
+        }
 
         ret = sss_packet_grow(packet, rsize);
         if (ret != EOK) {
@@ -2500,28 +2477,9 @@ static int fill_grent(struct sss_packet *packet,
 
         /*  8-X: sequence of strings (name, passwd, mem..) */
         if (add_domain) {
-            ret = sss_fqname((char *)&body[rzero+STRS_ROFFSET],
-                             name.len + delim + dom_len,
+            ret = sss_fqname((char *)&body[rzero+STRS_ROFFSET], fq_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;
-                ret = sss_packet_grow(packet, t);
-                if (ret != EOK) {
-                    num = 0;
-                    goto done;
-                }
-                sss_packet_get_body(packet, &body, &blen);
-                rsize += t;
-                delim += t;
-
-                /* retry */
-                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) {
+            if (ret < 0 || ret != fq_len - 1) {
                 DEBUG(1, ("Failed to generate a fully qualified name for"
                           " group [%s] in [%s]! Skipping\n", name.str, domain));
                 /* reclaim space */
diff --git a/src/tests/cmocka/test_fqnames.c b/src/tests/cmocka/test_fqnames.c
index e073508..90f60c5 100644
--- a/src/tests/cmocka/test_fqnames.c
+++ b/src/tests/cmocka/test_fqnames.c
@@ -76,7 +76,6 @@ void test_default(void **state)
     char *fqdn;
     const int fqdn_size = 255;
     char fqdn_s[fqdn_size];
-    size_t domsize;
 
     if (test_ctx == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Type mismatch\n"));
@@ -87,7 +86,6 @@ void test_default(void **state)
                                    "(?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);
@@ -98,9 +96,6 @@ void test_default(void **state)
     assert_int_equal(ret + 1, sizeof(NAME"@"DOMNAME));
     assert_string_equal(fqdn_s, NAME"@"DOMNAME);
 
-    domsize = sss_fqdom_len(test_ctx->nctx, test_ctx->dom);
-    assert_int_equal(domsize, sizeof(DOMNAME)-1);
-
     talloc_free(test_ctx->nctx);
 }
 
@@ -113,7 +108,6 @@ void test_all(void **state)
     char *fqdn;
     const int fqdn_size = 255;
     char fqdn_s[fqdn_size];
-    size_t domsize;
 
     if (test_ctx == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Type mismatch\n"));
@@ -124,8 +118,6 @@ void test_all(void **state)
                                    "(?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);
@@ -136,9 +128,6 @@ void test_all(void **state)
     assert_int_equal(ret + 1, sizeof(NAME"@"DOMNAME"@"FLATNAME));
     assert_string_equal(fqdn_s, NAME"@"DOMNAME"@"FLATNAME);
 
-    domsize = sss_fqdom_len(test_ctx->nctx, test_ctx->dom);
-    assert_int_equal(domsize, sizeof(DOMNAME)-1 + sizeof(FLATNAME)-1);
-
     talloc_free(test_ctx->nctx);
 }
 
@@ -151,7 +140,6 @@ void test_flat(void **state)
     char *fqdn;
     const int fqdn_size = 255;
     char fqdn_s[fqdn_size];
-    size_t domsize;
 
     if (test_ctx == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Type mismatch\n"));
@@ -162,7 +150,6 @@ void test_flat(void **state)
                                    "(?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);
@@ -173,9 +160,6 @@ void test_flat(void **state)
     assert_int_equal(ret + 1, sizeof(NAME"@"FLATNAME));
     assert_string_equal(fqdn_s, NAME"@"FLATNAME);
 
-    domsize = sss_fqdom_len(test_ctx->nctx, test_ctx->dom);
-    assert_int_equal(domsize, sizeof(FLATNAME)-1);
-
     talloc_free(test_ctx->nctx);
 }
 
@@ -188,7 +172,6 @@ void test_flat_fallback(void **state)
     char *fqdn;
     const int fqdn_size = 255;
     char fqdn_s[fqdn_size];
-    size_t domsize;
 
     if (test_ctx == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Type mismatch\n"));
@@ -199,7 +182,6 @@ void test_flat_fallback(void **state)
                                    "(?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;
 
@@ -215,9 +197,6 @@ void test_flat_fallback(void **state)
     assert_int_equal(ret + 1, sizeof(NAME"@"DOMNAME));
     assert_string_equal(fqdn_s, NAME"@"DOMNAME);
 
-    domsize = sss_fqdom_len(test_ctx->nctx, test_ctx->dom);
-    assert_int_equal(domsize, sizeof(DOMNAME)-1);
-
     talloc_free(test_ctx->nctx);
 }
 
diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
index d1a4d4b..4db108a 100644
--- a/src/tests/cmocka/test_nss_srv.c
+++ b/src/tests/cmocka/test_nss_srv.c
@@ -540,8 +540,8 @@ void test_nss_getpwnam_fqdn(void **state)
  * Check that FQDN processing is able to handle arbitrarily sized
  * delimeter
  */
-static int test_nss_getpwnam_check_resize_fqdn(uint32_t status,
-                                               uint8_t *body, size_t blen)
+static int test_nss_getpwnam_check_fancy_fqdn(uint32_t status,
+                                              uint8_t *body, size_t blen)
 {
     struct passwd pwd;
     errno_t ret;
@@ -555,29 +555,28 @@ static int test_nss_getpwnam_check_resize_fqdn(uint32_t status,
 
     assert_int_equal(pwd.pw_uid, 125);
     assert_int_equal(pwd.pw_gid, 458);
-    assert_string_equal(pwd.pw_name, "testuser_fqdn_resize@@@@@"TEST_DOM_NAME);
+    assert_string_equal(pwd.pw_name, "testuser_fqdn_fancy@@@@@"TEST_DOM_NAME);
     assert_string_equal(pwd.pw_shell, "/bin/sh");
     return EOK;
 }
 
-void test_nss_getpwnam_fqdn_resize(void **state)
+void test_nss_getpwnam_fqdn_fancy(void **state)
 {
     errno_t ret;
 
     /* Prime the cache with a valid user */
     ret = sysdb_add_user(nss_test_ctx->tctx->dom,
-                         "testuser_fqdn_resize", 125, 458, "test user",
+                         "testuser_fqdn_fancy", 125, 458, "test user",
                          "/home/testuser", "/bin/sh", NULL,
                          NULL, 300, 0);
     assert_int_equal(ret, EOK);
 
-    mock_input_user_or_group("testuser_fqdn_resize@"TEST_DOM_NAME);
+    mock_input_user_or_group("testuser_fqdn_fancy@"TEST_DOM_NAME);
     will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM);
     mock_fill_user();
-    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
 
     /* Query for that user, call a callback when command finishes */
-    set_cmd_cb(test_nss_getpwnam_check_resize_fqdn);
+    set_cmd_cb(test_nss_getpwnam_check_fancy_fqdn);
     nss_test_ctx->cctx->rctx->domains[0].fqnames = true;
     ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWNAM,
                           nss_test_ctx->nss_cmds);
@@ -1298,7 +1297,7 @@ void nss_subdom_test_setup(void **state)
     nss_test_ctx->subdom = subdomain;
 }
 
-void nss_fqdn_resize_test_setup(void **state)
+void nss_fqdn_fancy_test_setup(void **state)
 {
     struct sss_test_conf_param params[] = {
         { "enumerate", "false" },
@@ -1339,8 +1338,8 @@ int main(int argc, const char *argv[])
                                  nss_test_setup, nss_test_teardown),
         unit_test_setup_teardown(test_nss_getpwnam_fqdn,
                                  nss_fqdn_test_setup, nss_test_teardown),
-        unit_test_setup_teardown(test_nss_getpwnam_fqdn_resize,
-                                 nss_fqdn_resize_test_setup, nss_test_teardown),
+        unit_test_setup_teardown(test_nss_getpwnam_fqdn_fancy,
+                                 nss_fqdn_fancy_test_setup, nss_test_teardown),
         unit_test_setup_teardown(test_nss_getgrnam_no_members,
                                  nss_test_setup, nss_test_teardown),
         unit_test_setup_teardown(test_nss_getgrnam_members,
diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
index eeef9ee..13a67f7 100644
--- a/src/tests/cmocka/test_utils.c
+++ b/src/tests/cmocka/test_utils.c
@@ -531,8 +531,7 @@ void test_sss_names_init(void **state)
     assert_int_equal(ret, EOK);
     assert_non_null(names_ctx);
     assert_string_equal(names_ctx->re_pattern, GLOBAL_RE_EXPRESSION);
-    assert_string_equal(names_ctx->fq_fmt, GLOBAL_FULL_NAME_FORMAT"%3$s");
-    assert_int_equal(names_ctx->fq_flags, FQ_FMT_NAME|FQ_FMT_DOMAIN);
+    assert_string_equal(names_ctx->fq_fmt, GLOBAL_FULL_NAME_FORMAT);
 
     talloc_free(names_ctx);
 
@@ -541,8 +540,7 @@ void test_sss_names_init(void **state)
     assert_int_equal(ret, EOK);
     assert_non_null(names_ctx);
     assert_string_equal(names_ctx->re_pattern, DOMAIN_RE_EXPRESSION);
-    assert_string_equal(names_ctx->fq_fmt, DOMAIN_FULL_NAME_FORMAT"%2$s");
-    assert_int_equal(names_ctx->fq_flags, FQ_FMT_NAME|FQ_FMT_FLAT_NAME);
+    assert_string_equal(names_ctx->fq_fmt, DOMAIN_FULL_NAME_FORMAT);
 
     talloc_free(names_ctx);
 }
diff --git a/src/util/usertools.c b/src/util/usertools.c
index cd57d0c..9469311 100644
--- a/src/util/usertools.c
+++ b/src/util/usertools.c
@@ -28,6 +28,8 @@
 #include "util/util.h"
 #include "responder/common/responder.h"
 
+#include <safe-printf.h>
+
 #ifdef HAVE_LIBPCRE_LESSER_THAN_7
 #define NAME_DOMAIN_PATTERN_OPTIONS (PCRE_EXTENDED)
 #else
@@ -138,18 +140,7 @@ done:
 
 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", FQ_FMT_NAME },
-        { "%2$s", "domain name", FQ_FMT_DOMAIN },
-        { "%3$s", "domain flat name", FQ_FMT_FLAT_NAME },
-        { NULL, NULL, 0 }
-    };
+    char *fq;
 
     nctx->fq_fmt = talloc_strdup(nctx, fq_fmt);
     if (nctx->fq_fmt == NULL) {
@@ -158,34 +149,20 @@ static errno_t sss_fqnames_init(struct sss_names_ctx *nctx, const char *fq_fmt)
 
     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) {
+    /* Fail if the name specifier is missing, or if the format is
+     * invalid */
+    fq = sss_tc_fqname2 (nctx, nctx, "unused.example.com", "unused", "the-test-user");
+    if (fq == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("The fq format is invalid [%s]", nctx->fq_fmt));
+        return EINVAL;
+    } else if (strstr (fq, "the-test-user") == NULL) {
         DEBUG(SSSDBG_OP_FAILURE,
               ("Username pattern not found in [%s]\n", nctx->fq_fmt));
         return ENOENT;
     }
-    nctx->fq_flags = FQ_FMT_NAME;
-
-    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;
-    }
 
+    talloc_free (fq);
     return EOK;
 }
 
@@ -568,19 +545,13 @@ 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)
+calc_flat_name(struct sss_domain_info *domain)
 {
     const char *s;
 
-    s = safe_fq_str(nctx, FQ_FMT_FLAT_NAME, domain->flat_name);
+    s = domain->flat_name;
     if (s == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, ("Flat name requested but domain has no"
+        DEBUG(SSSDBG_MINOR_FAILURE, ("Flat name requested but domain has no"
               "flat name set, falling back to domain name\n"));
         s = domain->name;
     }
@@ -588,28 +559,24 @@ safe_flat_name(struct sss_names_ctx *nctx, struct sss_domain_info *domain)
     return s;
 }
 
-static inline size_t
-fq_part_len(struct sss_names_ctx *nctx, struct sss_domain_info *dom,
-            uint8_t part, const char *str)
-{
-    const char *s = str;
-
-    if (part == FQ_FMT_FLAT_NAME) {
-        s = safe_flat_name(nctx, dom);
-    }
-    return nctx->fq_flags & part ? strlen(s) : 0;
-}
-
 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,
-                           safe_fq_str(nctx, FQ_FMT_NAME, name),
-                           safe_fq_str(nctx, FQ_FMT_DOMAIN, domain->name),
-                           safe_flat_name(nctx, domain));
+    return sss_tc_fqname2 (mem_ctx, nctx, domain->name,
+                           calc_flat_name (domain), name);
+}
+
+static void
+safe_talloc_callback (void *data,
+                      const char *piece,
+                      size_t len)
+{
+    char **output = data;
+    if (*output != NULL)
+        *output = talloc_strndup_append(*output, piece, len);
 }
 
 char *
@@ -617,12 +584,15 @@ sss_tc_fqname2(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
                const char *domain_name, const char *flat_dom_name,
                const char *name)
 {
+    const char *args[] = { name, domain_name, flat_dom_name, NULL };
+    char *output;
+
     if (nctx == NULL) return NULL;
 
-    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_fq_str(nctx, FQ_FMT_FLAT_NAME, flat_dom_name));
+    output = talloc_strdup(mem_ctx, "");
+    if (safe_printf_cb(safe_talloc_callback, &output, nctx->fq_fmt, args, 3) < 0)
+        output = NULL;
+    return output;
 }
 
 int
@@ -631,19 +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,
-                    safe_fq_str(nctx, FQ_FMT_NAME, name),
-                    safe_fq_str(nctx, FQ_FMT_DOMAIN, domain->name),
-                    safe_flat_name(nctx, domain));
-}
-
-size_t
-sss_fqdom_len(struct sss_names_ctx *nctx,
-              struct sss_domain_info *domain)
-{
-    size_t len = fq_part_len(nctx, domain, FQ_FMT_DOMAIN, domain->name);
-    len += fq_part_len(nctx, domain, FQ_FMT_FLAT_NAME, domain->flat_name);
-    return len;
+    return safe_snprintf(str, size, nctx->fq_fmt,
+                         name, domain->name, calc_flat_name (domain), NULL);
 }
 
 char *
diff --git a/src/util/util.h b/src/util/util.h
index 1012705..4a9dbb7 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -324,14 +324,9 @@ 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;
 };
@@ -384,15 +379,14 @@ sss_tc_fqname2(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
 /* 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.
+ *
+ * Pass a zero size to calculate the length that would be needed by the fully-qualified
+ * name.
  */
 int
 sss_fqname(char *str, size_t size, struct sss_names_ctx *nctx,
            struct sss_domain_info *domain, const char *name);
 
-size_t
-sss_fqdom_len(struct sss_names_ctx *nctx,
-              struct sss_domain_info *domain);
-
 /* Subdomains use fully qualified names in the cache while primary domains use
  * just the name. Return either of these for a specified domain or subdomain
  */
-- 
1.8.4.2

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to