[SSSD] [PATCH] Add expandable sequences to krb5_ccachedir

Sumit Bose sbose at redhat.com
Thu Mar 11 12:02:13 UTC 2010


On Wed, Mar 10, 2010 at 09:19:14AM -0500, Stephen Gallagher wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> On 03/09/2010 12:30 PM, Sumit Bose wrote:
> >> I have disabled the '%P' sequence for the directory expansion and change
> >> setting of the permissions as described above.
> > 
> >> bye,
> >> Sumit
> > 
> 
> Nack
> 
> Using "expand_ccache_dir" as the boolean for disallowing %P is
> confusing. Please rename the variable to make it more obvious why you
> can't use %P. (Maybe "bool restrict_substitions")

done, renamed to file_mode

> 
> Using lstat() in find_ccdir_parent_data() is a bad idea. We should just
> be using stat() to determine if we're ultimately dealing with a
> directory, unless your intent here is to mandate that krb5_ccachedir
> must never contain a symlink, in which case this needs to be documented
> in the manpage, along with why.

done

> 
> Please save errno before calling DEBUG(). It won't always be guaranteed
> that errno is unchanged within the DEBUG macro. This occurs in both
> find_ccdir_parent_data() and create_ccache_dir().

done

> 
> li->s = talloc_strdup(mem_ctx, dirname);
> Instead of mem_ctx, you should be using li for the memory parent.

done

> 
> Please talloc_zfree(parent) before returning from
> find_ccdir_parent_data(), otherwise you'll be carrying that unnecessary
> memory around.
> 
> 
> You have similar memory issues in create_ccache_dir(). You create
> dirname with the mem_ctx as the parent, but you never free it. And since
> you're moving the pointer on it, you probably want to save the start of
> the string so you can call talloc_free() on that.

done, additionally I introduced a temporary talloc context to get rid of
other allocated memory, too.

> 
> if (!private_path && (parent_stat.st_uid != 0 || parent_stat.st_gid != 0))
> 
> This check doesn't make sense to me. You're only going to allow the
> creation of a public directory if the parent dir is owned by root and
> has group root? I think the check should be for
> (parent_stat.st_mode & (S_ISVTX | S_IWOTH | S_IXOTH)) (aka
> world-writeable and world-executable and sticky-bit)
> 
> Similarly, the private path needs to check for
> (parent_stat.st_uid == uid && parent_stat.st_mode & (S_IWUSR | S_IXUSR))
>  ||
> (parent_stat.st_mode & (S_ISVTX | S_IWOTH | S_IXOTH))
> 
> (aka owned by the current user and writeable/executable or
> (world-writeable, world-executable and sticky-bit))

the sticky-bit won't help here if the directory does not belong to root.
However I added the x-bit check and removed the gid check.

New version attached.

bye,
Sumit

> 
> - -- 
> Stephen Gallagher
> RHCE 804006346421761
> 
> Delivering value year after year.
> Red Hat ranks #1 in value among software vendors.
> http://www.redhat.com/promo/vendor/
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.10 (GNU/Linux)
> Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/
> 
> iEYEARECAAYFAkuXqmEACgkQeiVVYja6o6N7GACgkBeQm+NcZ4l0BCOKhlNgiaka
> op4AoKXik40TL9yPpQUOwKxV0JJqDi5b
> =GnHu
> -----END PGP SIGNATURE-----
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
-------------- next part --------------
From e33dee7b8881f860279dd828fab01cd30a5fb91b Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 23 Feb 2010 09:43:51 +0100
Subject: [PATCH] Add expandable sequences to krb5_ccachedir

As with krb5_ccname_template sequences like %u can be used in the
krb5_ccachedir parameter which are expanded at runtime. If the directory
does not exist, it will be created. Depending on the used sequences it
is created as a public or private directory.
---
 src/man/sssd-krb5.5.xml          |   12 +-
 src/providers/krb5/krb5_auth.c   |   13 +-
 src/providers/krb5/krb5_auth.h   |    6 +
 src/providers/krb5/krb5_common.c |   13 -
 src/providers/krb5/krb5_init.c   |   12 +
 src/providers/krb5/krb5_utils.c  |  263 +++++++++++++++++-
 src/providers/krb5/krb5_utils.h  |    6 +-
 src/tests/krb5_utils-tests.c     |  562 +++++++++++++++++++++++++++++++-------
 8 files changed, 773 insertions(+), 114 deletions(-)

diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index 32b6c29..ca4dae2 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -102,7 +102,17 @@
                     <term>krb5_ccachedir (string)</term>
                     <listitem>
                         <para>
-                            Directory to store credential caches.
+                            Directory to store credential caches. All the
+                            substitution sequences of krb5_ccname_template can
+                            be used here, too, except %d and %P. If the
+                            directory does not exist it will be created. If %u,
+                            %U, %p or %h are used a private directory belonging
+                            to the user is created. Otherwise a public directory
+                            with restricted deletion flag (aka sticky bit, see
+                            <citerefentry>
+                                <refentrytitle>chmod</refentrytitle>
+                                <manvolnum>1</manvolnum>
+                            </citerefentry> for details) is created.
                         </para>
                         <para>
                             Default: /tmp
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 5be42a8..b8b498a 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -881,6 +881,7 @@ static void krb5_resolve_done(struct tevent_req *req)
     struct be_req *be_req = kr->req;
     char *msg;
     size_t offset = 0;
+    bool private_path = false;
 
     ret = be_resolve_server_recv(req, &kr->srv);
     talloc_zfree(req);
@@ -916,12 +917,20 @@ static void krb5_resolve_done(struct tevent_req *req)
             }
             kr->ccname = expand_ccname_template(kr, kr,
                                           dp_opt_get_cstring(kr->krb5_ctx->opts,
-                                                             KRB5_CCNAME_TMPL)
-                                    );
+                                                             KRB5_CCNAME_TMPL),
+                                                true, &private_path);
             if (kr->ccname == NULL) {
                 DEBUG(1, ("expand_ccname_template failed.\n"));
                 goto done;
             }
