From cfd79b92d3813ed53ef51ae2cf93be6287e73a27 Mon Sep 17 00:00:00 2001 From: Jan Zeleny Date: Tue, 29 Mar 2011 02:50:28 -0400 Subject: Extend and move function for finding principal in keytab The function now supports finding principal in keytab not only based on realm, but based on both realm and primary/instance parts. The function also supports * wildcard at the beginning or at the end of primary principal part. The function for finding principal has been moved to util/sss_krb5.c, so it can be used in other parts of the code. --- src/providers/krb5/krb5_child.c | 82 +-------------------- src/util/sss_krb5.c | 155 ++++++++++++++++++++++++++++++++++++++++ src/util/sss_krb5.h | 6 ++ 3 files changed, 163 insertions(+), 80 deletions(-) (limited to 'src') diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index 335da423..1ed63f6b 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -30,6 +30,7 @@ #include #include "util/util.h" +#include "util/sss_krb5.h" #include "util/user_info_msg.h" #include "providers/child_common.h" #include "providers/dp_backend.h" @@ -492,85 +493,6 @@ static errno_t add_ticket_times_to_response(struct krb5_req *kr) return ret; } -static krb5_error_code find_principal_in_keytab(krb5_context ctx, - krb5_keytab keytab, - const char *realm, - krb5_principal *princ) -{ - krb5_error_code kerr; - krb5_error_code kt_err; - krb5_error_code kerr_d; - krb5_kt_cursor cursor; - krb5_keytab_entry entry; - bool principal_found = false; - - memset(&cursor, 0, sizeof(cursor)); - kerr = krb5_kt_start_seq_get(ctx, keytab, &cursor); - if (kerr != 0) { - DEBUG(1, ("krb5_kt_start_seq_get failed.\n")); - KRB5_DEBUG(1, kerr); - return kerr; - } - - /* We look for the first entry from our realm or take the last one */ - memset(&entry, 0, sizeof(entry)); - while ((kt_err = krb5_kt_next_entry(ctx, keytab, &entry, &cursor)) == 0) { - if (krb5_princ_realm(ctx, entry.principal)->length == strlen(realm) && - strncmp(krb5_princ_realm(ctx, entry.principal)->data, realm, - krb5_princ_realm(ctx, entry.principal)->length) == 0) { - DEBUG(9, ("Found keytab entry with the realm of the credential.\n")); - principal_found = true; - break; - } - - kerr = krb5_free_keytab_entry_contents(ctx, &entry); - if (kerr != 0) { - DEBUG(1, ("Failed to free keytab entry.\n")); - } - memset(&entry, 0, sizeof(entry)); - } - - /* Close the keytab here. Even though we're using cursors, the file - * handle is stored in the krb5_keytab structure, and it gets - * overwritten by other keytab calls, creating a leak. */ - kerr = krb5_kt_end_seq_get(ctx, keytab, &cursor); - if (kerr != 0) { - DEBUG(1, ("krb5_kt_end_seq_get failed.\n")); - goto done; - } - - if (!principal_found) { - kerr = KRB5_KT_NOTFOUND; - DEBUG(1, ("No principal from realm [%s] found in keytab.\n", realm)); - goto done; - } - - /* check if we got any errors from krb5_kt_next_entry */ - if (kt_err != 0 && kt_err != KRB5_KT_END) { - DEBUG(1, ("Error while reading keytab.\n")); - KRB5_DEBUG(1, kerr); - goto done; - } - - kerr = krb5_copy_principal(ctx, entry.principal, princ); - if (kerr != 0) { - DEBUG(1, ("krb5_copy_principal failed.\n")); - KRB5_DEBUG(1, kerr); - goto done; - } - - kerr = 0; - -done: - kerr_d = krb5_free_keytab_entry_contents(ctx, &entry); - if (kerr_d != 0) { - DEBUG(1, ("Failed to free keytab entry.\n")); - KRB5_DEBUG(1, kerr_d); - } - - return kerr; -} - static krb5_error_code validate_tgt(struct krb5_req *kr) { krb5_error_code kerr; @@ -1338,7 +1260,7 @@ static krb5_error_code check_fast_ccache(krb5_context ctx, const char *realm, goto done; } - kerr = find_principal_in_keytab(ctx, keytab, realm, &client_princ); + kerr = find_principal_in_keytab(ctx, keytab, NULL, realm, &client_princ); if (kerr != 0) { DEBUG(1, ("find_principal_in_keytab failed.\n")); goto done; diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c index 894dd443..459f48b5 100644 --- a/src/util/sss_krb5.c +++ b/src/util/sss_krb5.c @@ -188,6 +188,161 @@ int sss_krb5_verify_keytab_ex(const char *principal, const char *keytab_name, return EOK; } + +enum matching_mode {MODE_NORMAL, MODE_PREFIX, MODE_POSTFIX}; +/** + * We only have primary and instances stored separately, we need to + * join them to one string and compare that string. + * + * @param ctx kerberos context + * @param principal principal we want to match + * @param pattern_primary primary part of the principal we want to + * perform matching against. It is possible to use * wildcard + * at the beginning or at the end of the string. If NULL, it + * will act as "*" + * @param pattern_realm realm part of the principal we want to perform + * the matching against. If NULL, it will act as "*" + */ +static bool match_principal(krb5_context ctx, + krb5_principal principal, + const char *pattern_primary, + const char *pattern_realm) +{ + krb5_data *realm_data; + char *primary = NULL; + char *primary_str = NULL; + int primary_str_len = 0; + int tmp_len; + int len_diff; + + int mode = MODE_NORMAL; + TALLOC_CTX *tmp_ctx; + bool ret; + + realm_data = krb5_princ_realm(ctx, principal); + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + DEBUG(1, ("talloc_new failed\n")); + return false; + } + + if (pattern_primary) { + tmp_len = strlen(pattern_primary); + if (pattern_primary[tmp_len] == '*') { + mode = MODE_PREFIX; + primary_str = talloc_strdup(tmp_ctx, pattern_primary); + primary_str[tmp_len] = '\0'; + primary_str_len = tmp_len-1; + } else if (pattern_primary[0] == '*') { + mode = MODE_POSTFIX; + primary_str = talloc_strdup(tmp_ctx, pattern_primary+1); + primary_str_len = tmp_len-1; + } + + krb5_unparse_name_flags(ctx, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, + &primary); + + len_diff = strlen(primary)-primary_str_len; + + if ((mode == MODE_NORMAL && + strcmp(primary, pattern_primary) != 0) || + (mode == MODE_PREFIX && + strncmp(primary, primary_str, primary_str_len) != 0) || + (mode == MODE_POSTFIX && + strcmp(primary+len_diff, primary_str) != 0)) { + ret = false; + goto done; + } + } + + if (!pattern_realm || (realm_data->length == strlen(pattern_realm) && + strncmp(realm_data->data, pattern_realm, realm_data->length) == 0)) { + DEBUG(7, ("Principal matched to the sample (%s@%s).\n", pattern_primary, + pattern_realm)); + ret = true; + } + +done: + free(primary); + talloc_free(tmp_ctx); + return ret; +} + +krb5_error_code find_principal_in_keytab(krb5_context ctx, + krb5_keytab keytab, + const char *pattern_primary, + const char *pattern_realm, + krb5_principal *princ) +{ + krb5_error_code kerr; + krb5_error_code kt_err; + krb5_error_code kerr_d; + krb5_kt_cursor cursor; + krb5_keytab_entry entry; + bool principal_found = false; + + memset(&cursor, 0, sizeof(cursor)); + kerr = krb5_kt_start_seq_get(ctx, keytab, &cursor); + if (kerr != 0) { + DEBUG(1, ("krb5_kt_start_seq_get failed.\n")); + return kerr; + } + + DEBUG(9, ("Trying to find principal %s@%s in keytab.\n", pattern_primary, pattern_realm)); + memset(&entry, 0, sizeof(entry)); + while ((kt_err = krb5_kt_next_entry(ctx, keytab, &entry, &cursor)) == 0) { + principal_found = match_principal(ctx, entry.principal, pattern_primary, pattern_realm); + if (principal_found) { + break; + } + + kerr = krb5_free_keytab_entry_contents(ctx, &entry); + if (kerr != 0) { + DEBUG(1, ("Failed to free keytab entry.\n")); + } + memset(&entry, 0, sizeof(entry)); + } + + /* Close the keytab here. Even though we're using cursors, the file + * handle is stored in the krb5_keytab structure, and it gets + * overwritten by other keytab calls, creating a leak. */ + kerr = krb5_kt_end_seq_get(ctx, keytab, &cursor); + if (kerr != 0) { + DEBUG(1, ("krb5_kt_end_seq_get failed.\n")); + goto done; + } + + if (!principal_found) { + kerr = KRB5_KT_NOTFOUND; + DEBUG(1, ("No principal matching %s@%s found in keytab.\n", + pattern_primary, pattern_realm)); + goto done; + } + + /* check if we got any errors from krb5_kt_next_entry */ + if (kt_err != 0 && kt_err != KRB5_KT_END) { + DEBUG(1, ("Error while reading keytab.\n")); + goto done; + } + + kerr = krb5_copy_principal(ctx, entry.principal, princ); + if (kerr != 0) { + DEBUG(1, ("krb5_copy_principal failed.\n")); + goto done; + } + + kerr = 0; + +done: + kerr_d = krb5_free_keytab_entry_contents(ctx, &entry); + if (kerr_d != 0) { + DEBUG(1, ("Failed to free keytab entry.\n")); + } + + return kerr; +} + const char *KRB5_CALLCONV sss_krb5_get_error_message(krb5_context ctx, krb5_error_code ec) { diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h index 0a82315c..f25f3285 100644 --- a/src/util/sss_krb5.h +++ b/src/util/sss_krb5.h @@ -58,6 +58,12 @@ int sss_krb5_verify_keytab(const char *principal, int sss_krb5_verify_keytab_ex(const char *principal, const char *keytab_name, krb5_context context, krb5_keytab keytab); +krb5_error_code find_principal_in_keytab(krb5_context ctx, + krb5_keytab keytab, + const char *pattern_primary, + const char *pattern_realm, + krb5_principal *princ); + #ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_EXPIRE_CALLBACK typedef void krb5_expire_callback_func(krb5_context context, void *data, krb5_timestamp password_expiration, -- cgit