On Wed, Feb 24, 2010 at 09:56:47AM -0500, Simo Sorce wrote:
> On Wed, 24 Feb 2010 12:05:18 +0100
> Sumit Bose <sb...@redhat.com> wrote:
> 
> > +    old_umask = umask(0000);
> > +    ret = mkdir(dirname, 01777);
> > +    umask(old_umask);
> > +    if (ret != EOK) {
> > +        DEBUG(1, ("mkdir [%s] failed: [%d][%s].\n", dirname, errno,
> > +                  strerror(errno)));
> > +        return errno;
> > +    }
> > +
> 
> NACK.
> We do not want to give every user on the system freedom to write what
> they want in these directories.
> 
> All parents need to be root.root 0755
> 
> If the admins wants it differently they will have to pre-create the
> parent directories themselves.
> 
> Simo.

New version attached.

Additionally to the issues mentions above the templates are only parsed
once and the expanded path is checked for suspicious patterns like
'/../'. If a private directory is created it is chown'd immediately
after its creation.

There are two questions where I do not know a good answer:

- The parent directories are always created with 0755 permissions and
  the owner is root. Should we handle the case where the first existing
  directory does not belong to root, e.g. if we have
  /home/u1/a/b/c/ccache_dir/ccfile and a, b and c have to be created?

- The sequence '%P' is expanded to the client's PID. I think the PID
  does not qualify to be unique for a user and consequently a ccache
  directory with only this sequence and no other user specific sequences
  like '%u' is created as public directory. Do you agree?

bye,
Sumit
From 8eab929b3fe38b33f31d22bce87f1a7b96db21c8 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@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   |   19 ++-
 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  |  144 +++++++++++++-
 src/providers/krb5/krb5_utils.h  |    8 +-
 src/tests/krb5_utils-tests.c     |  407 +++++++++++++++++++++++++++++---------
 8 files changed, 505 insertions(+), 116 deletions(-)

diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index 32b6c29..5a1d626 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. 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 0e5230c..fde3b93 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -880,6 +880,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);
@@ -915,12 +916,26 @@ 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;
             }
+
+            if (private_path) {
+                ret = create_ccache_dir(kr, kr->ccname,
+                                        kr->krb5_ctx->illegal_path_re,
+                                        kr->uid, kr->gid, 0700);
+            } else {
+                ret = create_ccache_dir(kr, kr->ccname,
+                                        kr->krb5_ctx->illegal_path_re,
+                                        0, 0, 01777);
+            }
+            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..d5d104b 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 expand_ccache_dir,
+                             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 (!expand_ccache_dir) *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 (!expand_ccache_dir) *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 (!expand_ccache_dir) *private_path = true;
                 break;
             case '%':
                 result = talloc_asprintf_append(result, "%s%%", p);