+
+            ret = create_ccache_dir(kr, kr->ccname,
+                                    kr->krb5_ctx->illegal_path_re,
+                                    kr->uid, kr->gid, private_path);
+            if (ret != EOK) {
+                DEBUG(1, ("create_ccache_dir failed.\n"));
+                goto done;
+            }
     }
 
     if (be_is_offline(be_req->be_ctx)) {
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 3e11f27..825f3d6 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -26,6 +26,8 @@
 #ifndef __KRB5_AUTH_H__
 #define __KRB5_AUTH_H__
 
+#include <pcre.h>
+
 #include "util/sss_krb5.h"
 #include "providers/dp_backend.h"
 #include "providers/krb5/krb5_common.h"
@@ -33,6 +35,8 @@
 #define CCACHE_ENV_NAME "KRB5CCNAME"
 #define SSSD_KRB5_CHANGEPW_PRINCIPLE "SSSD_KRB5_CHANGEPW_PRINCIPLE"
 
+#define ILLEGAL_PATH_PATTERN "//|/\\./|/\\.\\./"
+
 typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
 
 struct krb5child_req {
@@ -87,6 +91,8 @@ struct krb5_ctx {
     struct dp_option *opts;
     struct krb5_service *service;
     int child_debug_fd;
+
+    pcre *illegal_path_re;
 };
 
 void krb5_pam_handler(struct be_req *be_req);
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 86676f4..7619e6a 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -47,7 +47,6 @@ errno_t check_and_export_options(struct dp_option *opts,
     char *value;
     const char *realm;
     const char *dummy;
-    struct stat stat_buf;
     char **list;
 
     realm = dp_opt_get_cstring(opts, KRB5_REALM);
@@ -83,18 +82,6 @@ errno_t check_and_export_options(struct dp_option *opts,
         talloc_free(list);
     }
 
-    dummy = dp_opt_get_cstring(opts, KRB5_CCACHEDIR);
-    ret = lstat(dummy, &stat_buf);
-    if (ret != EOK) {
-        DEBUG(1, ("lstat for [%s] failed: [%d][%s].\n", dummy, errno,
-                  strerror(errno)));
-        return ret;
-    }
-    if ( !S_ISDIR(stat_buf.st_mode) ) {
-        DEBUG(1, ("Value of krb5ccache_dir [%s] is not a directory.\n", dummy));
-        return EINVAL;
-    }
-
     dummy = dp_opt_get_cstring(opts, KRB5_CCNAME_TMPL);
     if (dummy == NULL) {
         DEBUG(1, ("Missing credential cache name template.\n"));
diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c
index 43cbc1b..4d21238 100644
--- a/src/providers/krb5/krb5_init.c
+++ b/src/providers/krb5/krb5_init.c
@@ -53,6 +53,9 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
     FILE *debug_filep;
     const char *krb5_servers;
     const char *krb5_realm;
+    const char *errstr;
+    int errval;
+    int errpos;
 
     if (krb5_options == NULL) {
         krb5_options = talloc_zero(bectx, struct krb5_options);
@@ -135,6 +138,15 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
         fcntl(ctx->child_debug_fd, F_SETFD, v & ~FD_CLOEXEC);
     }
 
+    ctx->illegal_path_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0,
+                                         &errval, &errstr, &errpos, NULL);
+    if (ctx->illegal_path_re == NULL) {
+        DEBUG(1, ("Invalid Regular Expression pattern at position %d. "
+                  "(Error: %d [%s])\n", errpos, errval, errstr));
+        ret = EFAULT;
+        goto fail;
+    }
+
     *ops = &krb5_auth_ops;
     *pvt_auth_data = ctx;
     return EOK;
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index a75ad78..83971ab 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -29,13 +29,17 @@
 #include "util/util.h"
 
 char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
-                             const char *template)
+                             const char *template, bool file_mode,
+                             bool *private_path)
 {
     char *copy;
     char *p;
     char *n;
     char *result = NULL;
     const char *dummy;
+    const char *cache_dir_tmpl;
+
+    *private_path = false;
 
     if (template == NULL) {
         DEBUG(1, ("Missing template.\n"));
@@ -72,6 +76,7 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
                 }
                 result = talloc_asprintf_append(result, "%s%s", p,
                                                 kr->pd->user);
+                if (!file_mode) *private_path = true;
                 break;
             case 'U':
                 if (kr->uid <= 0) {
@@ -81,6 +86,7 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
                 }
                 result = talloc_asprintf_append(result, "%s%d", p,
                                                 kr->uid);
+                if (!file_mode) *private_path = true;
                 break;
             case 'p':
                 if (kr->upn == NULL) {
@@ -89,6 +95,7 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
                     return NULL;
                 }
                 result = talloc_asprintf_append(result, "%s%s", p, kr->upn);
+                if (!file_mode) *private_path = true;
                 break;
             case '%':
                 result = talloc_asprintf_append(result, "%s%%", p);
@@ -108,16 +115,35 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
                     return NULL;
                 }
                 result = talloc_asprintf_append(result, "%s%s", p, kr->homedir);
+                if (!file_mode) *private_path = true;
                 break;
             case 'd':
-                dummy = dp_opt_get_string(kr->krb5_ctx->opts, KRB5_CCACHEDIR);
-                if (dummy == NULL) {
-                    DEBUG(1, ("Missing credential cache directory.\n"));
+                if (file_mode) {
+                    cache_dir_tmpl = dp_opt_get_string(kr->krb5_ctx->opts,
+                                                       KRB5_CCACHEDIR);
+                    if (cache_dir_tmpl == NULL) {
+                        DEBUG(1, ("Missing credential cache directory.\n"));
+                        return NULL;
+                    }
+
+                    dummy = expand_ccname_template(mem_ctx, kr, cache_dir_tmpl,
+                                                   false, private_path);
+                    if (dummy == NULL) {
+                        DEBUG(1, ("Expanding credential cache directory "
+                                  "template failed.\n"));
+                        return NULL;
+                    }
+                    result = talloc_asprintf_append(result, "%s%s", p, dummy);
+                } else {
+                    DEBUG(1, ("'%%d' is not allowed in this template.\n"));
                     return NULL;
                 }
-                result = talloc_asprintf_append(result, "%s%s", p, dummy);
                 break;
             case 'P':
+                if (!file_mode) {
+                    DEBUG(1, ("'%%P' is not allowed in this template.\n"));
+                    return NULL;
+                }
                 if (kr->pd->cli_pid == 0) {
                     DEBUG(1, ("Cannot expand PID template "
                               "because PID is not available.\n"));
@@ -143,3 +169,230 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
 
     return result;
 }
+
+static errno_t check_parent_stat(bool private_path, struct stat *parent_stat,
+                                 uid_t uid, gid_t gid)
+{
+    if (private_path) {
+        if (!((parent_stat->st_uid == 0 && parent_stat->st_gid == 0) ||
+               parent_stat->st_uid == uid)) {
+            DEBUG(1, ("Private directory can only be created below a "
+                      "directory belonging to root or to [%d][%d].\n",
+                      uid, gid));
+            return EINVAL;
+        }
+
+        if (parent_stat->st_uid == uid) {
+            if (!(parent_stat->st_mode & S_IXUSR)) {
+                DEBUG(1, ("Parent directory does have the search bit set for "
+                          "the owner.\n"));
+                return EINVAL;
+            }
+        } else {
+            if (!(parent_stat->st_mode & S_IXOTH)) {
+                DEBUG(1, ("Parent directory does have the search bit set for "
+                        "others.\n"));
+                return EINVAL;
+            }
+        }
+    } else {
+        if (parent_stat->st_uid != 0 || parent_stat->st_gid != 0) {
+            DEBUG(1, ("Public directory cannot be created below a user "
+                      "directory.\n"));
+            return EINVAL;
+        }
+
+        if (!(parent_stat->st_mode & S_IXOTH)) {
+            DEBUG(1, ("Parent directory does have the search bit set for "
+                      "others.\n"));
+            return EINVAL;
+        }
+    }
+
+    return EOK;
+}
+
+struct string_list {
+    struct string_list *next;
+    struct string_list *prev;
+    char *s;
+};
+
+static errno_t find_ccdir_parent_data(TALLOC_CTX *mem_ctx, const char *dirname,
+                                      struct stat *parent_stat,
+                                      struct string_list **missing_parents)
+{
+    int ret = EFAULT;
+    char *parent = NULL;
+    char *end;
+    struct string_list *li;
+
+    ret = stat(dirname, parent_stat);
+    if (ret == EOK) {
+        if ( !S_ISDIR(parent_stat->st_mode) ) {
+            DEBUG(1, ("[%s] is not a directory.\n", dirname));
+            return EINVAL;
+        }
+        return EOK;
+    } else {
+        if (errno != ENOENT) {
+            ret = errno;
+            DEBUG(1, ("stat for [%s] failed: [%d][%s].\n", dirname, ret,
+                      strerror(ret)));
+            return ret;
+        }
+    }
+
+    li = talloc_zero(mem_ctx, struct string_list);
+    if (li == NULL) {
+        DEBUG(1, ("talloc_zero failed.\n"));
+        return ENOMEM;
+    }
+
+    li->s = talloc_strdup(li, dirname);
+    if (li->s == NULL) {
+        DEBUG(1, ("talloc_strdup failed.\n"));
+        return ENOMEM;
+    }
+
+    DLIST_ADD(*missing_parents, li);
+
+    parent = talloc_strdup(mem_ctx, dirname);
+    if (parent == NULL) {
+        DEBUG(1, ("talloc_strdup failed.\n"));
+        return ENOMEM;
+    }
+    end = strrchr(parent, '/');
+    if (end == NULL || end == parent) {
+        DEBUG(1, ("Cannot find parent directory of [%s], / is not allowed.\n",
+                   dirname));
+        ret = EINVAL;
+        goto done;
+    }
+    *end = '\0';
+
+    ret = find_ccdir_parent_data(mem_ctx, parent, parent_stat, missing_parents);
+
+done:
+    talloc_free(parent);
+    return ret;
+}
+
+errno_t create_ccache_dir(TALLOC_CTX *mem_ctx, const char *filename,
+                          pcre *illegal_re, uid_t uid, gid_t gid,
+                          bool private_path)
+{
+    int ret = EFAULT;
+    char *dirname;
+    char *end;
+    struct stat parent_stat;
+    struct string_list *missing_parents = NULL;
+    struct string_list *li = NULL;
+    mode_t old_umask;
+    mode_t new_dir_mode;
+    size_t offset = 0;
+    TALLOC_CTX *tmp_ctx = NULL;
+
+    tmp_ctx = talloc_new(mem_ctx);
+    if (tmp_ctx == NULL) {
+        DEBUG(1, ("talloc_new failed.\n"));
+        return ENOMEM;
+    }
+
+    if (strncmp(filename, "FILE:", 5) == 0) {
+        offset = 5;
+    }
+
+    dirname = talloc_strdup(tmp_ctx, filename + offset);
+    if (dirname == NULL) {
+        DEBUG(1, ("talloc_strndup failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    if (*dirname != '/') {
+        DEBUG(1, ("Only absolute paths are allowed, not [%s] .\n", dirname));
+        ret = EINVAL;
+        goto done;
+    }
+
+    if (illegal_re != NULL) {
+        ret = pcre_exec(illegal_re, NULL, dirname, strlen(dirname),
+                        0, 0, NULL, 0);
+        if (ret == 0) {
+            DEBUG(1, ("Illegal pattern in ccache directory name [%s].\n",
+                      dirname));
+            ret = EINVAL;
+            goto done;
+        } else if ( ret == PCRE_ERROR_NOMATCH) {
+            DEBUG(9, ("Ccache directory name [%s] does not contain "
+                      "illegal patterns.\n", dirname));
+        } else {
+            DEBUG(1, ("pcre_exec failed [%d].\n", ret));
+            ret = EFAULT;
+            goto done;
+        }
+    }
+
+    end = strrchr(dirname, '/');
+    if (end == NULL || end == dirname) {
+        DEBUG(1, ("Missing filename in [%s].\n", dirname));
+        ret = EINVAL;
+        goto done;
+    }
+    *end = '\0';
+
+    ret = find_ccdir_parent_data(tmp_ctx, dirname, &parent_stat,
+                                 &missing_parents);
+    if (ret != EOK) {
+        DEBUG(1, ("find_ccdir_parent_data failed.\n"));
+        goto done;
+    }
+
+    ret = check_parent_stat(private_path, &parent_stat, uid, gid);
+    if (ret != EOK) {
+        DEBUG(1, ("check_parent_stat failed for %s directory [%s].\n",
+                  private_path ? "private" : "public", dirname));
+        goto done;
+    }
+
+    DLIST_FOR_EACH(li, missing_parents) {
+        DEBUG(9, ("Creating directory [%s].\n", li->s));
+        if (li->next == NULL) {
+            new_dir_mode = private_path ? 0700 : 01777;
+        } else {
+            if (private_path &&
+                parent_stat.st_uid == uid && parent_stat.st_gid == gid) {
+                new_dir_mode = 0700;
+            } else {
+                new_dir_mode = 0755;
+            }
+        }
+
+        old_umask = umask(0000);
+        ret = mkdir(li->s, new_dir_mode);
+        umask(old_umask);
+        if (ret != EOK) {
+            ret = errno;
+            DEBUG(1, ("mkdir [%s] failed: [%d][%s].\n", li->s, ret,
+                      strerror(ret)));
+            goto done;
+        }
+        if (private_path &&
+            ((parent_stat.st_uid == uid && parent_stat.st_gid == gid) ||
+             li->next == NULL)) {
+            ret = chown(li->s, uid, gid);
+            if (ret != EOK) {
+                ret = errno;
+                DEBUG(1, ("chown failed [%d][%s].\n", ret, strerror(ret)));
+                goto done;
+            }
+        }
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h
index 7637041..be162bc 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -32,8 +32,12 @@
 #include "providers/data_provider.h"
 
 char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
-                             const char *template);
+                             const char *template, bool file_mode,
+                             bool *private_path);
 
 errno_t become_user(uid_t uid, gid_t gid);
 
+errno_t create_ccache_dir(TALLOC_CTX *mem_ctx, const char *filename,
+                          pcre *illegal_re, uid_t uid, gid_t gid,
+                          bool private_path);
 #endif /* __KRB5_UTILS_H__ */
diff --git a/src/tests/krb5_utils-tests.c b/src/tests/krb5_utils-tests.c
index 64cf9c3..3e7c2ec 100644
--- a/src/tests/krb5_utils-tests.c
+++ b/src/tests/krb5_utils-tests.c
@@ -23,13 +23,17 @@
 */
 
 #include <stdlib.h>
+#include <popt.h>
 #include <check.h>
 
 #include "providers/krb5/krb5_utils.h"
 #include "providers/krb5/krb5_auth.h"
 #include "tests/common.h"
 
+#define TESTS_PATH "tests_krb5_utils"
+
 #define BASE "/abc/def"
+#define FILENAME "ghi"
 
 #define USERNAME "testuser"
 #define UID "12345"
@@ -44,6 +48,315 @@ extern struct dp_option default_krb5_opts[];
 TALLOC_CTX *tmp_ctx = NULL;
 struct krb5child_req *kr;
 
+#define RMDIR(__dir__) do { \
+    ret = rmdir(__dir__); \
+    fail_unless(ret == EOK, "rmdir [%s] failed, [%d][%s].", __dir__, \
+                errno, strerror(errno)); \
+} while(0);
+
+void setup_create_dir(void)
+{
+    fail_unless(tmp_ctx == NULL, "Talloc context already initialized.");
+    tmp_ctx = talloc_new(NULL);
+    fail_unless(tmp_ctx != NULL, "Cannot create talloc context.");
+}
+
+void teardown_create_dir(void)
+{
+    int ret;
+    fail_unless(tmp_ctx != NULL, "Talloc context already freed.");
+    ret = talloc_free(tmp_ctx);
+    tmp_ctx = NULL;
+    fail_unless(ret == 0, "Connot free talloc context.");
+}
+
+static void check_dir(const char *dirname, uid_t uid, gid_t gid, mode_t mode)
+{
+    struct stat stat_buf;
+    int ret;
+
+    ret = stat(dirname, &stat_buf);
+    fail_unless(ret == EOK, "stat failed [%d][%s].", errno, strerror(errno));
+
+    fail_unless(S_ISDIR(stat_buf.st_mode), "[%s] is not a directory.", dirname);
+    fail_unless(stat_buf.st_uid == uid, "uid does not match, "
+                                        "expected [%d], got [%d].",
+                                        uid, stat_buf.st_uid);
+    fail_unless(stat_buf.st_gid == gid, "gid does not match, "
+                                        "expected [%d], got [%d].",
+                                        gid, stat_buf.st_gid);
+    fail_unless((stat_buf.st_mode & ~S_IFMT) == mode,
+                                           "mode of [%s] does not match, "
+                                           "expected [%o], got [%o].", dirname,
+                                            mode, (stat_buf.st_mode & ~S_IFMT));
+}
+
+START_TEST(test_pub_ccache_dir)
+{
+    int ret;
+    char *cwd;
+    char *testpath;
+    char *dirname;
+    char *subdirname;
+    char *filename;
+
+    fail_unless(getuid() == 0, "This test must be run as root.");
+
+    cwd = getcwd(NULL, 0);
+    fail_unless(cwd != NULL, "getcwd failed.");
+
+    testpath = talloc_asprintf(tmp_ctx, "%s/%s", cwd, TESTS_PATH);
+    free(cwd);
+    fail_unless(testpath != NULL, "talloc_asprintf failed.");
+    dirname = talloc_asprintf(tmp_ctx, "%s/pub_ccdir", testpath);
+    fail_unless(dirname != NULL, "talloc_asprintf failed.");
+    subdirname = talloc_asprintf(tmp_ctx, "%s/subdir", dirname);
+    fail_unless(subdirname != NULL, "talloc_asprintf failed.");
+    filename = talloc_asprintf(tmp_ctx, "%s/ccfile", subdirname);
+    fail_unless(filename != NULL, "talloc_asprintf failed.");
+
+    ret = chmod(testpath, 0754);
+    fail_unless(ret == EOK, "chmod failed.");
+    ret = create_ccache_dir(tmp_ctx, filename, NULL, 12345, 12345, false);
+    fail_unless(ret == EINVAL, "create_ccache_dir does not return EINVAL "
+                               "while x-bit is missing.");
+
+    ret = chmod(testpath, 0755);
+    fail_unless(ret == EOK, "chmod failed.");
+    ret = create_ccache_dir(tmp_ctx, filename, NULL, 12345, 12345, false);
+    fail_unless(ret == EOK, "create_ccache_dir failed.");
+
+    check_dir(subdirname, 0, 0, 01777);
+    RMDIR(subdirname);
+    check_dir(dirname, 0, 0, 0755);
+    RMDIR(dirname);
+}
+END_TEST
+
+START_TEST(test_pub_ccache_dir_in_user_dir)
+{
+    int ret;
+    char *cwd;
+    char *dirname;
+    char *subdirname;
+    char *filename;
+
+    fail_unless(getuid() == 0, "This test must be run as root.");
+
+    cwd = getcwd(NULL, 0);
+    fail_unless(cwd != NULL, "getcwd failed.");
+
+    dirname = talloc_asprintf(tmp_ctx, "%s/%s/pub_ccdir", cwd, TESTS_PATH);
+    free(cwd);
+    fail_unless(dirname != NULL, "talloc_asprintf failed.");
+    ret = mkdir(dirname, 0700);
+    fail_unless(ret == EOK, "mkdir failed.\n");
+    ret = chown(dirname, 12345, 12345);
+    fail_unless(ret == EOK, "chown failed.\n");
+    subdirname = talloc_asprintf(tmp_ctx, "%s/subdir", dirname);
+    fail_unless(subdirname != NULL, "talloc_asprintf failed.");
+    filename = talloc_asprintf(tmp_ctx, "%s/ccfile", subdirname);
+    fail_unless(filename != NULL, "talloc_asprintf failed.");
+
+    ret = create_ccache_dir(tmp_ctx, filename, NULL, 12345, 12345, false);
+    fail_unless(ret == EINVAL, "Creating public ccache dir in user dir "
+                               "does not failed with EINVAL.");
+
+    RMDIR(dirname);
+}
+END_TEST
+
+START_TEST(test_priv_ccache_dir)
+{
+    int ret;
+    char *cwd;
+    char *testpath;
+    char *dirname;
+    char *subdir;
+    char *filename;
+    uid_t uid = 12345;
+    gid_t gid = 12345;
+
+    fail_unless(getuid() == 0, "This test must be run as root.");
+
+    cwd = getcwd(NULL, 0);
+    fail_unless(cwd != NULL, "getcwd failed.");
+
+    testpath = talloc_asprintf(tmp_ctx, "%s/%s", cwd, TESTS_PATH);
+    free(cwd);
+    fail_unless(testpath != NULL, "talloc_asprintf failed.");
+    dirname = talloc_asprintf(tmp_ctx, "%s/base", testpath);
+    subdir = talloc_asprintf(tmp_ctx, "%s/priv_ccdir", dirname);
+    fail_unless(subdir != NULL, "talloc_asprintf failed.");
+    filename = talloc_asprintf(tmp_ctx, "%s/ccfile", subdir);
+    fail_unless(filename != NULL, "talloc_asprintf failed.");
+
+    ret = chmod(testpath, 0754);
+    fail_unless(ret == EOK, "chmod failed.");
+    ret = create_ccache_dir(tmp_ctx, filename, NULL, uid, gid, true);
+    fail_unless(ret == EINVAL, "create_ccache_dir does not return EINVAL "
+                               "while x-bit is missing.");
+
+    ret = chmod(testpath, 0755);
+    fail_unless(ret == EOK, "chmod failed.");
+    ret = create_ccache_dir(tmp_ctx, filename, NULL, uid, gid, true);
+    fail_unless(ret == EOK, "create_ccache_dir failed.");
+
+    check_dir(subdir, uid, gid, 0700);
+    RMDIR(subdir);
+    check_dir(dirname, 0, 0, 0755);
+    RMDIR(dirname);
+}
+END_TEST
+
+START_TEST(test_private_ccache_dir_in_user_dir)
+{
+    int ret;
+    char *cwd;
+    char *user_dir;
+    char *dn1;
+    char *dn2;
+    char *dn3;
+    char *filename;
+    uid_t uid = getuid();
+    gid_t gid = getgid();
+
+    if (uid == 0) {
+        uid = 12345;
+        gid = 12345;
+    }
+
+    cwd = getcwd(NULL, 0);
+    fail_unless(cwd != NULL, "getcwd failed.");
+
+    user_dir = talloc_asprintf(tmp_ctx, "%s/%s/user", cwd, TESTS_PATH);
+    free(cwd);
+    fail_unless(user_dir != NULL, "talloc_asprintf failed.");
+    ret = mkdir(user_dir, 0700);
+    fail_unless(ret == EOK, "mkdir failed.");
+    ret = chown(user_dir, uid, gid);
+    fail_unless(ret == EOK, "chown failed.");
+
+    dn1 = talloc_asprintf(tmp_ctx, "%s/a", user_dir);
+    fail_unless(dn1 != NULL, "talloc_asprintf failed.");
+    dn2 = talloc_asprintf(tmp_ctx, "%s/b", dn1);
+    fail_unless(dn2 != NULL, "talloc_asprintf failed.");
+    dn3 = talloc_asprintf(tmp_ctx, "%s/c", dn2);
+    fail_unless(dn3 != NULL, "talloc_asprintf failed.");
+    filename = talloc_asprintf(tmp_ctx, "%s/ccfile", dn3);
+    fail_unless(filename != NULL, "talloc_asprintf failed.");
+
+    ret = chmod(user_dir, 0600);
+    fail_unless(ret == EOK, "chmod failed.");
+    ret = create_ccache_dir(tmp_ctx, filename, NULL, uid, gid, true);
+    fail_unless(ret == EINVAL, "create_ccache_dir does not return EINVAL "
+                               "while x-bit is missing.");
+
+    ret = chmod(user_dir, 0700);
+    fail_unless(ret == EOK, "chmod failed.");
+    ret = create_ccache_dir(tmp_ctx, filename, NULL, uid, gid, true);
+    fail_unless(ret == EOK, "create_ccache_dir failed.");
+
+    check_dir(dn3, uid, gid, 0700);
+    RMDIR(dn3);
+    check_dir(dn2, uid, gid, 0700);
+    RMDIR(dn2);
+    check_dir(dn1, uid, gid, 0700);
+    RMDIR(dn1);
+    RMDIR(user_dir);
+}
+END_TEST
+
+START_TEST(test_private_ccache_dir_in_wrong_user_dir)
+{
+    int ret;
+    char *cwd;
+    char *dirname;
+    char *subdirname;
+    char *filename;
+
+    fail_unless(getuid() == 0, "This test must be run as root.");
+
+    cwd = getcwd(NULL, 0);
+    fail_unless(cwd != NULL, "getcwd failed.");
+
+    dirname = talloc_asprintf(tmp_ctx, "%s/%s/priv_ccdir", cwd, TESTS_PATH);
+    free(cwd);
+    fail_unless(dirname != NULL, "talloc_asprintf failed.");
+    ret = mkdir(dirname, 0700);
+    fail_unless(ret == EOK, "mkdir failed.\n");
+    ret = chown(dirname, 12346, 12346);
+    fail_unless(ret == EOK, "chown failed.\n");
+    subdirname = talloc_asprintf(tmp_ctx, "%s/subdir", dirname);
+    fail_unless(subdirname != NULL, "talloc_asprintf failed.");
+    filename = talloc_asprintf(tmp_ctx, "%s/ccfile", subdirname);
+    fail_unless(filename != NULL, "talloc_asprintf failed.");
+
+    ret = create_ccache_dir(tmp_ctx, filename, NULL, 12345, 12345, true);
+    fail_unless(ret == EINVAL, "Creating private ccache dir in wrong user "
+                               "dir does not failed with EINVAL.");
+
+    RMDIR(dirname);
+}
+END_TEST
+
+START_TEST(test_illegal_patterns)
+{
+    int ret;
+    char *cwd;
+    char *dirname;
+    char *filename;
+    uid_t uid = getuid();
+    gid_t gid = getgid();
+    pcre *illegal_re;
+    const char *errstr;
+    int errval;
+    int errpos;
+
+    illegal_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0,
+                               &errval, &errstr, &errpos, NULL);
+    fail_unless(illegal_re != NULL, "Invalid Regular Expression pattern at "
+                                    " position %d. (Error: %d [%s])\n",
+                                    errpos, errval, errstr);
+
+    cwd = getcwd(NULL, 0);
+    fail_unless(cwd != NULL, "getcwd failed.");
+
+    dirname = talloc_asprintf(tmp_ctx, "%s/%s/priv_ccdir", cwd, TESTS_PATH);
+    free(cwd);
+    fail_unless(dirname != NULL, "talloc_asprintf failed.");
+
+
+    filename = talloc_asprintf(tmp_ctx, "abc/./ccfile");
+    fail_unless(filename != NULL, "talloc_asprintf failed.");
+    ret = create_ccache_dir(tmp_ctx, filename, illegal_re, uid, gid, true);
+    fail_unless(ret == EINVAL, "create_ccache_dir allowed relative path [%s].",
+                               filename);
+
+    filename = talloc_asprintf(tmp_ctx, "%s/abc/./ccfile", dirname);
+    fail_unless(filename != NULL, "talloc_asprintf failed.");
+    ret = create_ccache_dir(tmp_ctx, filename, illegal_re, uid, gid, true);
+    fail_unless(ret == EINVAL, "create_ccache_dir allowed "
+                               "illegal pattern '/./' in filename [%s].",
+                               filename);
+
+    filename = talloc_asprintf(tmp_ctx, "%s/abc/../ccfile", dirname);
+    fail_unless(filename != NULL, "talloc_asprintf failed.");
+    ret = create_ccache_dir(tmp_ctx, filename, illegal_re, uid, gid, true);
+    fail_unless(ret == EINVAL, "create_ccache_dir allowed "
+                               "illegal pattern '/../' in filename [%s].",
+                               filename);
+
+    filename = talloc_asprintf(tmp_ctx, "%s/abc//ccfile", dirname);
+    fail_unless(filename != NULL, "talloc_asprintf failed.");
+    ret = create_ccache_dir(tmp_ctx, filename, illegal_re, uid, gid, true);
+    fail_unless(ret == EINVAL, "create_ccache_dir allowed "
+                               "illegal pattern '//' in filename [%s].",
+                               filename);
+
+}
+END_TEST
+
 void setup_talloc_context(void)
 {
     int ret;
@@ -97,138 +410,118 @@ void free_talloc_context(void)
     fail_unless(ret == 0, "Connot free talloc context.");
 }
 
-START_TEST(test_multiple_substitutions)
+static void do_test(const char *file_template, const char *dir_template,
+                    const char *expected, const bool expected_private_path)
 {
-    const char *test_template = BASE"_%u_%U_%u";
-    const char *expected = BASE"_"USERNAME"_"UID"_"USERNAME;
     char *result;
+    int ret;
+    bool private_path = false;
 
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
+    ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCACHEDIR, dir_template);
+    fail_unless(ret == EOK, "Failed to set Ccache dir");
 
-    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
+    result = expand_ccname_template(tmp_ctx, kr, file_template, true,
+                                    &private_path);
+
+    fail_unless(result != NULL, "Cannot expand template [%s].", file_template);
     fail_unless(strcmp(result, expected) == 0,
                 "Expansion failed, result [%s], expected [%s].",
                 result, expected);
+    fail_unless(private_path == expected_private_path,
+                "Unexprected private path, get [%s], expected [%s].",
+                private_path ? "true" : "false",
+                expected_private_path ? "true" : "false");
+}
+
+START_TEST(test_multiple_substitutions)
+{
+    do_test(BASE"_%u_%U_%u", CCACHE_DIR, BASE"_"USERNAME"_"UID"_"USERNAME, false);
+    do_test("%d/"FILENAME, BASE"_%u_%U_%u",
+            BASE"_"USERNAME"_"UID"_"USERNAME"/"FILENAME, true);
 }
 END_TEST
 
 START_TEST(test_username)
 {
-    const char *test_template = BASE"_%u";
-    const char *expected = BASE"_"USERNAME;
-    char *result;
-
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
-
-    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
-    fail_unless(strcmp(result, expected) == 0,
-                "Expansion failed, result [%s], expected [%s].",
-                result, expected);
+    do_test(BASE"_%u", CCACHE_DIR, BASE"_"USERNAME, false);
+    do_test("%d/"FILENAME, BASE"_%u", BASE"_"USERNAME"/"FILENAME, true);
 }
 END_TEST
 
 START_TEST(test_uid)
 {
-    const char *test_template = BASE"_%U";
-    const char *expected = BASE"_"UID;
-    char *result;
-
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
-
-    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
-    fail_unless(strcmp(result, expected) == 0,
-                "Expansion failed, result [%s], expected [%s].",
-                result, expected);
+    do_test(BASE"_%U", CCACHE_DIR, BASE"_"UID, false);
+    do_test("%d/"FILENAME, BASE"_%U", BASE"_"UID"/"FILENAME, true);
 }
 END_TEST
 
 START_TEST(test_upn)
 {
-    const char *test_template = BASE"_%p";
-    const char *expected = BASE"_"PRINCIPLE_NAME;
-    char *result;
-
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
-
-    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
-    fail_unless(strcmp(result, expected) == 0,
-                "Expansion failed, result [%s], expected [%s].",
-                result, expected);
+    do_test(BASE"_%p", CCACHE_DIR, BASE"_"PRINCIPLE_NAME, false);
+    do_test("%d/"FILENAME, BASE"_%p", BASE"_"PRINCIPLE_NAME"/"FILENAME, true);
 }
 END_TEST
 
 START_TEST(test_realm)
 {
-    const char *test_template = BASE"_%r";
-    const char *expected = BASE"_"REALM;
-    char *result;
-
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
-
-    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
-    fail_unless(strcmp(result, expected) == 0,
-                "Expansion failed, result [%s], expected [%s].",
-                result, expected);
+    do_test(BASE"_%r", CCACHE_DIR, BASE"_"REALM, false);
+    do_test("%d/"FILENAME, BASE"_%r", BASE"_"REALM"/"FILENAME, false);
 }
 END_TEST
 
 START_TEST(test_home)
 {
-    const char *test_template = BASE"_%h";
-    const char *expected = BASE"_"HOME_DIRECTORY;
-    char *result;
-
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
-
-    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
-    fail_unless(strcmp(result, expected) == 0,
-                "Expansion failed, result [%s], expected [%s].",
-                result, expected);
+    do_test(BASE"_%h", CCACHE_DIR, BASE"_"HOME_DIRECTORY, false);
+    do_test("%d/"FILENAME, BASE"_%h", BASE"_"HOME_DIRECTORY"/"FILENAME, true);
 }
 END_TEST
 
 START_TEST(test_ccache_dir)
 {
-    const char *test_template = BASE"_%d";
-    const char *expected = BASE"_"CCACHE_DIR;
     char *result;
+    int ret;
+    bool private_path = false;
 
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
+    do_test(BASE"_%d", CCACHE_DIR, BASE"_"CCACHE_DIR, false);
 
-    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
-    fail_unless(strcmp(result, expected) == 0,
-                "Expansion failed, result [%s], expected [%s].",
-                result, expected);
+    ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCACHEDIR, BASE"_%d");
+    fail_unless(ret == EOK, "Failed to set Ccache dir");
+
+    result = expand_ccname_template(tmp_ctx, kr, "%d/"FILENAME, true,
+                                    &private_path);
+
+    fail_unless(result == NULL, "Using %%d in ccache dir should fail.");
+    fail_unless(private_path == false,
+                "Unexprected private path, get [%s], expected [%s].",
+                private_path ? "true" : "false", "false");
 }
 END_TEST
 
 START_TEST(test_pid)
 {
-    const char *test_template = BASE"_%P";
-    const char *expected = BASE"_"PID;
     char *result;
+    int ret;
+    bool private_path = false;
 
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
+    do_test(BASE"_%P", CCACHE_DIR, BASE"_"PID, false);
 
-    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
-    fail_unless(strcmp(result, expected) == 0,
-                "Expansion failed, result [%s], expected [%s].",
-                result, expected);
+    ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCACHEDIR, BASE"_%P");
+    fail_unless(ret == EOK, "Failed to set Ccache dir");
+
+    result = expand_ccname_template(tmp_ctx, kr, "%d/"FILENAME, true,
+                                    &private_path);
+
+    fail_unless(result == NULL, "Using %%P in ccache dir should fail.");
+    fail_unless(private_path == false,
+                "Unexprected private path, get [%s], expected [%s].",
+                private_path ? "true" : "false", "false");
 }
 END_TEST
 
 START_TEST(test_percent)
 {
-    const char *test_template = BASE"_%%";
-    const char *expected = BASE"_%";
-    char *result;
-
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
-
-    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
-    fail_unless(strcmp(result, expected) == 0,
-                "Expansion failed, result [%s], expected [%s].",
-                result, expected);
+    do_test(BASE"_%%", CCACHE_DIR, BASE"_%", false);
+    do_test("%d/"FILENAME, BASE"_%%", BASE"_%/"FILENAME, false);
 }
 END_TEST
 
