From 29752834fbf3a19e4e117668abfce4e4c7c48ee4 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 23 Feb 2010 09:43:51 +0100 Subject: 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/providers/krb5/krb5_auth.c | 13 +- src/providers/krb5/krb5_auth.h | 6 + src/providers/krb5/krb5_common.c | 13 -- src/providers/krb5/krb5_init.c | 12 ++ src/providers/krb5/krb5_utils.c | 263 ++++++++++++++++++++++++++++++++++++++- src/providers/krb5/krb5_utils.h | 6 +- 6 files changed, 292 insertions(+), 21 deletions(-) (limited to 'src/providers') diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c index 5be42a81..b8b498a0 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 3e11f270..825f3d64 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 + #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 86676f44..7619e6a5 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 43cbc1bc..4d212381 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 a75ad782..83971abf 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 7637041a..be162bc7 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__ */ -- cgit