summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/man/sssd-krb5.5.xml12
-rw-r--r--src/providers/krb5/krb5_auth.c13
-rw-r--r--src/providers/krb5/krb5_auth.h6
-rw-r--r--src/providers/krb5/krb5_common.c13
-rw-r--r--src/providers/krb5/krb5_init.c12
-rw-r--r--src/providers/krb5/krb5_utils.c263
-rw-r--r--src/providers/krb5/krb5_utils.h6
-rw-r--r--src/tests/krb5_utils-tests.c562
8 files changed, 773 insertions, 114 deletions
diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index 32b6c293d..ca4dae256 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 5be42a813..b8b498a05 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 3e11f2702..825f3d648 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 86676f44c..7619e6a55 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 43cbc1bc7..4d2123815 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 a75ad782b..83971abfb 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 7637041a6..be162bc72 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 64cf9c3d3..3e7c2ec74 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;
}