@@ -236,11 +529,26 @@ START_TEST(test_unknow_template)
 {
     const char *test_template = BASE"_%X";
     char *result;
+    int ret;
+    bool private_path = false;
 
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
+    result = expand_ccname_template(tmp_ctx, kr, test_template, true,
+                                    &private_path);
 
     fail_unless(result == NULL, "Unknown template [%s] should fail.",
                 test_template);
+
+    ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCACHEDIR, BASE"_%X");
+    fail_unless(ret == EOK, "Failed to set Ccache dir");
+    test_template = "%d/"FILENAME;
+    result = expand_ccname_template(tmp_ctx, kr, test_template, true,
+                                    &private_path);
+
+    fail_unless(result == NULL, "Unknown template [%s] should fail.",
+                test_template);
+    fail_unless(private_path == false,
+                "Unexprected private path, get [%s], expected [%s].",
+                private_path ? "true" : "false", "false");
 }
 END_TEST
 
@@ -248,11 +556,16 @@ START_TEST(test_NULL)
 {
     char *test_template = NULL;
     char *result;
+    bool private_path = false;
 
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
+    result = expand_ccname_template(tmp_ctx, kr, test_template, true,
+                                    &private_path);
 
     fail_unless(result == NULL, "Expected NULL as a result for an empty input.",
                 test_template);
+    fail_unless(private_path == false,
+                "Unexprected private path, get [%s], expected [%s].",
+                private_path ? "true" : "false", "false");
 }
 END_TEST
 
