diff options
-rw-r--r-- | src/man/sssd-krb5.5.xml | 12 | ||||
-rw-r--r-- | src/providers/krb5/krb5_auth.c | 13 | ||||
-rw-r--r-- | src/providers/krb5/krb5_auth.h | 6 | ||||
-rw-r--r-- | src/providers/krb5/krb5_common.c | 13 | ||||
-rw-r--r-- | src/providers/krb5/krb5_init.c | 12 | ||||
-rw-r--r-- | src/providers/krb5/krb5_utils.c | 263 | ||||
-rw-r--r-- | src/providers/krb5/krb5_utils.h | 6 | ||||
-rw-r--r-- | 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 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; } |