@@ -108,14 +115,29 @@ 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 (!expand_ccache_dir) *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 (expand_ccache_dir) {
+                    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 (kr->pd->cli_pid == 0) {
@@ -143,3 +165,115 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct 
krb5child_req *kr,
 
     return result;
 }
+
+errno_t create_dir_with_parents(TALLOC_CTX *mem_ctx, const char *dirname,
+                                uid_t uid, gid_t gid, mode_t mode)
+{
+    int ret;
+    struct stat stat_buf;
+    char *parent;
+    char *end;
+    mode_t old_umask;
+
+    ret = lstat(dirname, &stat_buf);
+    if (ret == EOK) {
+        if ( !S_ISDIR(stat_buf.st_mode) ) {
+            DEBUG(1, ("[%s] is not a directory.\n", dirname));
+            return EINVAL;
+        }
+        return EOK;
+    } else {
+        if (errno != ENOENT) {
+            DEBUG(1, ("lstat for [%s] failed: [%d][%s].\n", dirname, errno,
+                      strerror(errno)));
+            return errno;
+        }
+    }
+
+    parent = talloc_strdup(mem_ctx, dirname);
+    end = strrchr(parent, '/');
+    if (end == NULL || end == parent) {
+        DEBUG(1, ("Cannot find parent directory of [%s], / is not allowed.\n",
+                   dirname));
+        return EINVAL;
+    }
+    *end = '\0';
+
+    ret = create_dir_with_parents(mem_ctx, parent, 0, 0, 0755);
+    if (ret != EOK) {
+        DEBUG(7, ("create_dir_with_parents failed.\n"));
+        return ret;
+    }
+
+    old_umask = umask(0000);
+    ret = mkdir(dirname, mode);
+    umask(old_umask);
+    if (ret != EOK) {
+        DEBUG(1, ("mkdir [%s] failed: [%d][%s].\n", dirname, errno,
+                  strerror(errno)));
+        return errno;
+    }
+    if (uid != 0) {
+        ret = chown(dirname, uid, gid);
+        if (ret != EOK) {
+            DEBUG(1, ("chown failed [%d][%s].\n", errno, strerror(errno)));
+            return errno;
+        }
+    }
+
+    return EOK;
+}
+
+errno_t create_ccache_dir(TALLOC_CTX *mem_ctx, const char *filename,
+                          pcre *illegal_re, uid_t uid, gid_t gid, mode_t mode)
+{
+    int ret;
+    char *dirname;
+    char *end;
+
+    dirname = talloc_strdup(mem_ctx, filename);
+    if (dirname == NULL) {
+        DEBUG(1, ("talloc_strndup failed.\n"));
+        return ENOMEM;
+    }
+
+    if (strncmp(dirname, "FILE:", 5) == 0) {
+        dirname = dirname + 5;
+    }
+
+    if (*dirname != '/') {
+        DEBUG(1, ("Only absolute paths are allowed, not [%s] .\n", dirname));
+        return EINVAL;
+    }
+
+    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));
+            return EINVAL;
+        } 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));
+            return EFAULT;
+        }
+    }
+
+    end = strrchr(dirname, '/');
+    if (end == NULL || end == dirname) {
+        DEBUG(1, ("Missing filename in [%s].\n", dirname));
+        return EINVAL;
+    }
+    *end = '\0';
+
+    ret = create_dir_with_parents(mem_ctx, dirname, uid, gid, mode);
+    if (ret != EOK) {
+        DEBUG(1, ("create_dir_with_parents failed.\n"));
+        return ret;
+    }
+
+    return EOK;
+}
diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h
index 7637041..e158cb8 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -32,8 +32,14 @@
 #include "providers/data_provider.h"
 
 char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
-                             const char *template);
+                             const char *template, bool expand_ccache_dir,
+                             bool *private_path);
 
 errno_t become_user(uid_t uid, gid_t gid);
 
+errno_t create_dir_with_parents(TALLOC_CTX *mem_ctx, const char *dirname,
+                                uid_t uid, gid_t gid, mode_t mode);
+
+errno_t create_ccache_dir(TALLOC_CTX *mem_ctx, const char *filename,
+                          pcre *illegal_re, uid_t uid, gid_t gid, mode_t mode);
 #endif /* __KRB5_UTILS_H__ */
diff --git a/src/tests/krb5_utils-tests.c b/src/tests/krb5_utils-tests.c
index 362daf4..8133e8e 100644
--- a/src/tests/krb5_utils-tests.c
+++ b/src/tests/krb5_utils-tests.c
@@ -23,12 +23,16 @@
 */
 
 #include <stdlib.h>
+#include <popt.h>
 #include <check.h>
 
 #include "providers/krb5/krb5_utils.h"
 #include "providers/krb5/krb5_auth.h"
 
+#define TESTS_PATH "tests_krb5_utils"
+
 #define BASE "/abc/def"
+#define FILENAME "ghi"
 
 #define USERNAME "testuser"
 #define UID "12345"