@@ -260,13 +573,18 @@ START_TEST(test_no_substitution)
 {
     const char *test_template = BASE;
     char *result;
+    bool private_path = false;
 
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
+    result = expand_ccname_template(tmp_ctx, kr, test_template, true,
+                                    &private_path);
 
     fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
     fail_unless(strcmp(result, test_template) == 0,
                 "Expansion failed, result [%s], expected [%s].",
                 result, test_template);
+    fail_unless(private_path == false,
+                "Unexprected private path, get [%s], expected [%s].",
+                private_path ? "true" : "false", "false");
 }
 END_TEST
 
@@ -291,21 +609,81 @@ Suite *krb5_utils_suite (void)
     tcase_add_test (tc_ccname_template, test_multiple_substitutions);
     suite_add_tcase (s, tc_ccname_template);
 
+    TCase *tc_create_dir = tcase_create("create_dir");
+    tcase_add_checked_fixture (tc_create_dir, setup_create_dir,
+                               teardown_create_dir);
+    tcase_add_test (tc_create_dir, test_illegal_patterns);
+    if (getuid() == 0) {
+        tcase_add_test (tc_create_dir, test_priv_ccache_dir);
+        tcase_add_test (tc_create_dir, test_private_ccache_dir_in_user_dir);
+        tcase_add_test (tc_create_dir, test_pub_ccache_dir);
+        tcase_add_test (tc_create_dir, test_pub_ccache_dir_in_user_dir);
+        tcase_add_test (tc_create_dir, test_private_ccache_dir_in_wrong_user_dir);
+    } else {
+        printf("Run as root to enable more tests.\n");
+    }
+    suite_add_tcase (s, tc_create_dir);
+
     return s;
 }
 
