From 3b313392baa798dfa76b0e0d7af95218c5d22114 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Thu, 15 Aug 2013 19:36:26 -0400 Subject: KRB5: Add support for KEYRING cache type https://fedorahosted.org/sssd/ticket/2036 --- src/man/sssd-krb5.5.xml | 23 ++++-- src/providers/krb5/krb5_child.c | 49 ++++++++++++ src/providers/krb5/krb5_common.c | 5 ++ src/providers/krb5/krb5_utils.c | 156 +++++++++++++++++++++++++++++++++++++++ src/providers/krb5/krb5_utils.h | 2 + src/util/sss_krb5.c | 14 ++++ src/util/sss_krb5.h | 2 + 7 files changed, 245 insertions(+), 6 deletions(-) diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml index df124b4d2..720f39b7b 100644 --- a/src/man/sssd-krb5.5.xml +++ b/src/man/sssd-krb5.5.xml @@ -158,12 +158,15 @@ krb5_ccname_template (string) - Location of the user's credential cache. Two credential - cache types are currently supported: FILE - and DIR. The cache can be specified either - as TYPE:RESIDUAL, or as an absolute - path, which implies the FILE type. In the - template, the following sequences are substituted: + Location of the user's credential cache. Three + credential cache types are currently supported: + FILE, DIR and + KEYRING:persistent. The cache can + be specified either as + TYPE:RESIDUAL, or as an + absolute path, which implies the + FILE type. In the template, the + following sequences are substituted: %u @@ -208,6 +211,14 @@ If the template ends with 'XXXXXX' mkstemp(3) is used to create a unique filename in a safe way. + + When using KEYRING types, the only supported + mechanism is KEYRING:persistent:%U, + which uses the Linux kernel keyring to store + credentials on a per-UID basis. This is also the + recommended choice, as it is the most secure and + predictable method. + Default: FILE:%d/krb5cc_%U_XXXXXX diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index 02eea79ab..e7ace14ac 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -505,6 +505,19 @@ store_creds_in_ccache(krb5_context ctx, krb5_principal princ, { krb5_error_code kerr; krb5_creds *l_cred; + char *ccname; + + if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) { + kerr = krb5_cc_get_full_name(ctx, cc, &ccname); + if (kerr != 0) { + DEBUG(SSSDBG_TRACE_ALL, + ("Couldn't determine full name of ccache\n")); + } else { + DEBUG(SSSDBG_TRACE_ALL, + ("Storing credentials in [%s]\n", ccname)); + krb5_free_string(ctx, ccname); + } + } kerr = krb5_cc_initialize(ctx, cc, princ); if (kerr != 0) { @@ -775,6 +788,37 @@ done: return kerr; } +static krb5_error_code +create_ccache_keyring(krb5_context ctx, + krb5_principal princ, + char *ccname, + krb5_creds *creds) +{ + krb5_error_code kerr; + krb5_ccache tmp_cc = NULL; + + DEBUG(SSSDBG_FUNC_DATA, ("Creating ccache at [%s]\n", ccname)); + + kerr = krb5_cc_resolve(ctx, ccname, &tmp_cc); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + goto done; + } + + kerr = store_creds_in_ccache(ctx, princ, tmp_cc, creds); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + goto done; + } + +done: + if (kerr != 0 && tmp_cc != NULL) { + krb5_cc_destroy(ctx, tmp_cc); + } + + return kerr; +} + #endif /* HAVE_KRB5_CC_COLLECTION */ static krb5_error_code @@ -787,10 +831,15 @@ create_ccache(uid_t uid, gid_t gid, krb5_context ctx, switch (cctype) { case SSS_KRB5_TYPE_FILE: return create_ccache_file(ctx, princ, ccname, creds); + #ifdef HAVE_KRB5_CC_COLLECTION case SSS_KRB5_TYPE_DIR: return create_ccache_in_dir(uid, gid, ctx, princ, ccname, creds); + + case SSS_KRB5_TYPE_KEYRING: + return create_ccache_keyring(ctx, princ, ccname, creds); #endif /* HAVE_KRB5_CC_COLLECTION */ + default: DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown cache type\n")); return EINVAL; diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c index e56dc6245..86e72157a 100644 --- a/src/providers/krb5/krb5_common.c +++ b/src/providers/krb5/krb5_common.c @@ -215,6 +215,11 @@ errno_t check_and_export_options(struct dp_option *opts, DEBUG(SSSDBG_CONF_SETTINGS, ("ccache is of type DIR\n")); krb5_ctx->cc_be = &dir_cc; break; + + case SSS_KRB5_TYPE_KEYRING: + DEBUG(SSSDBG_CONF_SETTINGS, ("ccache is of type KEYRING\n")); + krb5_ctx->cc_be = &keyring_cc; + break; #endif /* HAVE_KRB5_CC_COLLECTION */ default: diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c index f8fbb29eb..d4fb93adb 100644 --- a/src/providers/krb5/krb5_utils.c +++ b/src/providers/krb5/krb5_utils.c @@ -760,6 +760,8 @@ done: return ret; } + + /*======== ccache back end utilities ========*/ struct sss_krb5_cc_be * get_cc_be_ops(enum sss_krb5_cc_type type) @@ -775,6 +777,10 @@ get_cc_be_ops(enum sss_krb5_cc_type type) case SSS_KRB5_TYPE_DIR: be = &dir_cc; break; + + case SSS_KRB5_TYPE_KEYRING: + be = &keyring_cc; + break; #endif /* HAVE_KRB5_CC_COLLECTION */ case SSS_KRB5_TYPE_UNKNOWN: @@ -1189,6 +1195,156 @@ struct sss_krb5_cc_be dir_cc = { .remove = cc_dir_remove }; + +/*======== Operations on the KEYRING: back end ========*/ + +errno_t +cc_keyring_create(const char *location, pcre *illegal_re, + uid_t uid, gid_t gid, bool private_path) +{ + const char *residual; + + residual = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_KEYRING); + if (residual == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("Bad ccache type %s\n", location)); + return EINVAL; + } + + /* No special steps are needed to create a kernel keyring. + * Everything is handled in libkrb5. + */ + return EOK; +} + +errno_t +cc_keyring_check_existing(const char *location, uid_t uid, + const char *realm, const char *princ, + const char *cc_template, bool *_active, + bool *_valid) +{ + errno_t ret; + bool active; + bool valid; + const char *residual; + + residual = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_KEYRING); + if (!residual) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("%s is not of type KEYRING:\n", location)); + return EINVAL; + } + + /* The keyring cache is always active */ + active = true; + + /* Check if any user is actively using this cache */ + ret = check_cc_validity(location, realm, princ, &valid); + if (ret != EOK) { + return ret; + } + + *_active = active; + *_valid = valid; + return EOK; +} + +const char * +cc_keyring_cache_for_princ(TALLOC_CTX *mem_ctx, const char *location, + const char *princ) +{ + krb5_context context = NULL; + krb5_error_code krberr; + char *name = NULL; + const char *residual; + size_t i; + size_t count; + krb5_principal client_principal = NULL; + + residual = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_KEYRING); + if (!residual) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot get residual from %s\n", + location)); + return NULL; + } + + /* residual already points to a subsidiary cache if it of the + * form "KEYRING:::krb5_cc_XXXXXXX" + * For simplicity, we'll count the colons, up to three. + */ + i = count = 0; + while (residual[i] && count < 3) { + if (residual[i] == ':') { + count ++; + } + i++; + } + + if (count >= 3) { + 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 = krb5_parse_name(context, princ, &client_principal); + if (krberr != 0) { + KRB5_DEBUG(SSSDBG_OP_FAILURE, context, krberr); + DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_parse_name failed.\n")); + goto done; + } + + name = sss_get_ccache_name_for_principal(mem_ctx, context, + client_principal, location); + if (name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Could not get full name of ccache\n")); + goto done; + } + + talloc_zfree(name); + + /* Always return the master name here. + * We do the above only to ensure that the + * principal-specific name exists and can + * be found. + */ + name = talloc_strdup(mem_ctx, location); + +done: + krb5_free_principal(context, client_principal); + krb5_free_context(context); + + return name; +} + +errno_t +cc_keyring_remove(const char *location) +{ + const char *residual; + + residual = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_KEYRING); + if (!residual) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("%s is not of type KEYRING:\n", location)); + return EINVAL; + } + + /* No special steps are needed to create a kernel keyring. + * Everything is handled in libkrb5. + */ + return EOK; +} + +struct sss_krb5_cc_be keyring_cc = { + .type = SSS_KRB5_TYPE_KEYRING, + .create = cc_keyring_create, + .check_existing = cc_keyring_check_existing, + .ccache_for_princ = cc_keyring_cache_for_princ, + .remove = cc_keyring_remove +}; + #endif /* HAVE_KRB5_CC_COLLECTION */ errno_t get_domain_or_subdomain(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx, diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h index 4a5904cd7..cdc9f2364 100644 --- a/src/providers/krb5/krb5_utils.h +++ b/src/providers/krb5/krb5_utils.h @@ -87,12 +87,14 @@ errno_t get_ccache_file_data(const char *ccache_file, const char *client_name, #ifdef HAVE_KRB5_CC_COLLECTION extern struct sss_krb5_cc_be dir_cc; +extern struct sss_krb5_cc_be keyring_cc; errno_t cc_dir_create(const char *location, pcre *illegal_re, uid_t uid, gid_t gid, bool private_path); #endif /* HAVE_KRB5_CC_COLLECTION */ + errno_t get_domain_or_subdomain(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx, char *domain_name, struct sss_domain_info **dom); diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c index 457a52c0b..b25ed2491 100644 --- a/src/util/sss_krb5.c +++ b/src/util/sss_krb5.c @@ -928,6 +928,7 @@ sss_krb5_free_keytab_entry_contents(krb5_context context, #define SSS_KRB5_FILE "FILE:" #define SSS_KRB5_DIR "DIR:" +#define SSS_KRB5_KEYRING "KEYRING:" enum sss_krb5_cc_type sss_krb5_get_type(const char *full_location) @@ -945,7 +946,12 @@ sss_krb5_get_type(const char *full_location) sizeof(SSS_KRB5_DIR)-1) == 0) { return SSS_KRB5_TYPE_DIR; } + else if (strncmp(full_location, SSS_KRB5_KEYRING, + sizeof(SSS_KRB5_KEYRING)-1) == 0) { + return SSS_KRB5_TYPE_KEYRING; + } #endif /* HAVE_KRB5_CC_COLLECTION */ + else if (full_location[0] == '/') { return SSS_KRB5_TYPE_FILE; } @@ -973,7 +979,12 @@ sss_krb5_residual_by_type(const char *full_location, case SSS_KRB5_TYPE_DIR: offset = sizeof(SSS_KRB5_DIR)-1; break; + + case SSS_KRB5_TYPE_KEYRING: + offset = sizeof(SSS_KRB5_KEYRING)-1; + break; #endif /* HAVE_KRB5_CC_COLLECTION */ + default: return NULL; } @@ -991,6 +1002,9 @@ sss_krb5_cc_file_path(const char *full_location) residual = sss_krb5_residual_by_type(full_location, cc_type); switch(cc_type) { +#ifdef HAVE_KRB5_CC_COLLECTION + case SSS_KRB5_TYPE_KEYRING: +#endif /* HAVE_KRB5_CC_COLLECTION */ case SSS_KRB5_TYPE_FILE: return residual; #ifdef HAVE_KRB5_CC_COLLECTION diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h index 1c10d4713..b1074f813 100644 --- a/src/util/sss_krb5.h +++ b/src/util/sss_krb5.h @@ -147,7 +147,9 @@ enum sss_krb5_cc_type { SSS_KRB5_TYPE_FILE, #ifdef HAVE_KRB5_CC_COLLECTION SSS_KRB5_TYPE_DIR, + SSS_KRB5_TYPE_KEYRING, #endif /* HAVE_KRB5_CC_COLLECTION */ + SSS_KRB5_TYPE_UNKNOWN }; -- cgit