From: Isaac Boukris Date: Sun, 16 Jun 2019 21:21:38 +0000 (+0300) Subject: Move S4U2Proxy client code to s4u_creds.c X-Git-Tag: krb5-1.18-beta1~70 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2b29619aa27c2e63fea80cac60b5607a3fce972f;p=thirdparty%2Fkrb5.git Move S4U2Proxy client code to s4u_creds.c Add an internal libkrb5 interface k5_get_proxy_cred_from_kdc(), which implements S4U2Proxy requests synchronously. Call it from krb5_get_credentials() if constrained delegation is requested. [ghudson@mit.edu: rewrote commit message; made style changes] ticket: 8479 --- diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c index 0a04d68b99..e0a3b5cd8a 100644 --- a/src/lib/krb5/krb/get_creds.c +++ b/src/lib/krb5/krb/get_creds.c @@ -332,8 +332,7 @@ make_request_for_service(krb5_context context, krb5_tkt_creds_context ctx, extra_options = ctx->req_kdcopt; /* Automatically set the enc-tkt-in-skey flag for user-to-user requests. */ - if (ctx->in_creds->second_ticket.length != 0 && - (extra_options & KDC_OPT_CNAME_IN_ADDL_TKT) == 0) + if (ctx->in_creds->second_ticket.length != 0) extra_options |= KDC_OPT_ENC_TKT_IN_SKEY; /* Set the canonicalize flag for referral requests. */ @@ -444,12 +443,6 @@ complete(krb5_context context, krb5_tkt_creds_context ctx) (void) krb5_cc_store_cred(context, ctx->ccache, ctx->reply_creds); } - /* If we were doing constrained delegation, make sure we got a forwardable - * ticket, or it won't work. */ - if ((ctx->req_options & KRB5_GC_CONSTRAINED_DELEGATION) - && (ctx->reply_creds->ticket_flags & TKT_FLG_FORWARDABLE) == 0) - return KRB5_TKT_NOT_FORWARDABLE; - ctx->state = STATE_COMPLETE; return 0; } @@ -1023,11 +1016,6 @@ check_cache(krb5_context context, krb5_tkt_creds_context ctx) krb5_creds mcreds; krb5_flags fields; - /* For constrained delegation, the expected result is in second_ticket, so - * we can't really do a cache check here. */ - if (ctx->req_options & KRB5_GC_CONSTRAINED_DELEGATION) - return (ctx->req_options & KRB5_GC_CACHED) ? KRB5_CC_NOTFOUND : 0; - /* Perform the cache lookup. */ code = krb5int_construct_matching_creds(context, ctx->req_options, ctx->in_creds, &mcreds, &fields); @@ -1098,13 +1086,6 @@ krb5_tkt_creds_init(krb5_context context, krb5_ccache ccache, ctx->req_kdcopt |= KDC_OPT_FORWARDABLE; if (options & KRB5_GC_NO_TRANSIT_CHECK) ctx->req_kdcopt |= KDC_OPT_DISABLE_TRANSITED_CHECK; - if (options & KRB5_GC_CONSTRAINED_DELEGATION) { - if (options & KRB5_GC_USER_USER) { - code = EINVAL; - goto cleanup; - } - ctx->req_kdcopt |= KDC_OPT_FORWARDABLE | KDC_OPT_CNAME_IN_ADDL_TKT; - } ctx->state = STATE_BEGIN; @@ -1284,6 +1265,13 @@ krb5_get_credentials(krb5_context context, krb5_flags options, *out_creds = NULL; + /* If S4U2Proxy is requested, use the synchronous implementation in + * s4u_creds.c. */ + if (options & KRB5_GC_CONSTRAINED_DELEGATION) { + return k5_get_proxy_cred_from_kdc(context, options, ccache, in_creds, + out_creds); + } + /* Allocate a container. */ ncreds = k5alloc(sizeof(*ncreds), &code); if (ncreds == NULL) diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h index d45adab57b..fe61bebf5b 100644 --- a/src/lib/krb5/krb/int-proto.h +++ b/src/lib/krb5/krb/int-proto.h @@ -376,4 +376,14 @@ krb5_error_code k5_get_etype_info(krb5_context context, krb5_init_creds_context ctx, krb5_pa_data **padata); +/* + * Make an S4U2Proxy (constrained delegation) request. in_creds->client is the + * impersonator principal, and in_creds->second_ticket is the evidence + * ticket. + */ +krb5_error_code +k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options, + krb5_ccache ccache, krb5_creds *in_creds, + krb5_creds **out_creds); + #endif /* KRB5_INT_FUNC_PROTO__ */ diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c index 283417a058..26c15fedc2 100644 --- a/src/lib/krb5/krb/s4u_creds.c +++ b/src/lib/krb5/krb/s4u_creds.c @@ -725,6 +725,128 @@ cleanup: return code; } +/* Set *tgt_out to a local TGT for the client realm retrieved from ccache. */ +static krb5_error_code +get_client_tgt(krb5_context context, krb5_flags options, krb5_ccache ccache, + krb5_principal client, krb5_creds **tgt_out) +{ + krb5_error_code code; + krb5_principal tgs; + krb5_creds mcreds; + + *tgt_out = NULL; + + code = krb5int_tgtname(context, &client->realm, &client->realm, &tgs); + if (code) + return code; + + memset(&mcreds, 0, sizeof(mcreds)); + mcreds.client = client; + mcreds.server = tgs; + code = krb5_get_credentials(context, options, ccache, &mcreds, tgt_out); + krb5_free_principal(context, tgs); + return code; +} + +/* Copy req_server to *out_server. If req_server has the referral realm, set + * the realm of *out_server to realm. */ +static krb5_error_code +normalize_server_princ(krb5_context context, const krb5_data *realm, + krb5_principal req_server, krb5_principal *out_server) +{ + krb5_error_code code; + krb5_principal server; + + *out_server = NULL; + + code = krb5_copy_principal(context, req_server, &server); + if (code) + return code; + + if (krb5_is_referral_realm(&server->realm)) { + krb5_free_data_contents(context, &server->realm); + code = krb5int_copy_data_contents(context, realm, &server->realm); + if (code) { + krb5_free_principal(context, server); + return code; + } + } + + *out_server = server; + return 0; +} + +krb5_error_code +k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options, + krb5_ccache ccache, krb5_creds *in_creds, + krb5_creds **out_creds) +{ + krb5_error_code code; + krb5_flags kdcopt, flags; + krb5_principal server = NULL; + krb5_creds mcreds, *tgt = NULL, *tkt = NULL; + + *out_creds = NULL; + + if (in_creds->second_ticket.length == 0 || + (options & KRB5_GC_CONSTRAINED_DELEGATION) == 0) + return EINVAL; + + options &= ~KRB5_GC_CONSTRAINED_DELEGATION; + + code = get_client_tgt(context, options, ccache, in_creds->client, &tgt); + if (code) + goto cleanup; + + code = normalize_server_princ(context, &in_creds->client->realm, + in_creds->server, &server); + if (code) + goto cleanup; + + kdcopt = KDC_OPT_CNAME_IN_ADDL_TKT; + if (options & KRB5_GC_CANONICALIZE) + kdcopt |= KDC_OPT_CANONICALIZE; + if (options & KRB5_GC_FORWARDABLE) + kdcopt |= KDC_OPT_FORWARDABLE; + if (options & KRB5_GC_NO_TRANSIT_CHECK) + kdcopt |= KDC_OPT_DISABLE_TRANSITED_CHECK; + + mcreds = *in_creds; + mcreds.server = server; + + flags = kdcopt | FLAGS2OPTS(tgt->ticket_flags); + code = krb5_get_cred_via_tkt_ext(context, tgt, flags, tgt->addresses, + NULL, &mcreds, NULL, NULL, NULL, NULL, + &tkt, NULL); + if (code) + goto cleanup; + + if (!krb5_principal_compare(context, server, tkt->server)) { + code = KRB5KRB_AP_WRONG_PRINC; + goto cleanup; + } + + if (!krb5_principal_compare(context, in_creds->server, tkt->server)) { + krb5_free_principal(context, tkt->server); + tkt->server = NULL; + code = krb5_copy_principal(context, in_creds->server, &tkt->server); + if (code) + goto cleanup; + } + + if (!(options & KRB5_GC_NO_STORE)) + (void)krb5_cc_store_cred(context, ccache, tkt); + + *out_creds = tkt; + tkt = NULL; + +cleanup: + krb5_free_creds(context, tgt); + krb5_free_creds(context, tkt); + krb5_free_principal(context, server); + return code; +} + /* * Exported API for constrained delegation (S4U2Proxy). * @@ -801,11 +923,9 @@ krb5_get_credentials_for_proxy(krb5_context context, s4u_creds.client = evidence_tkt->server; s4u_creds.second_ticket = *evidence_tkt_data; - code = krb5_get_credentials(context, - options | KRB5_GC_CONSTRAINED_DELEGATION, - ccache, - &s4u_creds, - out_creds); + code = k5_get_proxy_cred_from_kdc(context, + options | KRB5_GC_CONSTRAINED_DELEGATION, + ccache, &s4u_creds, out_creds); if (code != 0) goto cleanup;