-int main(void)
+int main(int argc, const char *argv[])
 {
-  int number_failed;
+    int ret;
+    int opt;
+    poptContext pc;
+    int number_failed;
+
+    tests_set_cwd();
+
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_MAIN_OPTS
+        { NULL }
+    };
+
+    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);
+
 
-  tests_set_cwd();
+    ret = mkdir(TESTS_PATH, 0775);
+    if (ret != EOK) {
+        fprintf(stderr, "Could not create empty directory [%s]. ", TESTS_PATH);
+        if (errno == EEXIST) {
+            fprintf(stderr, "Please remove [%s].\n", TESTS_PATH);
+        } else {
+            fprintf(stderr, "[%d][%s].\n", errno, strerror(errno));
+        }
+
+        return 1;
+    }
+
+    Suite *s = krb5_utils_suite ();
+    SRunner *sr = srunner_create (s);
+    /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */
+    srunner_run_all(sr, CK_ENV);
+    number_failed = srunner_ntests_failed (sr);
+    srunner_free (sr);
+    if (number_failed == 0) {
+        ret = rmdir(TESTS_PATH);
+        if (ret != EOK) {
+            fprintf(stderr, "Cannot remove [%s]: [%d][%s].\n", TESTS_PATH,
+                            errno, strerror(errno));
+            return EXIT_FAILURE;
+        }
+
+        return EXIT_SUCCESS;
+    }
 
-  Suite *s = krb5_utils_suite ();
-  SRunner *sr = srunner_create (s);
-  /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */
-  srunner_run_all(sr, CK_ENV);
-  number_failed = srunner_ntests_failed (sr);
-  srunner_free (sr);
-  return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+    return EXIT_FAILURE;
 }
 
-- 
1.6.6.1



More information about the sssd-devel mailing list