From 9a3ba9ca00e73adc3fb17ce8afa532076768023b Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Tue, 5 Jun 2012 23:41:12 +0200 Subject: Add support for storing credential caches in the DIR: back end https://fedorahosted.org/sssd/ticket/974 --- src/man/sssd-krb5.5.xml | 10 +- src/providers/krb5/krb5_child.c | 228 +++++++++++++++++++++++++---- src/providers/krb5/krb5_common.c | 34 ++++- src/providers/krb5/krb5_opts.h | 4 +- src/providers/krb5/krb5_utils.c | 307 ++++++++++++++++++++++++++++++++++----- src/providers/krb5/krb5_utils.h | 7 + src/tests/krb5_child-test.c | 46 ++++-- src/tests/krb5_utils-tests.c | 66 ++++++++- src/util/sss_krb5.c | 21 +-- src/util/sss_krb5.h | 3 +- 10 files changed, 615 insertions(+), 111 deletions(-) diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml index 247337a90..1baee36e9 100644 --- a/src/man/sssd-krb5.5.xml +++ b/src/man/sssd-krb5.5.xml @@ -157,10 +157,12 @@ krb5_ccname_template (string) - Location of the user's credential cache. Currently - only file based credential caches are supported. In - the template the following sequences are - substituted: + Location of the user's credential cache. Two credential + cache types are currently supported - FILE + and DIR. The cache can either be specified + as TYPE:RESIDUAL, or an absolute + path, which implies the FILE type. In the + template the following sequences are substituted: %u diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index 195c431af..6b8722a8a 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -221,6 +221,51 @@ done: return kerr; } +static krb5_error_code +store_creds_in_ccache(krb5_context ctx, krb5_principal princ, + krb5_ccache cc, krb5_creds *creds) +{ + krb5_error_code kerr; + krb5_creds *l_cred; + + kerr = krb5_cc_initialize(ctx, cc, princ); + if (kerr != 0) { + KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr); + goto done; + } + + if (creds == NULL) { + kerr = create_empty_cred(ctx, princ, &l_cred); + if (kerr != 0) { + KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr); + goto done; + } + } else { + l_cred = creds; + } + + kerr = krb5_cc_store_cred(ctx, cc, l_cred); + if (kerr != 0) { + KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr); + goto done; + } + + kerr = krb5_cc_switch(ctx, cc); + if (kerr != 0) { + KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr); + goto done; + } + + kerr = krb5_cc_close(ctx, cc); + if (kerr != 0) { + KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr); + goto done; + } + +done: + return kerr; +} + static krb5_error_code create_ccache_file(krb5_context ctx, krb5_principal princ, char *ccname, krb5_creds *creds) @@ -232,7 +277,6 @@ static krb5_error_code create_ccache_file(krb5_context ctx, size_t ccname_len; char *dummy; char *tmp_ccname; - krb5_creds *l_cred; TALLOC_CTX *tmp_ctx = NULL; mode_t old_umask; @@ -285,38 +329,16 @@ static krb5_error_code create_ccache_file(krb5_context ctx, goto done; } - kerr = krb5_cc_initialize(ctx, tmp_cc, princ); - if (kerr != 0) { - KRB5_DEBUG(1, kerr); - goto done; - } + kerr = store_creds_in_ccache(ctx, princ, tmp_cc, creds); if (fd != -1) { close(fd); fd = -1; } - - if (creds == NULL) { - kerr = create_empty_cred(ctx, princ, &l_cred); - if (kerr != 0) { - KRB5_DEBUG(1, kerr); - goto done; - } - } else { - l_cred = creds; - } - - kerr = krb5_cc_store_cred(ctx, tmp_cc, l_cred); if (kerr != 0) { KRB5_DEBUG(1, kerr); goto done; } - kerr = krb5_cc_close(ctx, tmp_cc); - if (kerr != 0) { - KRB5_DEBUG(1, kerr); - goto done; - } - tmp_cc = NULL; ccname_len = strlen(cc_file_name); if (ccname_len >= 6 && strcmp(cc_file_name + (ccname_len-6), "XXXXXX")==0 ) { @@ -338,16 +360,161 @@ static krb5_error_code create_ccache_file(krb5_context ctx, DEBUG(SSSDBG_TRACE_LIBS, ("Created ccache file: [%s]\n", ccname)); done: + if (kerr != 0 && tmp_cc != NULL) { + krb5_cc_destroy(ctx, tmp_cc); + } + if (fd != -1) { close(fd); } + + talloc_free(tmp_ctx); + return kerr; +} + +static errno_t +create_ccdir(const char *dirname, uid_t uid, gid_t gid) +{ + mode_t old_umask; + struct stat statbuf; + errno_t ret; + + old_umask = umask(0000); + ret = mkdir(dirname, 0700); + umask(old_umask); + if (ret == -1) { + /* Failing the mkdir is only OK if the directory already + * exists AND it is owned by the same user and group and + * has the correct permissions. + */ + ret = errno; + if (ret == EEXIST) { + errno = 0; + ret = stat(dirname, &statbuf); + if (ret == -1) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + ("stat failed [%d]: %s\n", ret, strerror(ret))); + return EIO; + } + + if (statbuf.st_uid != uid || statbuf.st_gid != gid) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("The directory %s is owned by %d/%d, expected %d/%d\n", + dirname, statbuf.st_uid, statbuf.st_gid, uid, gid)); + return EACCES; + } + + if ((statbuf.st_mode & ~S_IFMT) != 0700) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("The directory %s has wrong permissions %o, expected 0700\n", + dirname, (statbuf.st_mode & ~S_IFMT))); + return EACCES; + } + } else { + DEBUG(SSSDBG_CRIT_FAILURE, ("mkdir [%s] failed [%d]: %s\n", + dirname, ret, strerror(ret))); + return ret; + } + } + + return EOK; +} + +static krb5_error_code +create_ccache_in_dir(uid_t uid, gid_t gid, + krb5_context ctx, + krb5_principal princ, + char *ccname, krb5_creds *creds) +{ + krb5_error_code kerr; + krb5_ccache tmp_cc = NULL; + const char *dirname; + + DEBUG(SSSDBG_FUNC_DATA, ("Creating ccache at [%s]\n", ccname)); + + dirname = sss_krb5_residual_check_type(ccname, SSS_KRB5_TYPE_DIR); + if (dirname == NULL) { + return EIO; + } + + kerr = become_user(uid, gid); + if (kerr != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("become_user failed.\n")); + goto done; + } + + if (dirname[0] == ':') { + /* Cache name in the form of DIR::filepath represents a single + * ccache in a collection that we are trying to reuse. + * See src/lib/krb5/ccache/cc_dir.c in the MIT Kerberos tree. + */ + kerr = krb5_cc_resolve(ctx, ccname, &tmp_cc); + if (kerr != 0) { + KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr); + goto done; + } + } else if (dirname[0] == '/') { + /* An absolute path denotes that krb5_child should create a new + * ccache. We can afford to just call mkdir(dirname) because we + * only want the last component to be created. + */ + + kerr = create_ccdir(dirname, uid, gid); + if (kerr) { + DEBUG(SSSDBG_OP_FAILURE, + ("Cannot create directory %s\n", dirname)); + goto done; + } + + kerr = krb5_cc_set_default_name(ctx, ccname); + if (kerr != 0) { + KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr); + goto done; + } + + kerr = krb5_cc_new_unique(ctx, "DIR", NULL, &tmp_cc); + if (kerr != 0) { + KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr); + goto done; + } + } else { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Wrong residual format for DIR in ccache %s\n", ccname)); + return EIO; + } + + kerr = store_creds_in_ccache(ctx, princ, tmp_cc, creds); + if (kerr != 0) { + KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr); + goto done; + } + +done: if (kerr != 0 && tmp_cc != NULL) { krb5_cc_destroy(ctx, tmp_cc); } + return kerr; +} - talloc_free(tmp_ctx); +static krb5_error_code +create_ccache(uid_t uid, gid_t gid, krb5_context ctx, + krb5_principal princ, char *ccname, krb5_creds *creds) +{ + enum sss_krb5_cc_type cctype; + + cctype = sss_krb5_get_type(ccname); + switch (cctype) { + case SSS_KRB5_TYPE_FILE: + return create_ccache_file(ctx, princ, ccname, creds); + case SSS_KRB5_TYPE_DIR: + return create_ccache_in_dir(uid, gid, ctx, princ, ccname, creds); + default: + DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown cache type\n")); + return EINVAL; + } - return kerr; + return EINVAL; /* Should never get here */ } static errno_t pack_response_packet(struct response *resp, int status, @@ -729,9 +896,9 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, } /* Use the updated principal in the creds in case canonicalized */ - kerr = create_ccache_file(kr->ctx, - kr->creds ? kr->creds->client : kr->princ, - kr->ccname, kr->creds); + kerr = create_ccache(kr->uid, kr->gid, kr->ctx, + kr->creds ? kr->creds->client : kr->princ, + kr->ccname, kr->creds); if (kerr != 0) { KRB5_DEBUG(1, kerr); goto done; @@ -1142,7 +1309,8 @@ static errno_t create_empty_ccache(int fd, struct krb5_req *kr) DEBUG(SSSDBG_TRACE_LIBS, ("Creating empty ccache\n")); - ret = create_ccache_file(kr->ctx, kr->princ, kr->ccname, NULL); + ret = create_ccache(kr->uid, kr->gid, kr->ctx, + kr->princ, kr->ccname, NULL); if (ret != 0) { KRB5_DEBUG(1, ret); pam_status = PAM_SYSTEM_ERR; diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c index 91816662b..f4033d295 100644 --- a/src/providers/krb5/krb5_common.c +++ b/src/providers/krb5/krb5_common.c @@ -182,13 +182,37 @@ errno_t check_and_export_options(struct dp_option *opts, } cc_be = sss_krb5_get_type(dummy); - if (cc_be != SSS_KRB5_TYPE_FILE || dummy[0] != '/') { - DEBUG(SSSDBG_CONF_SETTINGS, - ("Currently only file based credential caches are supported " - "and krb5ccname_template must start with '/' or 'FILE:'\n")); + switch (cc_be) { + case SSS_KRB5_TYPE_FILE: + DEBUG(SSSDBG_CONF_SETTINGS, ("ccache is of type FILE\n")); + krb5_ctx->cc_be = &file_cc; + if (dummy[0] != '/') { + /* FILE:/path/to/cc */ + break; + } + + DEBUG(SSSDBG_CONF_SETTINGS, ("The ccname template was " + "missing an explicit type, but is an absolute " + "path specifier. Assuming FILE:\n")); + + dummy = talloc_asprintf(opts, "FILE:%s", dummy); + if (!dummy) return ENOMEM; + + ret = dp_opt_set_string(opts, KRB5_CCNAME_TMPL, dummy); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("dp_opt_set_string failed.\n")); + return ret; + } + break; + case SSS_KRB5_TYPE_DIR: + DEBUG(SSSDBG_CONF_SETTINGS, ("ccache is of type DIR\n")); + krb5_ctx->cc_be = &dir_cc; + break; + default: + DEBUG(SSSDBG_OP_FAILURE, ("Unkown ccname database\n")); return EINVAL; + break; } - krb5_ctx->cc_be = &file_cc; return EOK; } diff --git a/src/providers/krb5/krb5_opts.h b/src/providers/krb5/krb5_opts.h index 7f47290c9..45dfec544 100644 --- a/src/providers/krb5/krb5_opts.h +++ b/src/providers/krb5/krb5_opts.h @@ -28,8 +28,8 @@ struct dp_option default_krb5_opts[] = { { "krb5_server", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING }, - { "krb5_ccachedir", DP_OPT_STRING, { "/tmp" }, NULL_STRING }, - { "krb5_ccname_template", DP_OPT_STRING, { "FILE:%d/krb5cc_%U_XXXXXX" }, NULL_STRING}, + { "krb5_ccachedir", DP_OPT_STRING, { "/run/user/%u" }, NULL_STRING }, + { "krb5_ccname_template", DP_OPT_STRING, { "DIR:%d" }, NULL_STRING}, { "krb5_auth_timeout", DP_OPT_NUMBER, { .number = 15 }, NULL_NUMBER }, { "krb5_keytab", DP_OPT_STRING, { "/etc/krb5.keytab" }, NULL_STRING }, { "krb5_validate", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c index 9f02fe332..35ece8117 100644 --- a/src/providers/krb5/krb5_utils.c +++ b/src/providers/krb5/krb5_utils.c @@ -23,6 +23,7 @@ */ #include #include +#include #include "providers/krb5/krb5_utils.h" #include "providers/krb5/krb5_auth.h" @@ -330,7 +331,7 @@ check_ccache_re(const char *filename, pcre *illegal_re) return EFAULT; } -static errno_t +errno_t create_ccache_dir(const char *dirname, pcre *illegal_re, uid_t uid, gid_t gid, bool private_path) { @@ -518,6 +519,42 @@ done: return EOK; } +static errno_t +create_ccache_dir_head(const char *parent, pcre *illegal_re, + uid_t uid, gid_t gid, bool private_path) +{ + char *dirname; + TALLOC_CTX *tmp_ctx = NULL; + char *end; + errno_t ret; + + dirname = talloc_strdup(tmp_ctx, parent); + if (dirname == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strdup failed.\n")); + ret = ENOMEM; + goto done; + } + + /* We'll remove all trailing slashes from the back so that + * we only pass /some/path to find_ccdir_parent_data, not + * /some/path/ */ + do { + end = strrchr(dirname, '/'); + if (end == NULL || end == dirname) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot find parent directory of [%s], " + "/ is not allowed.\n", dirname)); + ret = EINVAL; + goto done; + } + *end = '\0'; + } while (*(end+1) == '\0'); + + ret = create_ccache_dir(dirname, illegal_re, uid, gid, private_path); +done: + talloc_free(tmp_ctx); + return ret; +} + /*======== ccache back end utilities ========*/ struct sss_krb5_cc_be * get_cc_be_ops(enum sss_krb5_cc_type type) @@ -528,6 +565,9 @@ get_cc_be_ops(enum sss_krb5_cc_type type) case SSS_KRB5_TYPE_FILE: be = &file_cc; break; + case SSS_KRB5_TYPE_DIR: + be = &dir_cc; + break; case SSS_KRB5_TYPE_UNKNOWN: be = NULL; break; @@ -551,10 +591,6 @@ cc_file_create(const char *location, pcre *illegal_re, uid_t uid, gid_t gid, bool private_path) { const char *filename; - char *dirname; - char *end; - TALLOC_CTX *tmp_ctx = NULL; - errno_t ret; filename = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_FILE); if (filename == NULL) { @@ -562,31 +598,7 @@ cc_file_create(const char *location, pcre *illegal_re, return EINVAL; } - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new failed.\n")); - return ENOMEM; - } - - dirname = talloc_strdup(tmp_ctx, filename); - if (dirname == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strdup failed.\n")); - ret = ENOMEM; - goto done; - } - - end = strrchr(dirname, '/'); - if (end == NULL || end == dirname) { - DEBUG(SSSDBG_CRIT_FAILURE, ("Missing filename in [%s].\n", dirname)); - ret = EINVAL; - goto done; - } - *end = '\0'; - - ret = create_ccache_dir(dirname, illegal_re, uid, gid, private_path); -done: - talloc_free(tmp_ctx); - return ret; + return create_ccache_dir_head(filename, illegal_re, uid, gid, private_path); } static errno_t @@ -619,6 +631,9 @@ cc_residual_is_used(uid_t uid, const char *ccname, } switch (type) { + case SSS_KRB5_TYPE_DIR: + ret = S_ISDIR(stat_buf.st_mode); + break; case SSS_KRB5_TYPE_FILE: ret = S_ISREG(stat_buf.st_mode); break; @@ -661,6 +676,9 @@ cc_file_check_existing(const char *location, uid_t uid, bool active; bool valid; const char *filename; + krb5_ccache ccache = NULL; + krb5_context context = NULL; + krb5_error_code kerr; filename = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_FILE); if (!filename) { @@ -679,11 +697,26 @@ cc_file_check_existing(const char *location, uid_t uid, return ret; } - ret = check_for_valid_tgt(location, realm, princ, &valid); - if (ret != EOK) { + kerr = krb5_init_context(&context); + if (kerr != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to init kerberos context\n")); + return EIO; + } + + kerr = krb5_cc_resolve(context, location, &ccache); + if (kerr != 0) { + krb5_free_context(context); + DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_cc_resolve failed.\n")); + return EIO; + } + + kerr = check_for_valid_tgt(context, ccache, realm, princ, &valid); + krb5_free_context(context); + krb5_cc_close(context, ccache); + if (kerr != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("Could not check if ccache contains a valid principal\n")); - return ret; + return EIO; } *_active = active; @@ -735,3 +768,211 @@ struct sss_krb5_cc_be file_cc = { .ccache_for_princ = cc_file_cache_for_princ, .remove = cc_file_remove, }; + +/*======== Operations on the DIR: back end ========*/ +errno_t +cc_dir_create(const char *location, pcre *illegal_re, + uid_t uid, gid_t gid, bool private_path) +{ + const char *dirname; + + dirname = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_DIR); + if (dirname == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Bad residual type\n")); + return EINVAL; + } + + return create_ccache_dir_head(dirname, illegal_re, uid, gid, private_path); +} + +static krb5_error_code +get_ccache_for_princ(krb5_context context, const char *location, + const char *princ, krb5_ccache *_ccache) +{ + krb5_error_code krberr; + krb5_principal client_principal = NULL; + + krberr = krb5_cc_set_default_name(context, location); + if (krberr != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("krb5_cc_resolve failed.\n")); + return krberr; + } + + krberr = krb5_parse_name(context, princ, &client_principal); + if (krberr != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("krb5_parse_name failed.\n")); + return krberr; + } + + krberr = krb5_cc_cache_match(context, client_principal, _ccache); + krb5_free_principal(context, client_principal); + return krberr; +} + +errno_t +cc_dir_check_existing(const char *location, uid_t uid, + const char *realm, const char *princ, + bool *_active, bool *_valid) +{ + bool active = false; + bool valid = false; + krb5_ccache ccache = NULL; + krb5_context context = NULL; + krb5_error_code krberr; + enum sss_krb5_cc_type type; + const char *filename; + const char *dir; + char *tmp; + errno_t ret; + + type = sss_krb5_get_type(location); + if (type != SSS_KRB5_TYPE_DIR) { + DEBUG(SSSDBG_CRIT_FAILURE, ("%s is not of type DIR:\n", location)); + return EINVAL; + } + + filename = sss_krb5_cc_file_path(location); + if (!filename) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Existing ccname does not contain path into the collection")); + return EINVAL; + } + + if (filename[0] != '/') { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Only absolute path names are allowed.\n")); + return EINVAL; + } + + tmp = talloc_strdup(NULL, filename); + if (!tmp) return ENOMEM; + + dir = dirname(tmp); + if (!dir) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Cannot base get directory of %s\n", location)); + return EINVAL; + } + + ret = cc_residual_is_used(uid, dir, SSS_KRB5_TYPE_DIR, &active); + talloc_free(tmp); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not check if ccache is active\n")); + return ret; + } + + krberr = krb5_init_context(&context); + if (krberr) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to init kerberos context\n")); + return EIO; + } + + krberr = krb5_cc_resolve(context, location, &ccache); + if (krberr == KRB5_FCC_NOFILE || ccache == NULL) { + /* KRB5_FCC_NOFILE would be returned if the directory components + * of the DIR cache do not exist, which is the case in /run + * after a reboot + */ + DEBUG(SSSDBG_TRACE_FUNC, + ("ccache %s is missing or empty\n", location)); + valid = false; + ret = EOK; + goto done; + } else if (krberr != 0) { + KRB5_DEBUG(SSSDBG_OP_FAILURE, context, krberr); + DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_cc_resolve failed.\n")); + ret = EIO; + goto done; + } + + krberr = check_for_valid_tgt(context, ccache, realm, princ, &valid); + if (krberr != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Could not check if ccache contains a valid principal\n")); + ret = EIO; + goto done; + } + + ret = EOK; +done: + if (ccache) krb5_cc_close(context, ccache); + krb5_free_context(context); + *_active = active; + *_valid = valid; + return ret; +} + +const char * +cc_dir_cache_for_princ(TALLOC_CTX *mem_ctx, const char *location, + const char *princ) +{ + krb5_context context = NULL; + krb5_error_code krberr; + krb5_ccache ccache = NULL; + char *name; + const char *ccname; + + ccname = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_DIR); + if (!ccname) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot get ccname file from %s\n", + location)); + return NULL; + } + + /* ccname already points to a subsidiary cache */ + if (ccname[0] == ':' && ccname[1] && ccname[1] == '/') { + return talloc_strdup(mem_ctx, location); + } + + krberr = krb5_init_context(&context); + if (krberr) { + DEBUG(SSSDBG_OP_FAILURE, ("Failed to init kerberos context\n")); + return NULL; + } + + krberr = get_ccache_for_princ(context, location, princ, &ccache); + if (krberr) { + DEBUG(SSSDBG_TRACE_FUNC, ("No principal for %s in %s\n", + princ, location)); + krb5_free_context(context); + return NULL; + } + + krberr = krb5_cc_get_full_name(context, ccache, &name); + if (ccache) krb5_cc_close(context, ccache); + krb5_free_context(context); + if (krberr) { + DEBUG(SSSDBG_TRACE_FUNC, ("Could not get full name of ccache\n")); + return NULL; + } + + return talloc_strdup(mem_ctx, name); +} + +errno_t +cc_dir_remove(const char *location) +{ + const char *subsidiary; + + if (sss_krb5_get_type(location) != SSS_KRB5_TYPE_DIR) { + DEBUG(SSSDBG_CRIT_FAILURE, ("%s is not of type DIR\n", location)); + return EINVAL; + } + + subsidiary = sss_krb5_cc_file_path(location); + if (!subsidiary) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot get subsidiary cache from %s\n", + location)); + return EINVAL; + } + + return cc_file_remove(subsidiary); +} + +struct sss_krb5_cc_be dir_cc = { + .type = SSS_KRB5_TYPE_DIR, + .create = cc_dir_create, + .check_existing = cc_dir_check_existing, + .ccache_for_princ = cc_dir_cache_for_princ, + .remove = cc_dir_remove +}; diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h index 87bba2bae..5f677cae2 100644 --- a/src/providers/krb5/krb5_utils.h +++ b/src/providers/krb5/krb5_utils.h @@ -53,10 +53,17 @@ struct sss_krb5_cc_be { }; struct sss_krb5_cc_be file_cc; +struct sss_krb5_cc_be dir_cc; + +errno_t create_ccache_dir(const char *dirname, pcre *illegal_re, + uid_t uid, gid_t gid, bool private_path); errno_t cc_file_create(const char *filename, pcre *illegal_re, uid_t uid, gid_t gid, bool private_path); +errno_t cc_dir_create(const char *location, pcre *illegal_re, + uid_t uid, gid_t gid, bool private_path); + struct sss_krb5_cc_be *get_cc_be_ops(enum sss_krb5_cc_type type); struct sss_krb5_cc_be *get_cc_be_ops_ccache(const char *ccache); diff --git a/src/tests/krb5_child-test.c b/src/tests/krb5_child-test.c index 2ca655310..015bd39ae 100644 --- a/src/tests/krb5_child-test.c +++ b/src/tests/krb5_child-test.c @@ -40,6 +40,7 @@ extern struct dp_option default_krb5_opts[]; extern struct sss_krb5_cc_be file_cc; +extern struct sss_krb5_cc_be dir_cc; static krb5_context krb5_error_ctx; #define KRB5_DEBUG(level, krb5_error) do { \ @@ -119,8 +120,6 @@ create_dummy_krb5_ctx(TALLOC_CTX *mem_ctx, const char *realm) krb5_ctx = talloc_zero(mem_ctx, struct krb5_ctx); if (!krb5_ctx) return NULL; - krb5_ctx->cc_be = &file_cc; - krb5_ctx->illegal_path_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0, &errval, &errstr, &errpos, NULL); if (krb5_ctx->illegal_path_re == NULL) { @@ -202,10 +201,12 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user, const char *ccname, const char *ccname_template, int timeout) { + enum sss_krb5_cc_type cc_be; struct krb5child_req *kr; struct passwd *pwd; bool private; errno_t ret; + const char *tmpl; /* The top level child request */ kr = talloc_zero(mem_ctx, struct krb5child_req); @@ -237,6 +238,9 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user, ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL, ccname_template); if (ret != EOK) goto fail; + tmpl = ccname_template; + } else { + tmpl = dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL); } if (timeout) { @@ -252,18 +256,42 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user, DEBUG(SSSDBG_FUNC_DATA, ("ccname [%s] uid [%llu] gid [%llu]\n", kr->ccname, kr->uid, kr->gid)); - - ret = kr->krb5_ctx->cc_be->create(kr->ccname, - kr->krb5_ctx->illegal_path_re, - kr->uid, kr->gid, private); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, ("create_ccache_dir failed.\n")); - } } else { kr->ccname = talloc_strdup(kr, ccname); } if (!kr->ccname) goto fail; + cc_be = sss_krb5_get_type(kr->ccname); + switch (cc_be) { + case SSS_KRB5_TYPE_FILE: + kr->krb5_ctx->cc_be = &file_cc; + break; + case SSS_KRB5_TYPE_DIR: + kr->krb5_ctx->cc_be = &dir_cc; + break; + default: + if (tmpl[0] != '/') { + DEBUG(SSSDBG_OP_FAILURE, ("Unkown ccname database\n")); + ret = EINVAL; + goto fail; + } + DEBUG(SSSDBG_CONF_SETTINGS, ("The ccname template was " + "missing an explicit type, but looks like an absolute " + "path specifier. Assuming FILE:\n")); + kr->krb5_ctx->cc_be = &file_cc; + break; + } + DEBUG(SSSDBG_FUNC_DATA, ("ccname [%s] uid [%llu] gid [%llu]\n", + kr->ccname, kr->uid, kr->gid)); + + ret = kr->krb5_ctx->cc_be->create(kr->ccname, + kr->krb5_ctx->illegal_path_re, + kr->uid, kr->gid, private); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("create_ccache_dir failed.\n")); + goto fail; + } + return kr; fail: diff --git a/src/tests/krb5_utils-tests.c b/src/tests/krb5_utils-tests.c index 4a572488f..bcd9acb19 100644 --- a/src/tests/krb5_utils-tests.c +++ b/src/tests/krb5_utils-tests.c @@ -329,34 +329,83 @@ START_TEST(test_illegal_patterns) filename = talloc_asprintf(tmp_ctx, "abc/./ccfile"); fail_unless(filename != NULL, "talloc_asprintf failed."); - ret = cc_file_create(filename, illegal_re, uid, gid, true); - fail_unless(ret == EINVAL, "cc_file_create allowed relative path [%s].", + ret = create_ccache_dir(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 = cc_file_create(filename, illegal_re, uid, gid, true); - fail_unless(ret == EINVAL, "cc_file_create allowed " + ret = create_ccache_dir(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 = cc_file_create(filename, illegal_re, uid, gid, true); - fail_unless(ret == EINVAL, "cc_file_create allowed " + ret = create_ccache_dir(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 = cc_file_create(filename, illegal_re, uid, gid, true); - fail_unless(ret == EINVAL, "cc_file_create allowed " + ret = create_ccache_dir(filename, illegal_re, uid, gid, true); + fail_unless(ret == EINVAL, "create_ccache_dir allowed " "illegal pattern '//' in filename [%s].", filename); } END_TEST +START_TEST(test_cc_dir_create) +{ + char *residual; + char *dirname; + char *cwd; + uid_t uid = getuid(); + gid_t gid = getgid(); + pcre *illegal_re; + errno_t ret; + 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/user_dir", + cwd, TESTS_PATH); + fail_unless(dirname != NULL, "talloc_asprintf failed."); + residual = talloc_asprintf(tmp_ctx, "DIR:%s/%s", dirname, "ccdir"); + fail_unless(residual != NULL, "talloc_asprintf failed."); + + ret = cc_dir_create(residual, illegal_re, uid, gid, true); + fail_unless(ret == EOK, "cc_dir_create failed\n"); + ret = rmdir(dirname); + fail_unless(ret == 0, "Cannot remove %s: %s\n", dirname, strerror(ret)); + talloc_free(residual); + + dirname = talloc_asprintf(tmp_ctx, "%s/%s/user_dir2", + cwd, TESTS_PATH); + fail_unless(dirname != NULL, "talloc_asprintf failed."); + residual = talloc_asprintf(tmp_ctx, "DIR:%s/%s", dirname, "ccdir/"); + fail_unless(residual != NULL, "talloc_asprintf failed."); + + ret = cc_dir_create(residual, illegal_re, uid, gid, true); + fail_unless(ret == EOK, "cc_dir_create failed\n"); + ret = rmdir(dirname); + fail_unless(ret == 0, "Cannot remove %s: %s\n", dirname, strerror(ret)); + talloc_free(residual); + free(cwd); +} +END_TEST + void setup_talloc_context(void) { int ret; @@ -645,6 +694,7 @@ Suite *krb5_utils_suite (void) tcase_add_checked_fixture (tc_create_dir, setup_create_dir, teardown_create_dir); tcase_add_test (tc_create_dir, test_illegal_patterns); + tcase_add_test (tc_create_dir, test_cc_dir_create); 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); diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c index 5bc1140d0..8a6bfe2dc 100644 --- a/src/util/sss_krb5.c +++ b/src/util/sss_krb5.c @@ -604,11 +604,10 @@ void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name) } -krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm, +krb5_error_code check_for_valid_tgt(krb5_context context, + krb5_ccache ccache, const char *realm, const char *client_princ_str, bool *result) { - krb5_context context = NULL; - krb5_ccache ccache = NULL; krb5_error_code krberr; TALLOC_CTX *tmp_ctx = NULL; krb5_creds mcred; @@ -625,18 +624,6 @@ krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm, return ENOMEM; } - krberr = krb5_init_context(&context); - if (krberr) { - DEBUG(1, ("Failed to init kerberos context\n")); - goto done; - } - - krberr = krb5_cc_resolve(context, ccname, &ccache); - if (krberr != 0) { - DEBUG(1, ("krb5_cc_resolve failed.\n")); - goto done; - } - server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm); if (server_name == NULL) { DEBUG(1, ("talloc_asprintf failed.\n")); @@ -685,10 +672,6 @@ done: if (server_principal != NULL) { krb5_free_principal(context, server_principal); } - if (ccache != NULL) { - krb5_cc_close(context, ccache); - } - if (context != NULL) krb5_free_context(context); talloc_free(tmp_ctx); return krberr; } diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h index bba2a7eda..89ec00021 100644 --- a/src/util/sss_krb5.h +++ b/src/util/sss_krb5.h @@ -55,7 +55,8 @@ void KRB5_CALLCONV sss_krb5_get_init_creds_opt_free (krb5_context context, void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name); -krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm, +krb5_error_code check_for_valid_tgt(krb5_context context, + krb5_ccache ccache, const char *realm, const char *client_princ_str, bool *result); int sss_krb5_verify_keytab(const char *principal, -- cgit