summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2009-11-26 00:05:08 +0000
committerGreg Hudson <ghudson@mit.edu>2009-11-26 00:05:08 +0000
commitdc3ba26a2c9acde7ca4ed9260fdc01997511e478 (patch)
tree53fc40f83d889fff25bc59e7ca9189cfbbc94e07 /src
parent1ea7f1d6b0d7a51468f2258b33ba7b9b657f962e (diff)
downloadkrb5-dc3ba26a2c9acde7ca4ed9260fdc01997511e478.tar.gz
krb5-dc3ba26a2c9acde7ca4ed9260fdc01997511e478.tar.xz
krb5-dc3ba26a2c9acde7ca4ed9260fdc01997511e478.zip
libkrb5 support for non-blocking AS requests
Merge Luke's iakerb-libkrb5-as-only branch into trunk with several bug fixes. Adds support for the krb5_init_creds APIs (same as Heimdal's) which allow AS requests to be performed via a different transport than the blocking send_to_kdc. ticket: 6586 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23358 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src')
-rw-r--r--src/include/krb5/krb5.hin52
-rw-r--r--src/lib/krb5/krb/fast.c2
-rw-r--r--src/lib/krb5/krb/gc_via_tkt.c300
-rw-r--r--src/lib/krb5/krb/get_in_tkt.c1275
-rw-r--r--src/lib/krb5/krb/gic_keytab.c12
-rw-r--r--src/lib/krb5/krb/gic_pwd.c29
-rw-r--r--src/lib/krb5/krb/init_creds_ctx.h49
-rw-r--r--src/lib/krb5/krb/int-proto.h53
-rw-r--r--src/lib/krb5/krb/mk_req_ext.c20
-rw-r--r--src/lib/krb5/krb/send_tgs.c124
-rw-r--r--src/lib/krb5/libkrb5.exports11
11 files changed, 1293 insertions, 634 deletions
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 3b86b2328..0f1d40007 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -2261,6 +2261,58 @@ krb5_get_init_creds_password(krb5_context context, krb5_creds *creds,
krb5_deltat start_time, char *in_tkt_service,
krb5_get_init_creds_opt *k5_gic_options);
+struct _krb5_init_creds_context;
+typedef struct _krb5_init_creds_context *krb5_init_creds_context;
+
+void KRB5_CALLCONV
+krb5_init_creds_free(krb5_context context, krb5_init_creds_context ctx);
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx);
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_get_creds(krb5_context context, krb5_init_creds_context ctx,
+ krb5_creds *creds);
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_get_error(krb5_context context, krb5_init_creds_context ctx,
+ krb5_error **error);
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_init(krb5_context context, krb5_principal client,
+ krb5_prompter_fct prompter, void *data,
+ krb5_deltat start_time, krb5_get_init_creds_opt *options,
+ krb5_init_creds_context *ctx);
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_set_keyblock(krb5_context context, krb5_init_creds_context ctx,
+ krb5_keyblock *keyblock);
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_set_keytab(krb5_context context, krb5_init_creds_context ctx,
+ krb5_keytab keytab);
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_step(krb5_context context, krb5_init_creds_context ctx,
+ krb5_data *in, krb5_data *out, krb5_data *realm,
+ unsigned int *flags);
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_set_password(krb5_context context, krb5_init_creds_context ctx,
+ const char *password);
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_set_service(krb5_context context, krb5_init_creds_context ctx,
+ const char *service);
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_store_creds(krb5_context context, krb5_init_creds_context ctx,
+ krb5_ccache ccache);
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_get_times(krb5_context context, krb5_init_creds_context ctx,
+ krb5_ticket_times *times);
+
krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds,
krb5_principal client, krb5_keytab arg_keytab,
diff --git a/src/lib/krb5/krb/fast.c b/src/lib/krb5/krb/fast.c
index 7d6bc274c..884a4ed82 100644
--- a/src/lib/krb5/krb/fast.c
+++ b/src/lib/krb5/krb/fast.c
@@ -373,7 +373,7 @@ krb5int_fast_process_error(krb5_context context,
* ever changed then this will need to be a copy not a cast.
*/
if (retval == 0)
- retval = encode_krb5_typed_data( (krb5_typed_data **) fast_response->padata,
+ retval = encode_krb5_typed_data( (const krb5_typed_data **)fast_response->padata,
&encoded_td);
if (retval == 0) {
fx_error->e_data = *encoded_td;
diff --git a/src/lib/krb5/krb/gc_via_tkt.c b/src/lib/krb5/krb/gc_via_tkt.c
index 8b202d259..df54621a1 100644
--- a/src/lib/krb5/krb/gc_via_tkt.c
+++ b/src/lib/krb5/krb/gc_via_tkt.c
@@ -167,33 +167,29 @@ krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt,
}
krb5_error_code
-krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt,
- krb5_flags kdcoptions, krb5_address *const *address,
- krb5_pa_data **in_padata,
- krb5_creds *in_cred,
- krb5_error_code (*pacb_fct)(krb5_context,
- krb5_keyblock *,
- krb5_kdc_req *,
- void *),
- void *pacb_data,
- krb5_pa_data ***out_padata,
- krb5_pa_data ***out_enc_padata,
- krb5_creds **out_cred,
- krb5_keyblock **out_subkey)
+krb5int_make_tgs_request(krb5_context context,
+ krb5_creds *tkt,
+ krb5_flags kdcoptions,
+ krb5_address *const *address,
+ krb5_pa_data **in_padata,
+ krb5_creds *in_cred,
+ krb5_error_code (*pacb_fct)(krb5_context,
+ krb5_keyblock *,
+ krb5_kdc_req *,
+ void *),
+ void *pacb_data,
+ krb5_data *request_data,
+ krb5_timestamp *timestamp,
+ krb5_int32 *nonce,
+ krb5_keyblock **subkey)
{
krb5_error_code retval;
- krb5_kdc_rep *dec_rep;
- krb5_error *err_reply;
- krb5_response tgsrep;
- krb5_enctype *enctypes = 0;
- krb5_keyblock *subkey = NULL;
- krb5_boolean s4u2self = FALSE, second_tkt;
+ krb5_enctype *enctypes = NULL;
+ krb5_boolean second_tkt;
-#ifdef DEBUG_REFERRALS
- printf("krb5_get_cred_via_tkt starting; referral flag is %s\n", kdcoptions&KDC_OPT_CANONICALIZE?"on":"off");
- krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt requested ticket", in_cred->server);
- krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt TGT in use", tkt->server);
-#endif
+ request_data->data = NULL;
+ *timestamp = 0;
+ *subkey = NULL;
/* tkt->client must be equal to in_cred->client */
if (!krb5_principal_compare(context, tkt->client, in_cred->client))
@@ -202,74 +198,64 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt,
if (!tkt->ticket.length)
return KRB5_NO_TKT_SUPPLIED;
- second_tkt = ((kdcoptions & (KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) != 0);
-
+ second_tkt = ((kdcoptions & (KDC_OPT_ENC_TKT_IN_SKEY |
+ KDC_OPT_CNAME_IN_ADDL_TKT)) != 0);
if (second_tkt && !in_cred->second_ticket.length)
- return(KRB5_NO_2ND_TKT);
-
- s4u2self = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_S4U_X509_USER) ||
- krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FOR_USER);
-
- /* check if we have the right TGT */
- /* tkt->server must be equal to */
- /* krbtgt/realmof(cred->server)@realmof(tgt->server) */
-/*
- {
- krb5_principal tempprinc;
- if (retval = krb5_tgtname(context,
- krb5_princ_realm(context, in_cred->server),
- krb5_princ_realm(context, tkt->server), &tempprinc))
- return(retval);
-
- if (!krb5_principal_compare(context, tempprinc, tkt->server)) {
- krb5_free_principal(context, tempprinc);
- return (KRB5_PRINC_NOMATCH);
- }
- krb5_free_principal(context, tempprinc);
- }
-*/
+ return KRB5_NO_2ND_TKT;
if (in_cred->keyblock.enctype) {
- enctypes = (krb5_enctype *) malloc(sizeof(krb5_enctype)*2);
- if (!enctypes)
+ enctypes = (krb5_enctype *)malloc(sizeof(krb5_enctype)*2);
+ if (enctypes == NULL)
return ENOMEM;
enctypes[0] = in_cred->keyblock.enctype;
enctypes[1] = 0;
}
- retval = krb5int_send_tgs(context, kdcoptions, &in_cred->times, enctypes,
- in_cred->server, address, in_cred->authdata,
- in_padata,
- second_tkt ? &in_cred->second_ticket : NULL,
- tkt, pacb_fct, pacb_data, &tgsrep, &subkey);
- if (enctypes)
+ retval = krb5int_make_tgs_request_ext(context, kdcoptions, &in_cred->times,
+ enctypes, in_cred->server, address,
+ in_cred->authdata, in_padata,
+ second_tkt ?
+ &in_cred->second_ticket : 0,
+ tkt, pacb_fct, pacb_data,
+ request_data,
+ timestamp, nonce, subkey);
+ if (enctypes != NULL)
free(enctypes);
- if (retval) {
-#ifdef DEBUG_REFERRALS
- printf("krb5_get_cred_via_tkt ending early after send_tgs with: %s\n",
- error_message(retval));
-#endif
- return retval;
- }
-
- switch (tgsrep.message_type) {
- case KRB5_TGS_REP:
- break;
- case KRB5_ERROR:
- default:
- if (krb5_is_krb_error(&tgsrep.response))
- retval = decode_krb5_error(&tgsrep.response, &err_reply);
- else
- retval = KRB5KRB_AP_ERR_MSG_TYPE;
- if (retval) /* neither proper reply nor error! */
- goto error_4;
+ return retval;
+}
+krb5_error_code
+krb5int_process_tgs_reply(krb5_context context,
+ krb5_data *response_data,
+ krb5_creds *tkt,
+ krb5_flags kdcoptions,
+ krb5_address *const *address,
+ krb5_pa_data **in_padata,
+ krb5_creds *in_cred,
+ krb5_timestamp timestamp,
+ krb5_int32 nonce,
+ krb5_keyblock *subkey,
+ krb5_pa_data ***out_padata,
+ krb5_pa_data ***out_enc_padata,
+ krb5_creds **out_cred)
+{
+ krb5_error_code retval;
+ krb5_kdc_rep *dec_rep = NULL;
+ krb5_error *err_reply = NULL;
+ krb5_boolean s4u2self;
+
+ s4u2self = krb5int_find_pa_data(context, in_padata,
+ KRB5_PADATA_S4U_X509_USER) ||
+ krb5int_find_pa_data(context, in_padata,
+ KRB5_PADATA_FOR_USER);
+
+ if (krb5_is_krb_error(response_data)) {
+ retval = decode_krb5_error(response_data, &err_reply);
+ if (retval != 0)
+ goto cleanup;
retval = (krb5_error_code) err_reply->error + ERROR_TABLE_BASE_krb5;
if (err_reply->text.length > 0) {
-#if 0
- const char *m;
-#endif
switch (err_reply->error) {
case KRB_ERR_GENERIC:
krb5_set_error_message(context, retval,
@@ -280,7 +266,8 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt,
case KDC_ERR_S_PRINCIPAL_UNKNOWN:
{
char *s_name;
- if (krb5_unparse_name(context, in_cred->server, &s_name) == 0) {
+ if (err_reply->server &&
+ krb5_unparse_name(context, err_reply->server, &s_name) == 0) {
krb5_set_error_message(context, retval,
"Server %s not found in Kerberos database",
s_name);
@@ -291,45 +278,33 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt,
krb5_clear_error_message(context);
}
break;
- default:
-#if 0 /* We should stop the KDC from sending back this text, because
- if the local language doesn't match the KDC's language, we'd
- just wind up printing out the error message in two languages.
- Well, when we get some localization. Which is already
- happening in KfM. */
- m = error_message(retval);
- /* Special case: MIT KDC may return this same string
- in the e-text field. */
- if (strlen (m) == err_reply->text.length-1
- && !strcmp(m, err_reply->text.data))
- break;
- krb5_set_error_message(context, retval,
- "%s (KDC supplied additional data: %s)",
- m, err_reply->text.data);
-#endif
- break;
}
}
-
krb5_free_error(context, err_reply);
- goto error_4;
+ goto cleanup;
+ } else if (!krb5_is_tgs_rep(response_data)) {
+ retval = KRB5KRB_AP_ERR_MSG_TYPE;
+ goto cleanup;
}
/* Unfortunately, Heimdal at least up through 1.2 encrypts using
the session key not the subsession key. So we try both. */
- if ((retval = krb5int_decode_tgs_rep(context, &tgsrep.response,
- subkey,
- KRB5_KEYUSAGE_TGS_REP_ENCPART_SUBKEY, &dec_rep))) {
- if ((krb5int_decode_tgs_rep(context, &tgsrep.response,
+ retval = krb5int_decode_tgs_rep(context, response_data,
+ subkey,
+ KRB5_KEYUSAGE_TGS_REP_ENCPART_SUBKEY,
+ &dec_rep);
+ if (retval) {
+ if ((krb5int_decode_tgs_rep(context, response_data,
&tkt->keyblock,
KRB5_KEYUSAGE_TGS_REP_ENCPART_SESSKEY, &dec_rep)) == 0)
retval = 0;
- else goto error_4;
+ else
+ goto cleanup;
}
if (dec_rep->msg_type != KRB5_TGS_REP) {
retval = KRB5KRB_AP_ERR_MSG_TYPE;
- goto error_3;
+ goto cleanup;
}
/*
@@ -358,7 +333,7 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt,
if (retval == 0)
retval = check_reply_server(context, kdcoptions, in_cred, dec_rep);
- if (dec_rep->enc_part2->nonce != tgsrep.expected_nonce)
+ if (dec_rep->enc_part2->nonce != nonce)
retval = KRB5_KDCREP_MODIFIED;
if ((kdcoptions & KDC_OPT_POSTDATED) &&
@@ -382,13 +357,13 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt,
retval = KRB5_KDCREP_MODIFIED;
if (retval != 0)
- goto error_3;
+ goto cleanup;
if (!in_cred->times.starttime &&
!in_clock_skew(dec_rep->enc_part2->times.starttime,
- tgsrep.request_time)) {
+ timestamp)) {
retval = KRB5_KDCREP_SKEW;
- goto error_3;
+ goto cleanup;
}
if (out_padata != NULL) {
@@ -401,9 +376,103 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt,
}
retval = krb5_kdcrep2creds(context, dec_rep, address,
- &in_cred->second_ticket, out_cred);
+ &in_cred->second_ticket, out_cred);
+ if (retval != 0)
+ goto cleanup;
+
+cleanup:
+ if (dec_rep != NULL) {
+ memset(dec_rep->enc_part2->session->contents, 0,
+ dec_rep->enc_part2->session->length);
+ krb5_free_kdc_rep(context, dec_rep);
+ }
+
+ return retval;
+}
+
+krb5_error_code
+krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt,
+ krb5_flags kdcoptions, krb5_address *const *address,
+ krb5_pa_data **in_padata,
+ krb5_creds *in_cred,
+ krb5_error_code (*pacb_fct)(krb5_context,
+ krb5_keyblock *,
+ krb5_kdc_req *,
+ void *),
+ void *pacb_data,
+ krb5_pa_data ***out_padata,
+ krb5_pa_data ***out_enc_padata,
+ krb5_creds **out_cred,
+ krb5_keyblock **out_subkey)
+{
+ krb5_error_code retval;
+ krb5_data request_data;
+ krb5_data response_data;
+ krb5_timestamp timestamp;
+ krb5_int32 nonce;
+ krb5_keyblock *subkey = NULL;
+ int tcp_only = 0, use_master = 0;
+
+ request_data.data = NULL;
+ request_data.length = 0;
+ response_data.data = NULL;
+ response_data.length = 0;
+
+#ifdef DEBUG_REFERRALS
+ printf("krb5_get_cred_via_tkt starting; referral flag is %s\n", kdcoptions&KDC_OPT_CANONICALIZE?"on":"off");
+ krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt requested ticket", in_cred->server);
+ krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt TGT in use", tkt->server);
+#endif
+
+ retval = krb5int_make_tgs_request(context, tkt, kdcoptions,
+ address, in_padata, in_cred,
+ pacb_fct, pacb_data,
+ &request_data, &timestamp, &nonce,
+ &subkey);
+ if (retval != 0)
+ goto cleanup;
+
+send_again:
+ use_master = 0;
+ retval = krb5_sendto_kdc(context, &request_data,
+ krb5_princ_realm(context, in_cred->server),
+ &response_data, &use_master, tcp_only);
+ if (retval == 0) {
+ if (krb5_is_krb_error(&response_data)) {
+ if (!tcp_only) {
+ krb5_error *err_reply;
+ retval = decode_krb5_error(&response_data, &err_reply);
+ if (retval != 0)
+ goto cleanup;
+ if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) {
+ tcp_only = 1;
+ krb5_free_error(context, err_reply);
+ krb5_free_data_contents(context, &response_data);
+ goto send_again;
+ }
+ krb5_free_error(context, err_reply);
+ }
+ }
+ } else
+ goto cleanup;
+
+ retval = krb5int_process_tgs_reply(context, &response_data,
+ tkt, kdcoptions, address,
+ in_padata, in_cred,
+ timestamp, nonce, subkey,
+ out_padata,
+ out_enc_padata, out_cred);
+ if (retval != 0)
+ goto cleanup;
+
+cleanup:
+#ifdef DEBUG_REFERRALS
+ printf("krb5_get_cred_via_tkt ending; %s\n", retval?error_message(retval):"no error");
+#endif
+
+ krb5_free_data_contents(context, &request_data);
+ krb5_free_data_contents(context, &response_data);
-error_3:;
if (subkey != NULL) {
if (retval == 0 && out_subkey != NULL)
*out_subkey = subkey;
@@ -411,14 +480,5 @@ error_3:;
krb5_free_keyblock(context, subkey);
}
- memset(dec_rep->enc_part2->session->contents, 0,
- dec_rep->enc_part2->session->length);
- krb5_free_kdc_rep(context, dec_rep);
-
-error_4:;
- free(tgsrep.response.data);
-#ifdef DEBUG_REFERRALS
- printf("krb5_get_cred_via_tkt ending; %s\n", retval?error_message(retval):"no error");
-#endif
return retval;
}
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index d8849ecf4..6ab8afad0 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -34,6 +34,7 @@
#include "int-proto.h"
#include "os-proto.h"
#include "fast.h"
+#include "init_creds_ctx.h"
#if APPLE_PKINIT
#define IN_TKT_DEBUG 0
@@ -1057,462 +1058,729 @@ build_in_tkt_name(krb5_context context,
return ret;
}
+void KRB5_CALLCONV
+krb5_init_creds_free(krb5_context context,
+ krb5_init_creds_context ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ if (ctx->opte != NULL && krb5_gic_opt_is_shadowed(ctx->opte)) {
+ krb5_get_init_creds_opt_free(context,
+ (krb5_get_init_creds_opt *)ctx->opte);
+ }
+ free(ctx->in_tkt_service);
+ zap(ctx->password.data, ctx->password.length);
+ krb5_free_data_contents(context, &ctx->password);
+ krb5_free_error(context, ctx->err_reply);
+ krb5_free_cred_contents(context, &ctx->cred);
+ krb5_free_kdc_req(context, ctx->request);
+ krb5_free_kdc_rep(context, ctx->reply);
+ krb5_free_data(context, ctx->encoded_request_body);
+ krb5_free_data(context, ctx->encoded_previous_request);
+ krb5int_fast_free_state(context, ctx->fast_state);
+ krb5_free_pa_data(context, ctx->preauth_to_use);
+ krb5_free_data_contents(context, &ctx->salt);
+ krb5_free_data_contents(context, &ctx->s2kparams);
+ krb5_free_keyblock_contents(context, &ctx->as_key);
+ free(ctx);
+}
+
+static krb5_error_code
+init_creds_get(krb5_context context,
+ krb5_init_creds_context ctx,
+ int *use_master)
+{
+ krb5_error_code code;
+ krb5_data request;
+ krb5_data reply;
+ krb5_data realm;
+ unsigned int flags = 0;
+ int tcp_only = 0;
+
+ request.length = 0;
+ request.data = NULL;
+ reply.length = 0;
+ reply.data = NULL;
+ realm.length = 0;
+ realm.data = NULL;
+
+ for (;;) {
+ code = krb5_init_creds_step(context,
+ ctx,
+ &reply,
+ &request,
+ &realm,
+ &flags);
+ if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !tcp_only)
+ tcp_only = 1;
+ else if (code != 0 || (flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE))
+ break;
+
+ krb5_free_data_contents(context, &reply);
+
+ code = krb5_sendto_kdc(context, &request, &realm,
+ &reply, use_master, tcp_only);
+ if (code != 0)
+ break;
+
+ krb5_free_data_contents(context, &request);
+ krb5_free_data_contents(context, &realm);
+ }
+
+ krb5_free_data_contents(context, &request);
+ krb5_free_data_contents(context, &reply);
+ krb5_free_data_contents(context, &realm);
+
+ return code;
+}
+
+/* Heimdal API */
krb5_error_code KRB5_CALLCONV
-krb5int_get_init_creds(krb5_context context,
- krb5_creds *creds,
- krb5_principal client,
- krb5_prompter_fct prompter,
- void *prompter_data,
- krb5_deltat start_time,
- char *in_tkt_service,
- krb5_get_init_creds_opt *opts,
- krb5_gic_get_as_key_fct gak_fct,
- void *gak_data,
- int *use_master,
- krb5_kdc_rep **as_reply)
+krb5_init_creds_get(krb5_context context,
+ krb5_init_creds_context ctx)
{
- krb5_error_code ret;
- krb5_kdc_req request;
- krb5_data *encoded_request_body, *encoded_previous_request;
- krb5_pa_data **preauth_to_use, **kdc_padata;
- int tempint;
- char *tempstr;
- krb5_deltat tkt_life;
- krb5_deltat renew_life;
- int loopcount;
- krb5_data salt;
- krb5_data s2kparams;
- krb5_keyblock as_key, encrypting_key;
- krb5_keyblock *strengthen_key = NULL;
- krb5_error *err_reply;
- krb5_kdc_rep *local_as_reply;
- krb5_timestamp time_now;
- krb5_enctype etype = 0;
- krb5_preauth_client_rock get_data_rock;
- int canon_flag = 0;
- krb5_principal_data referred_client;
- krb5_boolean retry = 0;
- struct krb5int_fast_request_state *fast_state = NULL;
- krb5_pa_data **out_padata = NULL;
- krb5_gic_opt_ext *options = NULL;
-
- /* initialize everything which will be freed at cleanup */
-
- s2kparams.data = NULL;
- s2kparams.length = 0;
- request.server = NULL;
- request.ktype = NULL;
- request.addresses = NULL;
- request.padata = NULL;
- encoded_request_body = NULL;
- encoded_previous_request = NULL;
- preauth_to_use = NULL;
- kdc_padata = NULL;
- as_key.length = 0;
- encrypting_key.length = 0;
- encrypting_key.contents = NULL;
- salt.length = 0;
- salt.data = NULL;
+ int use_master = 0;
- local_as_reply = 0;
-#if APPLE_PKINIT
- inTktDebug("krb5_get_init_creds top\n");
-#endif /* APPLE_PKINIT */
+ return init_creds_get(context, ctx, &use_master);
+}
- err_reply = NULL;
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_get_creds(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_creds *creds)
+{
+ if ((ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) == 0)
+ return KRB5_NO_TKT_SUPPLIED;
- /* referred_client is used to rewrite the client realm for referrals */
- referred_client = *client;
- referred_client.realm.data = NULL;
- referred_client.realm.length = 0;
- ret = krb5int_fast_make_state(context, &fast_state);
- if (ret)
+ return krb5int_copy_creds_contents(context, &ctx->cred, creds);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_store_creds(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_ccache ccache)
+{
+ if ((ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) == 0)
+ return KRB5_NO_TKT_SUPPLIED;
+
+ return krb5_cc_store_cred(context, ccache, &ctx->cred);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_get_times(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_ticket_times *times)
+{
+ if ((ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) == 0)
+ return KRB5_NO_TKT_SUPPLIED;
+
+ *times = ctx->cred.times;
+
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_get_error(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_error **error)
+{
+ krb5_error_code code;
+ krb5_error *ret = NULL;
+
+ *error = NULL;
+
+ if (ctx->err_reply == NULL)
+ return 0;
+
+ ret = k5alloc(sizeof(*ret), &code);
+ if (code != 0)
goto cleanup;
- ret = krb5int_gic_opt_to_opte(context, opts, &options, 1,
- "krb5int_get_init_creds");
- if (ret)
+ ret->magic = KV5M_ERROR;
+ ret->ctime = ctx->err_reply->ctime;
+ ret->cusec = ctx->err_reply->cusec;
+ ret->susec = ctx->err_reply->susec;
+ ret->stime = ctx->err_reply->stime;
+ ret->error = ctx->err_reply->error;
+
+ if (ctx->err_reply->client != NULL) {
+ code = krb5_copy_principal(context, ctx->err_reply->client,
+ &ret->client);
+ if (code != 0)
+ goto cleanup;
+ }
+
+ code = krb5_copy_principal(context, ctx->err_reply->server, &ret->server);
+ if (code != 0)
goto cleanup;
- /*
- * Set up the basic request structure
- */
- request.magic = KV5M_KDC_REQ;
- request.msg_type = KRB5_AS_REQ;
+ code = krb5int_copy_data_contents(context, &ctx->err_reply->text,
+ &ret->text);
+ if (code != 0)
+ goto cleanup;
- /* request.nonce is filled in when we send a request to the kdc */
- request.nonce = 0;
+ code = krb5int_copy_data_contents(context, &ctx->err_reply->e_data,
+ &ret->e_data);
+ if (code != 0)
+ goto cleanup;
+
+ *error = ret;
+
+cleanup:
+ if (code != 0)
+ krb5_free_error(context, ret);
+
+ return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_init(krb5_context context,
+ krb5_principal client,
+ krb5_prompter_fct prompter,
+ void *data,
+ krb5_deltat start_time,
+ krb5_get_init_creds_opt *options,
+ krb5_init_creds_context *pctx)
+{
+ krb5_error_code code;
+ krb5_init_creds_context ctx;
+ int tmp;
+ char *str = NULL;
+ krb5_gic_opt_ext *opte;
+
+ ctx = k5alloc(sizeof(*ctx), &code);
+ if (code != 0)
+ goto cleanup;
+
+ ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
+ if (code != 0)
+ goto cleanup;
+
+ code = krb5_copy_principal(context, client, &ctx->request->client);
+ if (code != 0)
+ goto cleanup;
+
+ ctx->prompter = prompter;
+ ctx->prompter_data = data;
+ ctx->gak_fct = krb5_get_as_key_password;
+ ctx->gak_data = &ctx->password;
+
+ ctx->request_time = 0; /* filled in later */
+ ctx->start_time = start_time;
- /* request.padata is filled in later */
+ if (options == NULL) {
+ code = krb5_get_init_creds_opt_alloc(context, &options);
+ if (code != 0)
+ goto cleanup;
+ }
+
+ code = krb5int_gic_opt_to_opte(context, options,
+ &ctx->opte, 1, "krb5_init_creds_init");
+ if (code != 0)
+ goto cleanup;
+
+ opte = ctx->opte;
+
+ code = krb5int_fast_make_state(context, &ctx->fast_state);
+ if (code != 0)
+ goto cleanup;
- request.kdc_options = context->kdc_default_options;
+ ctx->get_data_rock.magic = CLIENT_ROCK_MAGIC;
+ ctx->get_data_rock.etype = &ctx->etype;
+ ctx->get_data_rock.fast_state = ctx->fast_state;
- /* forwardable */
+ /* Initialise request parameters as per krb5_get_init_creds() */
+ ctx->request->kdc_options = context->kdc_default_options;
- if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE))
- tempint = options->forwardable;
- else if ((ret = krb5_libdefault_boolean(context, &client->realm,
- KRB5_CONF_FORWARDABLE, &tempint)) == 0)
+ /* forwaradble */
+ if (opte->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
+ tmp = opte->forwardable;
+ else if (krb5_libdefault_boolean(context, &ctx->request->client->realm,
+ KRB5_CONF_FORWARDABLE, &tmp) == 0)
;
else
- tempint = 0;
- if (tempint)
- request.kdc_options |= KDC_OPT_FORWARDABLE;
+ tmp = 0;
+ if (tmp)
+ ctx->request->kdc_options |= KDC_OPT_FORWARDABLE;
/* proxiable */
-
- if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE))
- tempint = options->proxiable;
- else if ((ret = krb5_libdefault_boolean(context, &client->realm,
- KRB5_CONF_PROXIABLE, &tempint)) == 0)
+ if (opte->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
+ tmp = opte->proxiable;
+ else if (krb5_libdefault_boolean(context, &ctx->request->client->realm,
+ KRB5_CONF_PROXIABLE, &tmp) == 0)
;
else
- tempint = 0;
- if (tempint)
- request.kdc_options |= KDC_OPT_PROXIABLE;
+ tmp = 0;
+ if (tmp)
+ ctx->request->kdc_options |= KDC_OPT_PROXIABLE;
/* canonicalize */
- if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE))
- tempint = 1;
- else if ((ret = krb5_libdefault_boolean(context, &client->realm,
- KRB5_CONF_CANONICALIZE, &tempint)) == 0)
+ if (opte->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE)
+ tmp = 1;
+ else if (krb5_libdefault_boolean(context, &ctx->request->client->realm,
+ KRB5_CONF_CANONICALIZE, &tmp) == 0)
;
else
- tempint = 0;
- if (tempint)
- request.kdc_options |= KDC_OPT_CANONICALIZE;
+ tmp = 0;
+ if (tmp)
+ ctx->request->kdc_options |= KDC_OPT_CANONICALIZE;
/* allow_postdate */
-
- if (start_time > 0)
- request.kdc_options |= (KDC_OPT_ALLOW_POSTDATE|KDC_OPT_POSTDATED);
+ if (ctx->start_time > 0)
+ ctx->request->kdc_options |= KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED;
/* ticket lifetime */
+ if (opte->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
+ ctx->tkt_life = options->tkt_life;
+ else if (krb5_libdefault_string(context, &ctx->request->client->realm,
+ KRB5_CONF_TICKET_LIFETIME, &str) == 0) {
+ code = krb5_string_to_deltat(str, &ctx->tkt_life);
+ if (code != 0)
+ goto cleanup;
+ free(str);
+ str = NULL;
+ } else
+ ctx->tkt_life = 24 * 60 * 60; /* previously hardcoded in kinit */
- if ((ret = krb5_timeofday(context, &request.from)))
+ /* renewable lifetime */
+ if (opte->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)
+ ctx->renew_life = options->renew_life;
+ else if (krb5_libdefault_string(context, &ctx->request->client->realm,
+ KRB5_CONF_RENEW_LIFETIME, &str) == 0) {
+ code = krb5_string_to_deltat(str, &ctx->renew_life);
+ if (code != 0)
+ goto cleanup;
+ free(str);
+ str = NULL;
+ } else
+ ctx->renew_life = 0;
+
+ if (ctx->renew_life > 0)
+ ctx->request->kdc_options |= KDC_OPT_RENEWABLE;
+
+ /* enctypes */
+ if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
+ ctx->request->ktype =
+ k5alloc((opte->etype_list_length * sizeof(krb5_enctype)),
+ &code);
+ if (code != 0)
+ goto cleanup;
+ ctx->request->nktypes = opte->etype_list_length;
+ memcpy(ctx->request->ktype, opte->etype_list,
+ ctx->request->nktypes * sizeof(krb5_enctype));
+ } else if (krb5_get_default_in_tkt_ktypes(context,
+ &ctx->request->ktype) == 0) {
+ for (ctx->request->nktypes = 0;
+ ctx->request->ktype[ctx->request->nktypes] != ENCTYPE_NULL;
+ ctx->request->nktypes++)
+ ;
+ } else {
+ /* there isn't any useful default here. */
+ code = KRB5_CONFIG_ETYPE_NOSUPP;
goto cleanup;
- request.from = krb5int_addint32(request.from, start_time);
-
- if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)) {
- tkt_life = options->tkt_life;
- } else if ((ret = krb5_libdefault_string(context, &client->realm,
- KRB5_CONF_TICKET_LIFETIME, &tempstr))
- == 0) {
- ret = krb5_string_to_deltat(tempstr, &tkt_life);
- free(tempstr);
- if (ret) {
+ }
+
+ /* addresess */
+ if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
+ code = krb5_copy_addresses(context, opte->address_list,
+ &ctx->request->addresses);
+ if (code != 0)
goto cleanup;
- }
+ } else if (krb5_libdefault_boolean(context, &ctx->request->client->realm,
+ KRB5_CONF_NOADDRESSES, &tmp) != 0
+ || tmp) {
+ ctx->request->addresses = NULL;
} else {
- /* this used to be hardcoded in kinit.c */
- tkt_life = 24*60*60;
+ code = krb5_os_localaddr(context, &ctx->request->addresses);
+ if (code != 0)
+ goto cleanup;
}
- request.till = krb5int_addint32(request.from, tkt_life);
- /* renewable lifetime */
+ /* initial preauth state */
+ krb5_preauth_request_context_init(context);
- if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
- renew_life = options->renew_life;
- } else if ((ret = krb5_libdefault_string(context, &client->realm,
- KRB5_CONF_RENEW_LIFETIME, &tempstr))
- == 0) {
- ret = krb5_string_to_deltat(tempstr, &renew_life);
- free(tempstr);
- if (ret) {
+ if (opte->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
+ code = make_preauth_list(context,
+ opte->preauth_list,
+ opte->preauth_list_length,
+ &ctx->preauth_to_use);
+ if (code != 0)
+ goto cleanup;
+ }
+
+ if (opte->flags & KRB5_GET_INIT_CREDS_OPT_SALT) {
+ code = krb5int_copy_data_contents(context, opte->salt, &ctx->salt);
+ if (code != 0)
goto cleanup;
- }
} else {
- renew_life = 0;
+ ctx->salt.length = SALT_TYPE_AFS_LENGTH;
+ ctx->salt.data = NULL;
}
- if (renew_life > 0)
- request.kdc_options |= KDC_OPT_RENEWABLE;
-
- if (renew_life > 0) {
- request.rtime = krb5int_addint32(request.from, renew_life);
- if (request.rtime < request.till) {
- /* don't ask for a smaller renewable time than the lifetime */
- request.rtime = request.till;
+
+ /* nonce */
+ {
+ unsigned char random_buf[4];
+ krb5_data random_data;
+
+ random_data.length = sizeof(random_buf);
+ random_data.data = (char *)random_buf;
+
+ /*
+ * See RT ticket 3196 at MIT. If we set the high bit, we
+ * may have compatibility problems with Heimdal, because
+ * we (incorrectly) encode this value as signed.
+ */
+ if (krb5_c_random_make_octets(context, &random_data) == 0)
+ ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
+ else {
+ krb5_timestamp now;
+
+ code = krb5_timeofday(context, &now);
+ if (code != 0)
+ goto cleanup;
+
+ ctx->request->nonce = (krb5_int32)now;
}
- /* we are already asking for renewable tickets so strip this option */
- request.kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
- } else {
- request.rtime = 0;
}
- /* client */
+ ctx->loopcount = 0;
- request.client = client;
+ *pctx = ctx;
- /* per referrals draft, enterprise principals imply canonicalization */
- canon_flag = ((request.kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
- client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
+cleanup:
+ if (code != 0)
+ krb5_init_creds_free(context, ctx);
+ if (str != NULL)
+ free(str);
- /* service */
- if ((ret = build_in_tkt_name(context, in_tkt_service,
- request.client, &request.server)))
- goto cleanup;
+ return code;
+}
- krb5_preauth_request_context_init(context);
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_set_service(krb5_context context,
+ krb5_init_creds_context ctx,
+ const char *service)
+{
+ char *s;
+ s = strdup(service);
+ if (s == NULL)
+ return ENOMEM;
- if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) {
- request.ktype = options->etype_list;
- request.nktypes = options->etype_list_length;
- } else if ((ret = krb5_get_default_in_tkt_ktypes(context,
- &request.ktype)) == 0) {
- for (request.nktypes = 0;
- request.ktype[request.nktypes];
- request.nktypes++)
- ;
- } else {
- /* there isn't any useful default here. ret is set from above */
- goto cleanup;
+ free(ctx->in_tkt_service);
+ ctx->in_tkt_service = s;
+
+ return 0;
+}
+
+static krb5_error_code
+init_creds_validate_reply(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_data *reply)
+{
+ krb5_error_code code;
+ krb5_error *error = NULL;
+ krb5_kdc_rep *as_reply = NULL;
+
+ krb5_free_error(context, ctx->err_reply);
+ ctx->err_reply = NULL;
+
+ krb5_free_kdc_rep(context, ctx->reply);
+ ctx->reply = NULL;
+
+ if (krb5_is_krb_error(reply)) {
+ code = decode_krb5_error(reply, &error);
+ if (code != 0)
+ return code;
+
+ assert(error != NULL);
+
+ if (error->error == KRB_ERR_RESPONSE_TOO_BIG) {
+ krb5_free_error(context, error);
+ return KRB5KRB_ERR_RESPONSE_TOO_BIG;
+ } else {
+ ctx->err_reply = error;
+ return 0;
+ }
}
- if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)) {
- request.addresses = options->address_list;
+ /*
+ * Check to make sure it isn't a V4 reply.
+ */
+ if (reply->length != 0 && !krb5_is_as_rep(reply)) {
+/* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
+#define V4_KRB_PROT_VERSION 4
+#define V4_AUTH_MSG_ERR_REPLY (5<<1)
+ /* check here for V4 reply */
+ unsigned int t_switch;
+
+ /* From v4 g_in_tkt.c: This used to be
+ switch (pkt_msg_type(rpkt) & ~1) {
+ but SCO 3.2v4 cc compiled that incorrectly. */
+ t_switch = reply->data[1];
+ t_switch &= ~1;
+
+ if (t_switch == V4_AUTH_MSG_ERR_REPLY
+ && reply->data[0] == V4_KRB_PROT_VERSION) {
+ code = KRB5KRB_AP_ERR_V4_REPLY;
+ } else {
+ code = KRB5KRB_AP_ERR_MSG_TYPE;
+ }
+ return code;
}
- /* it would be nice if this parsed out an address list, but
- that would be work. */
- else if (((ret = krb5_libdefault_boolean(context, &client->realm,
- KRB5_CONF_NOADDRESSES, &tempint)) != 0)
- || (tempint == 1)) {
- ;
- } else {
- if ((ret = krb5_os_localaddr(context, &request.addresses)))
- goto cleanup;
+
+ /* It must be a KRB_AS_REP message, or an bad returned packet */
+ code = decode_krb5_as_rep(reply, &as_reply);
+ if (code != 0)
+ return code;
+
+ if (as_reply->msg_type != KRB5_AS_REP) {
+ krb5_free_kdc_rep(context, as_reply);
+ return KRB5KRB_AP_ERR_MSG_TYPE;
}
- request.authorization_data.ciphertext.length = 0;
- request.authorization_data.ciphertext.data = 0;
- request.unenc_authdata = 0;
- request.second_ticket = 0;
+ ctx->reply = as_reply;
+
+ return 0;
+}
+
+static krb5_error_code
+init_creds_step_request(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_data *out)
+{
+ krb5_error_code code;
- /* set up the other state. */
+ krb5_free_principal(context, ctx->request->server);
+ ctx->request->server = NULL;
- if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
- if ((ret = make_preauth_list(context, options->preauth_list,
- options->preauth_list_length,
- &preauth_to_use)))
+ code = build_in_tkt_name(context, ctx->in_tkt_service,
+ ctx->request->client,
+ &ctx->request->server);
+ if (code != 0)
+ goto cleanup;
+
+ if (ctx->loopcount == 0) {
+ code = krb5_timeofday(context, &ctx->request_time);
+ if (code != 0)
+ goto cleanup;
+
+ code = krb5int_fast_as_armor(context, ctx->fast_state,
+ ctx->opte, ctx->request);
+ if (code != 0)
goto cleanup;
- }
- /* the salt is allocated from somewhere, unless it is from the caller,
- then it is a reference */
+ /* give the preauth plugins a chance to prep the request body */
+ krb5_preauth_prepare_request(context, ctx->opte, ctx->request);
+ code = krb5int_fast_prep_req_body(context, ctx->fast_state,
+ ctx->request,
+ &ctx->encoded_request_body);
+ if (code != 0)
+ goto cleanup;
- if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)) {
- salt = *options->salt;
+ ctx->request->from = krb5int_addint32(ctx->request_time,
+ ctx->start_time);
+ ctx->request->till = krb5int_addint32(ctx->request->from,
+ ctx->tkt_life);
+
+ if (ctx->renew_life > 0) {
+ ctx->request->rtime =
+ krb5int_addint32(ctx->request->from, ctx->renew_life);
+ if (ctx->request->rtime < ctx->request->till) {
+ /* don't ask for a smaller renewable time than the lifetime */
+ ctx->request->rtime = ctx->request->till;
+ }
+ ctx->request->kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
+ } else
+ ctx->request->rtime = 0;
+ } else if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
+ code = KRB5_GET_IN_TKT_LOOP;
+ goto cleanup;
+ }
+
+ if (ctx->err_reply == NULL) {
+ /* either our first attempt, or retrying after PREAUTH_NEEDED */
+ code = krb5_do_preauth(context,
+ ctx->request,
+ ctx->encoded_request_body,
+ ctx->encoded_previous_request,
+ ctx->preauth_to_use,
+ &ctx->request->padata,
+ &ctx->salt,
+ &ctx->s2kparams,
+ &ctx->etype,
+ &ctx->as_key,
+ ctx->prompter,
+ ctx->prompter_data,
+ ctx->gak_fct,
+ ctx->gak_data,
+ &ctx->get_data_rock,
+ ctx->opte);
+ if (code != 0)
+ goto cleanup;
} else {
- salt.length = SALT_TYPE_AFS_LENGTH;
- salt.data = NULL;
+ if (ctx->preauth_to_use != NULL) {
+ /*
+ * Retry after an error other than PREAUTH_NEEDED,
+ * using e-data to figure out what to change.
+ */
+ code = krb5_do_preauth_tryagain(context,
+ ctx->request,
+ ctx->encoded_request_body,
+ ctx->encoded_previous_request,
+ ctx->preauth_to_use,
+ &ctx->request->padata,
+ ctx->err_reply,
+ &ctx->salt,
+ &ctx->s2kparams,
+ &ctx->etype,
+ &ctx->as_key,
+ ctx->prompter,
+ ctx->prompter_data,
+ ctx->gak_fct,
+ ctx->gak_data,
+ &ctx->get_data_rock,
+ ctx->opte);
+ } else {
+ /* No preauth supplied, so can't query the plugins. */
+ code = KRB5KRB_ERR_GENERIC;
+ }
+ if (code != 0) {
+ /* couldn't come up with anything better */
+ code = ctx->err_reply->error + ERROR_TABLE_BASE_krb5;
+ goto cleanup;
+ }
}
+ if (ctx->encoded_previous_request != NULL) {
+ krb5_free_data(context, ctx->encoded_previous_request);
+ ctx->encoded_previous_request = NULL;
+ }
- /* set the request nonce */
- if ((ret = krb5_timeofday(context, &time_now)))
+ code = krb5int_fast_prep_req(context, ctx->fast_state,
+ ctx->request, ctx->encoded_request_body,
+ encode_krb5_as_req,
+ &ctx->encoded_previous_request);
+ if (code != 0)
goto cleanup;
- /*
- * XXX we know they are the same size... and we should do
- * something better than just the current time
- */
- {
- unsigned char random_buf[4];
- krb5_data random_data;
- random_data.length = 4;
- random_data.data = (char *)random_buf;
- if (krb5_c_random_make_octets(context, &random_data) == 0)
- /* See RT ticket 3196 at MIT. If we set the high bit, we
- may have compatibility problems with Heimdal, because
- we (incorrectly) encode this value as signed. */
- request.nonce = 0x7fffffff & load_32_n(random_buf);
- else
- /* XXX Yuck. Old version. */
- request.nonce = (krb5_int32) time_now;
- }
- ret = krb5int_fast_as_armor(context, fast_state, options, &request);
- if (ret != 0)
- goto cleanup;
- /* give the preauth plugins a chance to prep the request body */
- krb5_preauth_prepare_request(context, options, &request);
- ret = krb5int_fast_prep_req_body(context, fast_state,
- &request, &encoded_request_body);
- if (ret)
+ code = krb5int_copy_data_contents(context,
+ ctx->encoded_previous_request,
+ out);
+ if (code != 0)
goto cleanup;
- get_data_rock.magic = CLIENT_ROCK_MAGIC;
- get_data_rock.etype = &etype;
- get_data_rock.fast_state = fast_state;
+cleanup:
+ return code;
+}
- /* now, loop processing preauth data and talking to the kdc */
- for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) {
- if (request.padata) {
- krb5_free_pa_data(context, request.padata);
- request.padata = NULL;
- }
- if (!err_reply) {
- /* either our first attempt, or retrying after PREAUTH_NEEDED */
- if ((ret = krb5_do_preauth(context,
- &request,
- encoded_request_body,
- encoded_previous_request,
- preauth_to_use, &request.padata,
- &salt, &s2kparams, &etype, &as_key,
- prompter, prompter_data,
- gak_fct, gak_data,
- &get_data_rock, options)))
- goto cleanup;
- if (out_padata) {
- krb5_free_pa_data(context, out_padata);
- out_padata = NULL;
- }
- } else {
- if (preauth_to_use != NULL) {
- /*
- * Retry after an error other than PREAUTH_NEEDED,
- * using e-data to figure out what to change.
- */
- ret = krb5_do_preauth_tryagain(context,
- &request,
- encoded_request_body,
- encoded_previous_request,
- preauth_to_use, &request.padata,
- err_reply,
- &salt, &s2kparams, &etype,
- &as_key,
- prompter, prompter_data,
- gak_fct, gak_data,
- &get_data_rock, options);
- } else {
- /* No preauth supplied, so can't query the plug-ins. */
- ret = KRB5KRB_ERR_GENERIC;
- }
- if (ret) {
- /* couldn't come up with anything better */
- ret = err_reply->error + ERROR_TABLE_BASE_krb5;
- }
- krb5_free_error(context, err_reply);
- err_reply = NULL;
- if (ret)
- goto cleanup;
- }
+static krb5_error_code
+init_creds_step_reply(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_data *in)
+{
+ krb5_error_code code;
+ krb5_pa_data **padata = NULL;
+ krb5_pa_data **kdc_padata = NULL;
+ krb5_boolean retry = FALSE;
+ int canon_flag = 0;
+ krb5_keyblock *strengthen_key = NULL;
+ krb5_keyblock encrypting_key;
- if (encoded_previous_request != NULL) {
- krb5_free_data(context, encoded_previous_request);
- encoded_previous_request = NULL;
- }
- ret = krb5int_fast_prep_req(context, fast_state,
- &request, encoded_request_body,
- encode_krb5_as_req, &encoded_previous_request);
- if (ret)
- goto cleanup;
+ encrypting_key.length = 0;
+ encrypting_key.contents = NULL;
- err_reply = 0;
- local_as_reply = 0;
- if ((ret = send_as_request(context, encoded_previous_request,
- krb5_princ_realm(context, request.client), &err_reply,
- &local_as_reply, use_master)))
- goto cleanup;
+ /* process previous KDC response */
+ code = init_creds_validate_reply(context, ctx, in);
+ if (code != 0)
+ goto cleanup;
- if (err_reply) {
- ret = krb5int_fast_process_error(context, fast_state, &err_reply,
- &out_padata, &retry);
- if (ret !=0)
- goto cleanup;
- if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED && retry) {
- /* reset the list of preauth types to try */
- if (preauth_to_use) {
- krb5_free_pa_data(context, preauth_to_use);
- preauth_to_use = NULL;
- }
- preauth_to_use = out_padata;
- out_padata = NULL;
- krb5_free_error(context, err_reply);
- err_reply = NULL;
- ret = sort_krb5_padata_sequence(context,
- &request.server->realm,
- preauth_to_use);
- if (ret)
- goto cleanup;
- /* continue to next iteration */
- } else if (canon_flag && err_reply->error == KDC_ERR_WRONG_REALM) {
- if (err_reply->client == NULL ||
- err_reply->client->realm.length == 0) {
- ret = KRB5KDC_ERR_WRONG_REALM;
- krb5_free_error(context, err_reply);
- goto cleanup;
- }
- /* Rewrite request.client with realm from error reply */
- if (referred_client.realm.data) {
- krb5_free_data_contents(context, &referred_client.realm);
- referred_client.realm.data = NULL;
- }
- ret = krb5int_copy_data_contents(context,
- &err_reply->client->realm,
- &referred_client.realm);
- krb5_free_error(context, err_reply);
- err_reply = NULL;
- if (ret)
- goto cleanup;
- request.client = &referred_client;
+ /* per referrals draft, enterprise principals imply canonicalization */
+ canon_flag = ((ctx->request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
+ ctx->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
- krb5_free_principal(context, request.server);
- request.server = NULL;
+ if (ctx->err_reply != NULL) {
+ code = krb5int_fast_process_error(context, ctx->fast_state,
+ &ctx->err_reply, &padata, &retry);
+ if (code != 0)
+ goto cleanup;
- ret = build_in_tkt_name(context, in_tkt_service,
- request.client, &request.server);
- if (ret)
- goto cleanup;
- } else {
- if (retry) {
- /* continue to next iteration */
- } else {
- /* error + no hints = give up */
- ret = (krb5_error_code) err_reply->error
- + ERROR_TABLE_BASE_krb5;
- krb5_free_error(context, err_reply);
- goto cleanup;
- }
+ if (ctx->err_reply->error == KDC_ERR_PREAUTH_REQUIRED && retry) {
+ /* reset the list of preauth types to try */
+ krb5_free_pa_data(context, ctx->preauth_to_use);
+ ctx->preauth_to_use = padata;
+ padata = NULL;
+ /* this will trigger a new call to krb5_do_preauth() */
+ krb5_free_error(context, ctx->err_reply);
+ ctx->err_reply = NULL;
+ code = sort_krb5_padata_sequence(context,
+ &ctx->request->client->realm,
+ ctx->preauth_to_use);
+
+ } else if (canon_flag && ctx->err_reply->error == KDC_ERR_WRONG_REALM) {
+ if (ctx->err_reply->client == NULL ||
+ !krb5_princ_realm(context, ctx->err_reply->client)->length) {
+ code = KRB5KDC_ERR_WRONG_REALM;
+ goto cleanup;
}
- } else if (local_as_reply) {
- break;
+ /* Rewrite request.client with realm from error reply */
+ krb5_free_data_contents(context, &ctx->request->client->realm);
+ code = krb5int_copy_data_contents(context,
+ &ctx->err_reply->client->realm,
+ &ctx->request->client->realm);
+ /* this will trigger a new call to krb5_do_preauth() */
+ krb5_free_error(context, ctx->err_reply);
+ ctx->err_reply = NULL;
} else {
- ret = KRB5KRB_AP_ERR_MSG_TYPE;
- goto cleanup;
+ if (retry) {
+ code = 0;
+ } else {
+ /* error + no hints = give up */
+ code = (krb5_error_code)ctx->err_reply->error +
+ ERROR_TABLE_BASE_krb5;
+ }
}
- }
-#if APPLE_PKINIT
- inTktDebug("krb5_get_init_creds done with send_as_request loop lc %d\n",
- (int)loopcount);
-#endif /* APPLE_PKINIT */
- if (loopcount == MAX_IN_TKT_LOOPS) {
- ret = KRB5_GET_IN_TKT_LOOP;
+ /* Return error code, or continue with next iteration */
goto cleanup;
}
+ /* We have a response. Process it. */
+ assert(ctx->reply != NULL);
+
/* process any preauth data in the as_reply */
krb5_clear_preauth_context_use_counts(context);
- ret = krb5int_fast_process_response(context, fast_state,
- local_as_reply, &strengthen_key);
- if (ret)
+ code = krb5int_fast_process_response(context, ctx->fast_state,
+ ctx->reply, &strengthen_key);
+ if (code != 0)
goto cleanup;
- if ((ret = sort_krb5_padata_sequence(context, &request.server->realm,
- local_as_reply->padata)))
+
+ code = sort_krb5_padata_sequence(context, &ctx->request->client->realm,
+ ctx->reply->padata);
+ if (code != 0)
goto cleanup;
- etype = local_as_reply->enc_part.enctype;
- if ((ret = krb5_do_preauth(context,
- &request,
- encoded_request_body, encoded_previous_request,
- local_as_reply->padata, &kdc_padata,
- &salt, &s2kparams, &etype, &as_key, prompter,
- prompter_data, gak_fct, gak_data,
- &get_data_rock, options))) {
-#if APPLE_PKINIT
- inTktDebug("krb5_get_init_creds krb5_do_preauth returned %d\n", (int)ret);
-#endif /* APPLE_PKINIT */
+
+ ctx->etype = ctx->reply->enc_part.enctype;
+
+ code = krb5_do_preauth(context,
+ ctx->request,
+ ctx->encoded_request_body,
+ ctx->encoded_previous_request,
+ ctx->reply->padata,
+ &kdc_padata,
+ &ctx->salt,
+ &ctx->s2kparams,
+ &ctx->etype,
+ &ctx->as_key,
+ ctx->prompter,
+ ctx->prompter_data,
+ ctx->gak_fct,
+ ctx->gak_data,
+ &ctx->get_data_rock,
+ ctx->opte);
+ if (code != 0)
goto cleanup;
- }
/*
* If we haven't gotten a salt from another source yet, set up one
@@ -1524,9 +1792,9 @@ krb5int_get_init_creds(krb5_context context,
* salt. local_as_reply->client will be checked later on in
* verify_as_reply.
*/
- if (salt.length == SALT_TYPE_AFS_LENGTH && salt.data == NULL) {
- ret = krb5_principal2salt(context, local_as_reply->client, &salt);
- if (ret)
+ if (ctx->salt.length == SALT_TYPE_AFS_LENGTH && ctx->salt.data == NULL) {
+ code = krb5_principal2salt(context, ctx->reply->client, &ctx->salt);
+ if (code != 0)
goto cleanup;
}
@@ -1542,117 +1810,196 @@ krb5int_get_init_creds(krb5_context context,
it. If decrypting the as_rep fails, or if there isn't an
as_key at all yet, then use the gak_fct to get one, and try
again. */
- if (as_key.length) {
- ret = krb5int_fast_reply_key(context, strengthen_key, &as_key,
+ if (ctx->as_key.length) {
+ code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
&encrypting_key);
- if (ret)
+ if (code != 0)
goto cleanup;
- ret = decrypt_as_reply(context, NULL, local_as_reply, NULL,
- NULL, &encrypting_key, krb5_kdc_rep_decrypt_proc,
- NULL);
+ code = decrypt_as_reply(context, NULL, ctx->reply, NULL, NULL,
+ &encrypting_key, krb5_kdc_rep_decrypt_proc,
+ NULL);
} else
- ret = -1;
+ code = -1;
- if (ret) {
+ if (code != 0) {
/* if we haven't get gotten a key, get it now */
-
- if ((ret = ((*gak_fct)(context, request.client,
- local_as_reply->enc_part.enctype,
- prompter, prompter_data, &salt, &s2kparams,
- &as_key, gak_data))))
+ code = (*ctx->gak_fct)(context, ctx->request->client,
+ ctx->reply->enc_part.enctype,
+ ctx->prompter, ctx->prompter_data,
+ &ctx->salt, &ctx->s2kparams,
+ &ctx->as_key, ctx->gak_data);
+ if (code != 0)
goto cleanup;
- ret = krb5int_fast_reply_key(context, strengthen_key, &as_key,
- &encrypting_key);
- if (ret)
+ code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
+ &encrypting_key);
+ if (code != 0)
goto cleanup;
- if ((ret = decrypt_as_reply(context, NULL, local_as_reply, NULL,
- NULL, &encrypting_key, krb5_kdc_rep_decrypt_proc,
- NULL)))
+
+ code = decrypt_as_reply(context, NULL, ctx->reply, NULL, NULL,
+ &encrypting_key, krb5_kdc_rep_decrypt_proc,
+ NULL);
+ if (code != 0)
goto cleanup;
}
- if ((ret = verify_as_reply(context, time_now, &request, local_as_reply)))
+ code = verify_as_reply(context, ctx->request_time,
+ ctx->request, ctx->reply);
+ if (code != 0)
goto cleanup;
- /* XXX this should be inside stash_as_reply, but as long as
- get_in_tkt is still around using that arg as an in/out, I can't
- do that */
- memset(creds, 0, sizeof(*creds));
-
- if ((ret = stash_as_reply(context, time_now, &request, local_as_reply,
- creds, NULL)))
+ code = stash_as_reply(context, ctx->request_time, ctx->request,
+ ctx->reply, &ctx->cred, NULL);
+ if (code != 0)
goto cleanup;
- /* success */
+ krb5_preauth_request_context_fini(context);
- ret = 0;
+ /* success */
+ code = 0;
+ ctx->flags |= KRB5_INIT_CREDS_STEP_FLAG_COMPLETE;
cleanup:
- if (ret != 0) {
- char *client_name;
- /* See if we can produce a more detailed error message. */
- switch (ret) {
- case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
- client_name = NULL;
- if (krb5_unparse_name(context, client, &client_name) == 0) {
- krb5_set_error_message(context, ret,
- "Client '%s' not found in Kerberos database",
- client_name);
- free(client_name);
+ krb5_free_pa_data(context, padata);
+ krb5_free_pa_data(context, kdc_padata);
+ krb5_free_keyblock(context, strengthen_key);
+ krb5_free_keyblock_contents(context, &encrypting_key);
+
+ return code;
+}
+
+/*
+ * Do next step of credentials acquisition.
+ *
+ * On success returns 0 or KRB5KRB_ERR_RESPONSE_TOO_BIG if the request
+ * should be sent with TCP.
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_init_creds_step(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_data *in,
+ krb5_data *out,
+ krb5_data *realm,
+ unsigned int *flags)
+{
+ krb5_error_code code, code2;
+
+ *flags = 0;
+
+ out->data = NULL;
+ out->length = 0;
+
+ realm->data = NULL;
+ realm->length = 0;
+
+ if (ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE)
+ goto cleanup;
+
+ if (in->length != 0) {
+ code = init_creds_step_reply(context, ctx, in);
+ if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
+ code2 = krb5int_copy_data_contents(context,
+ ctx->encoded_previous_request,
+ out);
+ if (code2 != 0) {
+ code = code2;
+ goto cleanup;
}
- break;
- default:
- break;
+ goto copy_realm;
}
+ if (code != 0 || (ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE))
+ goto cleanup;
}
- krb5_preauth_request_context_fini(context);
- krb5_free_keyblock(context, strengthen_key);
- if (encrypting_key.contents)
- krb5_free_keyblock_contents(context, &encrypting_key);
- if (fast_state)
- krb5int_fast_free_state(context, fast_state);
- if (out_padata)
- krb5_free_pa_data(context, out_padata);
- if (encoded_previous_request != NULL) {
- krb5_free_data(context, encoded_previous_request);
- encoded_previous_request = NULL;
+
+ code = init_creds_step_request(context, ctx, out);
+ if (code != 0)
+ goto cleanup;
+
+ /* Only a new request increments the loop count, not a TCP retry */
+ ctx->loopcount++;
+
+copy_realm:
+ assert(ctx->request->server != NULL);
+
+ code2 = krb5int_copy_data_contents(context,
+ &ctx->request->server->realm,
+ realm);
+ if (code2 != 0) {
+ code = code2;
+ goto cleanup;
+ }
+
+cleanup:
+ if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) {
+ char *client_name;
+
+ /* See if we can produce a more detailed error message */
+ code2 = krb5_unparse_name(context, ctx->request->client, &client_name);
+ if (code2 == 0) {
+ krb5_set_error_message(context, code,
+ "Client '%s' not found in Kerberos database",
+ client_name);
+ krb5_free_unparsed_name(context, client_name);
+ }
}
- if (encoded_request_body != NULL) {
- krb5_free_data(context, encoded_request_body);
- encoded_request_body = NULL;
+
+ *flags = (ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE);
+
+ return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5int_get_init_creds(krb5_context context,
+ krb5_creds *creds,
+ krb5_principal client,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ krb5_deltat start_time,
+ char *in_tkt_service,
+ krb5_get_init_creds_opt *options,
+ krb5_gic_get_as_key_fct gak_fct,
+ void *gak_data,
+ int *use_master,
+ krb5_kdc_rep **as_reply)
+{
+ krb5_error_code code;
+ krb5_init_creds_context ctx = NULL;
+
+ code = krb5_init_creds_init(context,
+ client,
+ prompter,
+ prompter_data,
+ start_time,
+ options,
+ &ctx);
+ if (code != 0)
+ goto cleanup;
+
+ ctx->gak_fct = gak_fct;
+ ctx->gak_data = gak_data;
+
+ if (in_tkt_service) {
+ code = krb5_init_creds_set_service(context, ctx, in_tkt_service);
+ if (code != 0)
+ goto cleanup;
}
- if (request.server)
- krb5_free_principal(context, request.server);
- if (request.ktype &&
- (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))))
- free(request.ktype);
- if (request.addresses &&
- (!(options &&
- (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST))))
- krb5_free_addresses(context, request.addresses);
- if (preauth_to_use)
- krb5_free_pa_data(context, preauth_to_use);
- if (kdc_padata)
- krb5_free_pa_data(context, kdc_padata);
- if (request.padata)
- krb5_free_pa_data(context, request.padata);
- if (as_key.length)
- krb5_free_keyblock_contents(context, &as_key);
- if (salt.data &&
- (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT))))
- free(salt.data);
- krb5_free_data_contents(context, &s2kparams);
- if (as_reply)
- *as_reply = local_as_reply;
- else if (local_as_reply)
- krb5_free_kdc_rep(context, local_as_reply);
- if (referred_client.realm.data)
- krb5_free_data_contents(context, &referred_client.realm);
- if (krb5_gic_opt_is_shadowed(options)) {
- krb5_get_init_creds_opt_free(context,
- (krb5_get_init_creds_opt *)options);
+
+ code = init_creds_get(context, ctx, use_master);
+ if (code != 0)
+ goto cleanup;
+
+ code = krb5_init_creds_get_creds(context, ctx, creds);
+ if (code != 0)
+ goto cleanup;
+
+ if (as_reply != NULL) {
+ *as_reply = ctx->reply;
+ ctx->reply = NULL;
}
- return(ret);
+cleanup:
+ krb5_init_creds_free(context, ctx);
+
+ return code;
}
+
diff --git a/src/lib/krb5/krb/gic_keytab.c b/src/lib/krb5/krb/gic_keytab.c
index b6341778d..4bdf9ee1b 100644
--- a/src/lib/krb5/krb/gic_keytab.c
+++ b/src/lib/krb5/krb/gic_keytab.c
@@ -27,6 +27,7 @@
#ifndef LEAN_CLIENT
#include "k5-int.h"
+#include "init_creds_ctx.h"
static krb5_error_code
get_as_key_keytab(krb5_context context,
@@ -77,6 +78,17 @@ get_as_key_keytab(krb5_context context,
}
krb5_error_code KRB5_CALLCONV
+krb5_init_creds_set_keytab(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_keytab keytab)
+{
+ ctx->gak_fct = get_as_key_keytab;
+ ctx->gak_data = keytab;
+
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_keytab(krb5_context context,
krb5_creds *creds,
krb5_principal client,
diff --git a/src/lib/krb5/krb/gic_pwd.c b/src/lib/krb5/krb/gic_pwd.c
index aad0a4162..eac2afb28 100644
--- a/src/lib/krb5/krb/gic_pwd.c
+++ b/src/lib/krb5/krb/gic_pwd.c
@@ -1,8 +1,9 @@
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include "k5-int.h"
#include "com_err.h"
+#include "init_creds_ctx.h"
-static krb5_error_code
+krb5_error_code
krb5_get_as_key_password(
krb5_context context,
krb5_principal client,
@@ -39,7 +40,7 @@ krb5_get_as_key_password(
}
}
- if (password->data[0] == '\0') {
+ if (password->length == 0 || password->data[0] == '\0') {
if (prompter == NULL)
return(EIO);
@@ -83,6 +84,30 @@ krb5_get_as_key_password(
}
krb5_error_code KRB5_CALLCONV
+krb5_init_creds_set_password(krb5_context context,
+ krb5_init_creds_context ctx,
+ const char *password)
+{
+ char *s;
+
+ s = strdup(password);
+ if (s == NULL)
+ return ENOMEM;
+
+ if (ctx->password.data != NULL) {
+ zap(ctx->password.data, ctx->password.length);
+ krb5_free_data_contents(context, &ctx->password);
+ }
+
+ ctx->password.data = s;
+ ctx->password.length = strlen(s);
+ ctx->gak_fct = krb5_get_as_key_password;
+ ctx->gak_data = &ctx->password;
+
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_password(krb5_context context,
krb5_creds *creds,
krb5_principal client,
diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h
new file mode 100644
index 000000000..b830684f2
--- /dev/null
+++ b/src/lib/krb5/krb/init_creds_ctx.h
@@ -0,0 +1,49 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+#ifndef KRB5_INIT_CREDS_CONTEXT
+#define KRB5_INIT_CREDS_CONTEXT 1
+
+struct _krb5_init_creds_context {
+ krb5_gic_opt_ext *opte;
+ char *in_tkt_service;
+ krb5_prompter_fct prompter;
+ void *prompter_data;
+ krb5_gic_get_as_key_fct gak_fct;
+ void *gak_data;
+ krb5_timestamp request_time;
+ krb5_deltat start_time;
+ krb5_deltat tkt_life;
+ krb5_deltat renew_life;
+ unsigned int flags;
+ unsigned int loopcount;
+ krb5_data password;
+ krb5_error *err_reply;
+ krb5_creds cred;
+ krb5_kdc_req *request;
+ krb5_kdc_rep *reply;
+ krb5_data *encoded_request_body;
+ krb5_data *encoded_previous_request;
+ struct krb5int_fast_request_state *fast_state;
+ krb5_pa_data **preauth_to_use;
+ krb5_data salt;
+ krb5_data s2kparams;
+ krb5_keyblock as_key;
+ krb5_enctype etype;
+ krb5_preauth_client_rock get_data_rock;
+};
+
+#define KRB5_INIT_CREDS_STEP_FLAG_COMPLETE 0x1
+
+krb5_error_code
+krb5_get_as_key_password(krb5_context context,
+ krb5_principal client,
+ krb5_enctype etype,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ krb5_data *salt,
+ krb5_data *params,
+ krb5_keyblock *as_key,
+ void *gak_data);
+
+#endif /* !KRB5_INIT_CREDS_CONTEXT */
+
diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
index 44ea69d4e..47555d678 100644
--- a/src/lib/krb5/krb/int-proto.h
+++ b/src/lib/krb5/krb/int-proto.h
@@ -85,6 +85,59 @@ krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt,
krb5_creds **out_cred,
krb5_keyblock **out_subkey);
+krb5_error_code
+krb5int_make_tgs_request_ext(krb5_context context,
+ krb5_flags kdcoptions,
+ const krb5_ticket_times *timestruct,
+ const krb5_enctype *ktypes,
+ krb5_const_principal sname,
+ krb5_address *const *addrs,
+ krb5_authdata *const *authorization_data,
+ krb5_pa_data *const *padata,
+ const krb5_data *second_ticket,
+ krb5_creds *in_cred,
+ krb5_error_code (*pacb_fct)(krb5_context,
+ krb5_keyblock *,
+ krb5_kdc_req *,
+ void *),
+ void *pacb_data,
+ krb5_data *request_data,
+ krb5_timestamp *timestamp,
+ krb5_int32 *nonce,
+ krb5_keyblock **subkey);
+
+krb5_error_code
+krb5int_make_tgs_request(krb5_context context,
+ krb5_creds *tkt,
+ krb5_flags kdcoptions,
+ krb5_address *const *address,
+ krb5_pa_data **in_padata,
+ krb5_creds *in_cred,
+ krb5_error_code (*pacb_fct)(krb5_context,
+ krb5_keyblock *,
+ krb5_kdc_req *,
+ void *),
+ void *pacb_data,
+ krb5_data *request_data,
+ krb5_timestamp *timestamp,
+ krb5_int32 *nonce,
+ krb5_keyblock **subkey);
+
+krb5_error_code
+krb5int_process_tgs_reply(krb5_context context,
+ krb5_data *response_data,
+ krb5_creds *tkt,
+ krb5_flags kdcoptions,
+ krb5_address *const *address,
+ krb5_pa_data **in_padata,
+ krb5_creds *in_cred,
+ krb5_timestamp timestamp,
+ krb5_int32 nonce,
+ krb5_keyblock *subkey,
+ krb5_pa_data ***out_padata,
+ krb5_pa_data ***out_enc_padata,
+ krb5_creds **out_cred);
+
krb5_error_code krb5int_send_tgs(krb5_context, krb5_flags,
const krb5_ticket_times *,
const krb5_enctype *,
diff --git a/src/lib/krb5/krb/mk_req_ext.c b/src/lib/krb5/krb/mk_req_ext.c
index 95f04e9a4..c0f9f833d 100644
--- a/src/lib/krb5/krb/mk_req_ext.c
+++ b/src/lib/krb5/krb/mk_req_ext.c
@@ -181,17 +181,7 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
&(*auth_context)->local_seq_number)))
goto cleanup;
-
/* generate subkey if needed */
- if (!in_data &&(*auth_context)->checksum_func) {
- retval = (*auth_context)->checksum_func( context,
- *auth_context,
- (*auth_context)->checksum_func_data,
- &in_data);
- if (retval)
- goto cleanup;
- }
-
if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->send_subkey)) {
retval = krb5int_generate_and_save_subkey (context, *auth_context,
&in_creds->keyblock,
@@ -201,8 +191,16 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
}
- if (in_data) {
+ if (!in_data && (*auth_context)->checksum_func) {
+ retval = (*auth_context)->checksum_func( context,
+ *auth_context,
+ (*auth_context)->checksum_func_data,
+ &in_data);
+ if (retval)
+ goto cleanup;
+ }
+ if (in_data) {
if ((*auth_context)->req_cksumtype == 0x8003) {
/* XXX Special hack for GSSAPI */
checksum.checksum_type = 0x8003;
diff --git a/src/lib/krb5/krb/send_tgs.c b/src/lib/krb5/krb/send_tgs.c
index bee982b08..7980c8454 100644
--- a/src/lib/krb5/krb/send_tgs.c
+++ b/src/lib/krb5/krb/send_tgs.c
@@ -148,19 +148,27 @@ cleanup:
* The pacb_fct callback allows the caller access to the nonce
* and request subkey, for binding preauthentication data
*/
+
krb5_error_code
-krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions,
- const krb5_ticket_times *timestruct, const krb5_enctype *ktypes,
- krb5_const_principal sname, krb5_address *const *addrs,
- krb5_authdata *const *authorization_data,
- krb5_pa_data *const *padata, const krb5_data *second_ticket,
- krb5_creds *in_cred,
- krb5_error_code (*pacb_fct)(krb5_context,
- krb5_keyblock *,
- krb5_kdc_req *,
- void *),
- void *pacb_data,
- krb5_response *rep, krb5_keyblock **subkey)
+krb5int_make_tgs_request_ext(krb5_context context,
+ krb5_flags kdcoptions,
+ const krb5_ticket_times *timestruct,
+ const krb5_enctype *ktypes,
+ krb5_const_principal sname,
+ krb5_address *const *addrs,
+ krb5_authdata *const *authorization_data,
+ krb5_pa_data *const *padata,
+ const krb5_data *second_ticket,
+ krb5_creds *in_cred,
+ krb5_error_code (*pacb_fct)(krb5_context,
+ krb5_keyblock *,
+ krb5_kdc_req *,
+ void *),
+ void *pacb_data,
+ krb5_data *request_data,
+ krb5_timestamp *timestamp,
+ krb5_int32 *nonce,
+ krb5_keyblock **subkey)
{
krb5_error_code retval;
krb5_kdc_req tgsreq;
@@ -170,7 +178,6 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions,
krb5_timestamp time_now;
krb5_pa_data **combined_padata = NULL;
krb5_pa_data ap_req_padata;
- int tcp_only = 0, use_master;
krb5_keyblock *local_subkey = NULL;
assert (subkey != NULL);
@@ -195,10 +202,8 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions,
if ((retval = krb5_timeofday(context, &time_now)))
return(retval);
/* XXX we know they are the same size... */
- rep->expected_nonce = tgsreq.nonce = (krb5_int32) time_now;
- rep->request_time = time_now;
- rep->message_type = KRB5_ERROR; /*caller only uses the response
- * element on successful return*/
+ *nonce = tgsreq.nonce = (krb5_int32)time_now;
+ *timestamp = time_now;
tgsreq.addresses = (krb5_address **) addrs;
@@ -332,9 +337,71 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions,
krb5_free_pa_data(context, tgsreq.padata);
tgsreq.padata = NULL;
+ *request_data = *scratch;
+ free(scratch);
+ scratch = NULL;
+
+send_tgs_error_2:;
+ if (tgsreq.padata)
+ krb5_free_pa_data(context, tgsreq.padata);
+ if (sec_ticket)
+ krb5_free_ticket(context, sec_ticket);
+
+send_tgs_error_1:;
+ if (ktypes == NULL)
+ free(tgsreq.ktype);
+ if (tgsreq.authorization_data.ciphertext.data) {
+ memset(tgsreq.authorization_data.ciphertext.data, 0,
+ tgsreq.authorization_data.ciphertext.length);
+ free(tgsreq.authorization_data.ciphertext.data);
+ }
+
+ if (retval)
+ krb5_free_keyblock(context, local_subkey);
+ else
+ *subkey = local_subkey;
+
+ return retval;
+
+}
+
+krb5_error_code
+krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions,
+ const krb5_ticket_times *timestruct, const krb5_enctype *ktypes,
+ krb5_const_principal sname, krb5_address *const *addrs,
+ krb5_authdata *const *authorization_data,
+ krb5_pa_data *const *padata, const krb5_data *second_ticket,
+ krb5_creds *in_cred,
+ krb5_error_code (*pacb_fct)(krb5_context,
+ krb5_keyblock *,
+ krb5_kdc_req *,
+ void *),
+ void *pacb_data,
+ krb5_response *rep, krb5_keyblock **subkey)
+{
+ krb5_error_code retval;
+ krb5_data scratch;
+ int tcp_only = 0, use_master;
+ krb5_timestamp now;
+ krb5_int32 nonce;
+
+ rep->message_type = KRB5_ERROR;
+
+ retval = krb5int_make_tgs_request_ext(context, kdcoptions, timestruct,
+ ktypes, sname, addrs,
+ authorization_data, padata,
+ second_ticket, in_cred,
+ pacb_fct, pacb_data, &scratch, &now,
+ &nonce, subkey);
+ if (retval != 0)
+ return retval;
+
+ rep->expected_nonce = nonce;
+ rep->request_time = now;
+
send_again:
use_master = 0;
- retval = krb5_sendto_kdc(context, scratch,
+ retval = krb5_sendto_kdc(context, &scratch,
krb5_princ_realm(context, sname),
&rep->response, &use_master, tcp_only);
if (retval == 0) {
@@ -358,30 +425,15 @@ send_again:
rep->message_type = KRB5_ERROR;
} else if (krb5_is_tgs_rep(&rep->response)) {
rep->message_type = KRB5_TGS_REP;
- *subkey = local_subkey;
} else /* XXX: assume it's an error */
rep->message_type = KRB5_ERROR;
}
- krb5_free_data(context, scratch);
-
-send_tgs_error_2:;
- if (tgsreq.padata)
- krb5_free_pa_data(context, tgsreq.padata);
- if (sec_ticket)
- krb5_free_ticket(context, sec_ticket);
-
-send_tgs_error_1:;
- if (ktypes == NULL)
- free(tgsreq.ktype);
- if (tgsreq.authorization_data.ciphertext.data) {
- memset(tgsreq.authorization_data.ciphertext.data, 0,
- tgsreq.authorization_data.ciphertext.length);
- free(tgsreq.authorization_data.ciphertext.data);
- }
- if (rep->message_type != KRB5_TGS_REP && local_subkey){
+ if (rep->message_type != KRB5_TGS_REP && *subkey){
krb5_free_keyblock(context, *subkey);
+ *subkey = NULL;
}
+ krb5_free_data_contents(context, &scratch);
return retval;
}
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index 26b3da61d..8ea6c0223 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -354,6 +354,17 @@ krb5_get_tgs_ktypes
krb5_get_time_offsets
krb5_get_validated_creds
krb5_init_context
+krb5_init_creds_free
+krb5_init_creds_get
+krb5_init_creds_get_creds
+krb5_init_creds_get_error
+krb5_init_creds_get_times
+krb5_init_creds_init
+krb5_init_creds_set_keytab
+krb5_init_creds_set_password
+krb5_init_creds_set_service
+krb5_init_creds_step
+krb5_init_creds_store_creds
krb5_init_keyblock
krb5_init_secure_context
krb5_internalize_opaque