@@ -43,6 +47,174 @@ 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 does not match, "
+                                           "expected [%o], got [%o].",
+                                            mode, (stat_buf.st_mode & 
~S_IFMT));
+}
+
+START_TEST(test_mkdir_simple)
+{
+    int ret;
+
+    ret = create_dir_with_parents(tmp_ctx, TESTS_PATH"/abc",
+                                  getuid(), getgid(), 01777);
+    fail_unless(ret == EOK, "create_dir_with_parents failed.");
+    check_dir(TESTS_PATH"/abc", getuid(), getgid(), 01777);
+
+    RMDIR(TESTS_PATH"/abc");
+}
+END_TEST
+
+START_TEST(test_mkdir_parents)
+{
+    int ret;
+
+    ret = create_dir_with_parents(tmp_ctx, TESTS_PATH"/a/b/c",
+                                  getuid(), getgid(), 01777);
+    fail_unless(ret == EOK, "create_dir_with_parents failed.");
+    check_dir(TESTS_PATH"/a/b/c", getuid(), getgid(), 01777);
+    check_dir(TESTS_PATH"/a/b", getuid(), getgid(), 0755);
+    check_dir(TESTS_PATH"/a", getuid(), getgid(), 0755);
+    RMDIR(TESTS_PATH"/a/b/c");
+    RMDIR(TESTS_PATH"/a/b");
+    RMDIR(TESTS_PATH"/a");
+}
+END_TEST
+
+START_TEST(test_mkdir_nonexisting_parent)
+{
+    int ret;
+
+    ret = create_dir_with_parents(tmp_ctx, "/a/b/c", getuid(), getgid(), 0777);
+    fail_unless(ret == EINVAL, "create_dir_with_parents did not return 
EINVAL.");
+}
+END_TEST
+
+START_TEST(test_priv_ccache_dir)
+{
+    int ret;
+    char *cwd;
+    char *dirname;
+    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.");
+
+    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, "%s/ccfile", dirname);
+    fail_unless(filename != NULL, "talloc_asprintf failed.");
+
+    ret = create_ccache_dir(tmp_ctx, filename, NULL, uid, gid, 0700);
+    fail_unless(ret == EOK, "create_ccache_dir failed.");
+
+    check_dir(dirname, uid, gid, 0700);
+    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, 0700);
+    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, 0700);
+    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, 0700);
+    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, 0700);
+    fail_unless(ret == EINVAL, "create_ccache_dir allowed "
+                               "illegal pattern '//' in filename [%s].",
+                               filename);
+
+}
+END_TEST
+
 void setup_talloc_context(void)
 {
     int ret;
@@ -96,138 +268,104 @@ 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;
+
+    ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCACHEDIR, dir_template);
+    fail_unless(ret == EOK, "Failed to set Ccache dir");
 
-    result = expand_ccname_template(tmp_ctx, kr, test_template);
+    result = expand_ccname_template(tmp_ctx, kr, file_template, true,
+                                    &private_path);
 
-    fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
+    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;
-
-    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"_"PID, false);
+    do_test("%d/"FILENAME, BASE"_%P", BASE"_"PID"/"FILENAME, 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
 
@@ -235,11 +373,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
 
@@ -247,11 +400,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
 
@@ -259,13 +417,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
 
@@ -290,18 +453,74 @@ 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_mkdir_simple);
+    tcase_add_test (tc_create_dir, test_mkdir_parents);
+    tcase_add_test (tc_create_dir, test_mkdir_nonexisting_parent);
+    tcase_add_test (tc_create_dir, test_priv_ccache_dir);
+    tcase_add_test (tc_create_dir, test_illegal_patterns);
+    suite_add_tcase (s, tc_create_dir);
+
     return s;
 }
 
-int main(void)
+int main(int argc, const char *argv[])
 {
-  int number_failed;
-  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;
+    int ret;
+    int opt;
+    poptContext pc;
+    int number_failed;
+
+    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);
+
+
+    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;
+    }
+
+    return EXIT_FAILURE;
 }
 
-- 
1.6.6.1

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

Reply via email to