diff options
Diffstat (limited to 'source4/auth/kerberos')
-rw-r--r-- | source4/auth/kerberos/kerberos.c | 397 | ||||
-rw-r--r-- | source4/auth/kerberos/kerberos.h | 4 | ||||
-rw-r--r-- | source4/auth/kerberos/kerberos_pac.c | 4 | ||||
-rw-r--r-- | source4/auth/kerberos/kerberos_util.c | 15 | ||||
-rw-r--r-- | source4/auth/kerberos/krb5_init_context.c | 2 | ||||
-rw-r--r-- | source4/auth/kerberos/wscript_build | 2 |
6 files changed, 366 insertions, 58 deletions
diff --git a/source4/auth/kerberos/kerberos.c b/source4/auth/kerberos/kerberos.c index 0db0dd3ced..0fc9d143ab 100644 --- a/source4/auth/kerberos/kerberos.c +++ b/source4/auth/kerberos/kerberos.c @@ -81,86 +81,387 @@ The impersonate_principal is the principal if NULL, or the principal to impersonate - The target_service defaults to the krbtgt if NULL, but could be kpasswd/realm or the local service (if we are doing s4u2self) + The self_service, should be the local service (for S4U2Self if impersonate_principal is given). + + The target_service defaults to the krbtgt if NULL, but could be kpasswd/realm or a remote service (for S4U2Proxy) */ - krb5_error_code kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, - krb5_principal principal, const char *password, - krb5_principal impersonate_principal, const char *target_service, + krb5_error_code kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache store_cc, + krb5_principal init_principal, + const char *init_password, + krb5_principal impersonate_principal, + const char *self_service, + const char *target_service, krb5_get_init_creds_opt *krb_options, time_t *expire_time, time_t *kdc_time) { krb5_error_code code = 0; - krb5_creds my_creds; - krb5_creds *impersonate_creds; krb5_get_creds_opt options; + krb5_principal store_principal; + krb5_creds store_creds; + krb5_creds *s4u2self_creds; + Ticket s4u2self_ticket; + size_t s4u2self_ticketlen; + krb5_creds *s4u2proxy_creds; + krb5_principal self_princ; + bool s4u2proxy; + krb5_principal target_princ; + krb5_ccache tmp_cc; + const char *self_realm; + krb5_principal blacklist_principal = NULL; + krb5_principal whitelist_principal = NULL; - /* If we are not impersonating, then get this ticket for the + if (impersonate_principal && self_service == NULL) { + return EINVAL; + } + + /* + * If we are not impersonating, then get this ticket for the * target service, otherwise a krbtgt, and get the next ticket - * for the target */ - if ((code = krb5_get_init_creds_password(ctx, &my_creds, principal, password, - NULL, NULL, - 0, - impersonate_principal ? NULL : target_service, - krb_options))) { + * for the target + */ + code = krb5_get_init_creds_password(ctx, &store_creds, + init_principal, + init_password, + NULL, NULL, + 0, + impersonate_principal ? NULL : target_service, + krb_options); + if (code != 0) { return code; } - if ((code = krb5_cc_initialize(ctx, cc, principal))) { - krb5_free_cred_contents(ctx, &my_creds); + store_principal = init_principal; + + if (impersonate_principal == NULL) { + goto store; + } + + /* + * We are trying S4U2Self now: + * + * As we do not want to expose our TGT in the + * krb5_ccache, which is also holds the impersonated creds. + * + * Some low level krb5/gssapi function might use the TGT + * identity and let the client act as our machine account. + * + * We need to avoid that and use a temporary krb5_ccache + * in order to pass our TGT to the krb5_get_creds() function. + */ + code = krb5_cc_new_unique(ctx, NULL, NULL, &tmp_cc); + if (code != 0) { + krb5_free_cred_contents(ctx, &store_creds); return code; } - - if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) { - krb5_free_cred_contents(ctx, &my_creds); + + code = krb5_cc_initialize(ctx, tmp_cc, store_creds.client); + if (code != 0) { + krb5_cc_destroy(ctx, tmp_cc); + krb5_free_cred_contents(ctx, &store_creds); return code; } - - if (expire_time) { - *expire_time = (time_t) my_creds.times.endtime; + + code = krb5_cc_store_cred(ctx, tmp_cc, &store_creds); + if (code != 0) { + krb5_free_cred_contents(ctx, &store_creds); + krb5_cc_destroy(ctx, tmp_cc); + return code; } - if (kdc_time) { - *kdc_time = (time_t) my_creds.times.starttime; + /* + * we need to remember the client principal of our + * TGT and make sure the KDC does not return this + * in the impersonated tickets. This can happen + * if the KDC does not support S4U2Self and S4U2Proxy. + */ + blacklist_principal = store_creds.client; + store_creds.client = NULL; + krb5_free_cred_contents(ctx, &store_creds); + + /* + * Check if we also need S4U2Proxy or if S4U2Self is + * enough in order to get a ticket for the target. + */ + if (target_service == NULL) { + s4u2proxy = false; + } else if (strcmp(target_service, self_service) == 0) { + s4u2proxy = false; + } else { + s4u2proxy = true; } - krb5_free_cred_contents(ctx, &my_creds); - - if (code == 0 && impersonate_principal) { - krb5_principal target_princ; - if ((code = krb5_get_creds_opt_alloc(ctx, &options))) { - return code; - } + /* + * For S4U2Self we need our own service principal, + * which belongs to our own realm (available on + * our client principal). + */ + self_realm = krb5_principal_get_realm(ctx, init_principal); - if ((code = krb5_get_creds_opt_set_impersonate(ctx, options, impersonate_principal))) { - krb5_get_creds_opt_free(ctx, options); - return code; - } + code = krb5_parse_name(ctx, self_service, &self_princ); + if (code != 0) { + krb5_free_principal(ctx, blacklist_principal); + krb5_cc_destroy(ctx, tmp_cc); + return code; + } - if ((code = krb5_parse_name(ctx, target_service, &target_princ))) { - krb5_get_creds_opt_free(ctx, options); - return code; - } + code = krb5_principal_set_realm(ctx, self_princ, self_realm); + if (code != 0) { + krb5_free_principal(ctx, blacklist_principal); + krb5_free_principal(ctx, self_princ); + krb5_cc_destroy(ctx, tmp_cc); + return code; + } - if ((code = krb5_principal_set_realm(ctx, target_princ, krb5_principal_get_realm(ctx, principal)))) { - krb5_get_creds_opt_free(ctx, options); - krb5_free_principal(ctx, target_princ); - return code; - } + code = krb5_get_creds_opt_alloc(ctx, &options); + if (code != 0) { + krb5_free_principal(ctx, blacklist_principal); + krb5_free_principal(ctx, self_princ); + krb5_cc_destroy(ctx, tmp_cc); + return code; + } - if ((code = krb5_get_creds(ctx, options, cc, target_princ, &impersonate_creds))) { - krb5_free_principal(ctx, target_princ); - krb5_get_creds_opt_free(ctx, options); + if (s4u2proxy) { + /* + * If we want S4U2Proxy, we need the forwardable flag + * on the S4U2Self ticket. + */ + krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE); + } + + code = krb5_get_creds_opt_set_impersonate(ctx, options, + impersonate_principal); + if (code != 0) { + krb5_get_creds_opt_free(ctx, options); + krb5_free_principal(ctx, blacklist_principal); + krb5_free_principal(ctx, self_princ); + krb5_cc_destroy(ctx, tmp_cc); + return code; + } + + code = krb5_get_creds(ctx, options, tmp_cc, + self_princ, &s4u2self_creds); + krb5_get_creds_opt_free(ctx, options); + krb5_free_principal(ctx, self_princ); + if (code != 0) { + krb5_free_principal(ctx, blacklist_principal); + krb5_cc_destroy(ctx, tmp_cc); + return code; + } + + if (!s4u2proxy) { + krb5_cc_destroy(ctx, tmp_cc); + + /* + * Now make sure we store the impersonated principal + * and creds instead of the TGT related stuff + * in the krb5_ccache of the caller. + */ + code = krb5_copy_creds_contents(ctx, s4u2self_creds, + &store_creds); + krb5_free_creds(ctx, s4u2self_creds); + if (code != 0) { return code; } + /* + * It's important to store the principal the KDC + * returned, as otherwise the caller would not find + * the S4U2Self ticket in the krb5_ccache lookup. + */ + store_principal = store_creds.client; + goto store; + } + + /* + * We are trying S4U2Proxy: + * + * We need the ticket from the S4U2Self step + * and our TGT in order to get the delegated ticket. + */ + code = decode_Ticket((const uint8_t *)s4u2self_creds->ticket.data, + s4u2self_creds->ticket.length, + &s4u2self_ticket, + &s4u2self_ticketlen); + if (code != 0) { + krb5_free_creds(ctx, s4u2self_creds); + krb5_free_principal(ctx, blacklist_principal); + krb5_cc_destroy(ctx, tmp_cc); + return code; + } + + /* + * we need to remember the client principal of the + * S4U2Self stage and as it needs to match the one we + * will get for the S4U2Proxy stage. We need this + * in order to detect KDCs which does not support S4U2Proxy. + */ + whitelist_principal = s4u2self_creds->client; + s4u2self_creds->client = NULL; + krb5_free_creds(ctx, s4u2self_creds); + + /* + * For S4U2Proxy we also got a target service principal, + * which also belongs to our own realm (available on + * our client principal). + */ + code = krb5_parse_name(ctx, target_service, &target_princ); + if (code != 0) { + free_Ticket(&s4u2self_ticket); + krb5_free_principal(ctx, whitelist_principal); + krb5_free_principal(ctx, blacklist_principal); + krb5_cc_destroy(ctx, tmp_cc); + return code; + } + + code = krb5_principal_set_realm(ctx, target_princ, self_realm); + if (code != 0) { + free_Ticket(&s4u2self_ticket); krb5_free_principal(ctx, target_princ); + krb5_free_principal(ctx, whitelist_principal); + krb5_free_principal(ctx, blacklist_principal); + krb5_cc_destroy(ctx, tmp_cc); + return code; + } + + code = krb5_get_creds_opt_alloc(ctx, &options); + if (code != 0) { + free_Ticket(&s4u2self_ticket); + krb5_free_principal(ctx, target_princ); + krb5_free_principal(ctx, whitelist_principal); + krb5_free_principal(ctx, blacklist_principal); + krb5_cc_destroy(ctx, tmp_cc); + return code; + } + + krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE); + krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_CONSTRAINED_DELEGATION); - code = krb5_cc_store_cred(ctx, cc, impersonate_creds); + code = krb5_get_creds_opt_set_ticket(ctx, options, &s4u2self_ticket); + free_Ticket(&s4u2self_ticket); + if (code != 0) { krb5_get_creds_opt_free(ctx, options); - krb5_free_creds(ctx, impersonate_creds); + krb5_free_principal(ctx, target_princ); + krb5_free_principal(ctx, whitelist_principal); + krb5_free_principal(ctx, blacklist_principal); + krb5_cc_destroy(ctx, tmp_cc); + return code; } + code = krb5_get_creds(ctx, options, tmp_cc, + target_princ, &s4u2proxy_creds); + krb5_get_creds_opt_free(ctx, options); + krb5_free_principal(ctx, target_princ); + krb5_cc_destroy(ctx, tmp_cc); + if (code != 0) { + krb5_free_principal(ctx, whitelist_principal); + krb5_free_principal(ctx, blacklist_principal); + return code; + } + + /* + * Now make sure we store the impersonated principal + * and creds instead of the TGT related stuff + * in the krb5_ccache of the caller. + */ + code = krb5_copy_creds_contents(ctx, s4u2proxy_creds, + &store_creds); + krb5_free_creds(ctx, s4u2proxy_creds); + if (code != 0) { + krb5_free_principal(ctx, whitelist_principal); + krb5_free_principal(ctx, blacklist_principal); + return code; + } + + /* + * It's important to store the principal the KDC + * returned, as otherwise the caller would not find + * the S4U2Self ticket in the krb5_ccache lookup. + */ + store_principal = store_creds.client; + + store: + if (blacklist_principal && + krb5_principal_compare(ctx, store_creds.client, blacklist_principal)) { + char *sp = NULL; + char *ip = NULL; + + code = krb5_unparse_name(ctx, blacklist_principal, &sp); + if (code != 0) { + sp = NULL; + } + code = krb5_unparse_name(ctx, impersonate_principal, &ip); + if (code != 0) { + ip = NULL; + } + DEBUG(1, ("kerberos_kinit_password_cc: " + "KDC returned self principal[%s] while impersonating [%s]\n", + sp?sp:"<no memory>", + ip?ip:"<no memory>")); + + SAFE_FREE(sp); + SAFE_FREE(ip); + + krb5_free_principal(ctx, whitelist_principal); + krb5_free_principal(ctx, blacklist_principal); + krb5_free_cred_contents(ctx, &store_creds); + return KRB5_FWD_BAD_PRINCIPAL; + } + if (blacklist_principal) { + krb5_free_principal(ctx, blacklist_principal); + } + + if (whitelist_principal && + !krb5_principal_compare(ctx, store_creds.client, whitelist_principal)) { + char *sp = NULL; + char *ep = NULL; + + code = krb5_unparse_name(ctx, store_creds.client, &sp); + if (code != 0) { + sp = NULL; + } + code = krb5_unparse_name(ctx, whitelist_principal, &ep); + if (code != 0) { + ep = NULL; + } + DEBUG(1, ("kerberos_kinit_password_cc: " + "KDC returned wrong principal[%s] we expected [%s]\n", + sp?sp:"<no memory>", + ep?ep:"<no memory>")); + + SAFE_FREE(sp); + SAFE_FREE(ep); + + krb5_free_principal(ctx, whitelist_principal); + krb5_free_cred_contents(ctx, &store_creds); + return KRB5_FWD_BAD_PRINCIPAL; + } + if (whitelist_principal) { + krb5_free_principal(ctx, whitelist_principal); + } + + code = krb5_cc_initialize(ctx, store_cc, store_principal); + if (code != 0) { + krb5_free_cred_contents(ctx, &store_creds); + return code; + } + + code = krb5_cc_store_cred(ctx, store_cc, &store_creds); + if (code != 0) { + krb5_free_cred_contents(ctx, &store_creds); + return code; + } + + if (expire_time) { + *expire_time = (time_t) store_creds.times.endtime; + } + + if (kdc_time) { + *kdc_time = (time_t) store_creds.times.starttime; + } + + krb5_free_cred_contents(ctx, &store_creds); + return 0; } diff --git a/source4/auth/kerberos/kerberos.h b/source4/auth/kerberos/kerberos.h index c712569e5d..31794b8e03 100644 --- a/source4/auth/kerberos/kerberos.h +++ b/source4/auth/kerberos/kerberos.h @@ -97,7 +97,9 @@ krb5_error_code ads_krb5_mk_req(krb5_context context, bool get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt); krb5_error_code kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, krb5_principal principal, const char *password, - krb5_principal impersonate_principal, const char *target_service, + krb5_principal impersonate_principal, + const char *self_service, + const char *target_service, krb5_get_init_creds_opt *krb_options, time_t *expire_time, time_t *kdc_time); krb5_error_code kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc, diff --git a/source4/auth/kerberos/kerberos_pac.c b/source4/auth/kerberos/kerberos_pac.c index 5bc80c9a58..8ce970e486 100644 --- a/source4/auth/kerberos/kerberos_pac.c +++ b/source4/auth/kerberos/kerberos_pac.c @@ -448,14 +448,14 @@ NTSTATUS kerberos_pac_blob_to_user_info_dc(TALLOC_CTX *mem_ctx, pac_blob.data, pac_blob.length, &pac); if (ret) { - return map_nt_error_from_unix(ret); + return map_nt_error_from_unix_common(ret); } ret = kerberos_pac_to_user_info_dc(mem_ctx, pac, context, user_info_dc, pac_srv_sig, pac_kdc_sig); krb5_pac_free(context, pac); if (ret) { - return map_nt_error_from_unix(ret); + return map_nt_error_from_unix_common(ret); } return NT_STATUS_OK; } diff --git a/source4/auth/kerberos/kerberos_util.c b/source4/auth/kerberos/kerberos_util.c index 45b0b07e13..9a48e95c6d 100644 --- a/source4/auth/kerberos/kerberos_util.c +++ b/source4/auth/kerberos/kerberos_util.c @@ -173,7 +173,7 @@ static krb5_error_code principals_from_msg(TALLOC_CTX *parent_ctx, return ret; } - /* This song-and-dance effectivly puts the principal + /* This song-and-dance effectively puts the principal * into talloc, so we can't loose it. */ talloc_set_destructor(principals[i], free_principal); i++; @@ -262,7 +262,7 @@ static krb5_error_code salt_principal_from_msg(TALLOC_CTX *parent_ctx, upper_realm, "host", salt_body, NULL); if (ret == 0) { - /* This song-and-dance effectivly puts the principal + /* This song-and-dance effectively puts the principal * into talloc, so we can't loose it. */ mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context); mem_ctx->principal = *salt_princ; @@ -338,7 +338,9 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, const char **error_string) { krb5_error_code ret; - const char *password, *target_service; + const char *password; + const char *self_service; + const char *target_service; time_t kdc_time = 0; krb5_principal princ; krb5_principal impersonate_principal; @@ -363,6 +365,7 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, return ret; } + self_service = cli_credentials_get_self_service(credentials); target_service = cli_credentials_get_target_service(credentials); password = cli_credentials_get_password(credentials); @@ -403,7 +406,9 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, if (password) { ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, ccache, princ, password, - impersonate_principal, target_service, + impersonate_principal, + self_service, + target_service, krb_options, NULL, &kdc_time); } else if (impersonate_principal) { @@ -733,7 +738,7 @@ static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx, /* Release the enumeration. We are going to * have to start this from the top again, * because deletes during enumeration may not - * always be consistant. + * always be consistent. * * Also, the enumeration locks a FILE: keytab */ diff --git a/source4/auth/kerberos/krb5_init_context.c b/source4/auth/kerberos/krb5_init_context.c index db3a5375c9..fbcaad29d9 100644 --- a/source4/auth/kerberos/krb5_init_context.c +++ b/source4/auth/kerberos/krb5_init_context.c @@ -415,7 +415,7 @@ smb_krb5_init_context_basic(TALLOC_CTX *tmp_ctx, return ret; } - config_file = config_path(tmp_ctx, lp_ctx, "krb5.conf"); + config_file = lpcfg_config_path(tmp_ctx, lp_ctx, "krb5.conf"); if (!config_file) { krb5_free_context(krb5_ctx); return ENOMEM; diff --git a/source4/auth/kerberos/wscript_build b/source4/auth/kerberos/wscript_build index 586366d422..90e8560573 100644 --- a/source4/auth/kerberos/wscript_build +++ b/source4/auth/kerberos/wscript_build @@ -4,7 +4,7 @@ bld.SAMBA_LIBRARY('authkrb5', source='kerberos.c kerberos_heimdal.c kerberos_pac.c gssapi_parse.c krb5_init_context.c keytab_copy.c', autoproto='proto.h', public_deps='krb5 ndr-krb5pac samba_socket LIBCLI_RESOLVE com_err asn1', - deps='asn1util auth_sam_reply tevent LIBPACKET ndr ldb KRB5_WRAP', + deps='asn1util auth_sam_reply tevent LIBPACKET ndr ldb KRB5_WRAP errors', private_library=True ) |