Fast Pkinit
Sam Hartman
hartmans at MIT.EDU
Wed Sep 21 13:31:39 EDT 2011
Hi. I got most of the way through enabling pkinit for FAST.
Unfortunately, I ran out of time and it's still not working. The
problem is that when you get into verify_anonymous, krb5_c_decrypt
fails to decrypt. Presumably either the padata or encrypting_key is
wrong.
I don't have time to debug more now, but I thought others might be
interested.
>From 614716a65cd37b566d805d229a8a16e41919b433 Mon Sep 17 00:00:00 2001
From: Sam Hartman <hartmans at debian.org>
Date: Wed, 21 Sep 2011 09:34:23 -0400
Subject: [PATCH] Enable FAST Pkinit
Per RFC 6113 fast should use the inner request body for the pkinit
checksum. We did that on the KDC; now do so on the client. Remove
code that explicitly blocked pkinit under FAST.
Add FAST pkinit test
---
src/lib/krb5/krb/fast.c | 11 +--------
src/lib/krb5/krb/get_in_tkt.c | 37 +++++++++++++++++++++--------
src/lib/krb5/krb/init_creds_ctx.h | 12 +++++++++-
src/plugins/preauth/pkinit/pkinit_clnt.c | 10 --------
src/plugins/preauth/pkinit/pkinit_srv.c | 35 +--------------------------
src/tests/t_anonpkinit.py | 2 +
6 files changed, 43 insertions(+), 64 deletions(-)
diff --git a/src/lib/krb5/krb/fast.c b/src/lib/krb5/krb/fast.c
index 2290d48..bdd9435 100644
--- a/src/lib/krb5/krb/fast.c
+++ b/src/lib/krb5/krb/fast.c
@@ -207,8 +207,6 @@ krb5int_fast_prep_req(krb5_context context,
krb5_data *encoded_fast_req = NULL;
krb5_data *encoded_armored_req = NULL;
krb5_data *local_encoded_result = NULL;
- krb5_data random_data;
- char random_buf[4];
assert(state != NULL);
assert(state->fast_outer_request.padata == NULL);
@@ -218,15 +216,8 @@ krb5int_fast_prep_req(krb5_context context,
}
TRACE_FAST_ENCODE(context);
- /* Fill in a fresh random nonce for each inner request*/
- random_data.length = 4;
- random_data.data = (char *)random_buf;
- retval = krb5_c_random_make_octets(context, &random_data);
- if (retval == 0) {
- request->nonce = 0x7fffffff & load_32_n(random_buf);
state->nonce = request->nonce;
- }
- fast_req.req_body = request;
+ fast_req.req_body = request;
if (fast_req.req_body->padata == NULL) {
fast_req.req_body->padata = calloc(1, sizeof(krb5_pa_data *));
if (fast_req.req_body->padata == NULL)
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index 2968bd7..44ad0ed 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -529,7 +529,8 @@ krb5_init_creds_free(krb5_context context,
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->outer_request_body);
+ krb5_free_data(context, ctx->inner_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);
@@ -707,9 +708,9 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
goto cleanup;
ctx->preauth_rock.fast_state = ctx->fast_state;
krb5_preauth_request_context_init(context);
- if (ctx->encoded_request_body) {
- krb5_free_data(context, ctx->encoded_request_body);
- ctx->encoded_request_body = NULL;
+ if (ctx->outer_request_body) {
+ krb5_free_data(context, ctx->outer_request_body);
+ ctx->outer_request_body = NULL;
}
if (ctx->opte &&
(ctx->opte->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
@@ -774,7 +775,7 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
ctx->request->rtime = 0;
code = krb5int_fast_prep_req_body(context, ctx->fast_state,
ctx->request,
- &ctx->encoded_request_body);
+ &ctx->outer_request_body);
if (code != 0)
goto cleanup;
cleanup:
@@ -1094,17 +1095,33 @@ init_creds_step_request(krb5_context context,
krb5_data *out)
{
krb5_error_code code;
+ char random_buf[4];
+ krb5_data random_data;
if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
code = KRB5_GET_IN_TKT_LOOP;
goto cleanup;
}
+ /*
+ * RFC 6113 requires a new nonce for the inner request on each try. It's
+ * permitted to change the nonce even for non-FAST so we do here.
+ */
+ random_data.length = 4;
+ random_data.data = (char *)random_buf;
+ code = krb5_c_random_make_octets(context, &random_data);
+ if (code !=0)
+ goto cleanup;
+ krb5_free_data(context, ctx->inner_request_body);
+ ctx->inner_request_body = NULL;
+ code = encode_krb5_kdc_req_body(ctx->request, &ctx->inner_request_body);
+ if (code)
+ 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->inner_request_body,
ctx->encoded_previous_request,
ctx->preauth_to_use,
&ctx->request->padata,
@@ -1128,7 +1145,7 @@ init_creds_step_request(krb5_context context,
*/
code = krb5_do_preauth_tryagain(context,
ctx->request,
- ctx->encoded_request_body,
+ ctx->inner_request_body,
ctx->encoded_previous_request,
ctx->preauth_to_use,
&ctx->request->padata,
@@ -1165,7 +1182,7 @@ init_creds_step_request(krb5_context context,
if (code)
goto cleanup;
code = krb5int_fast_prep_req(context, ctx->fast_state,
- ctx->request, ctx->encoded_request_body,
+ ctx->request, ctx->outer_request_body,
encode_krb5_as_req,
&ctx->encoded_previous_request);
if (code != 0)
@@ -1361,7 +1378,7 @@ init_creds_step_reply(krb5_context context,
code = krb5_do_preauth(context,
ctx->request,
- ctx->encoded_request_body,
+ ctx->inner_request_body,
ctx->encoded_previous_request,
ctx->reply->padata,
&kdc_padata,
@@ -1453,7 +1470,7 @@ init_creds_step_reply(krb5_context context,
if (code != 0)
goto cleanup;
code = verify_anonymous(context, ctx->request, ctx->reply,
- &encrypting_key);
+ &ctx->as_key);
if (code)
goto cleanup;
diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h
index de43163..e95a500 100644
--- a/src/lib/krb5/krb/init_creds_ctx.h
+++ b/src/lib/krb5/krb/init_creds_ctx.h
@@ -21,7 +21,17 @@ struct _krb5_init_creds_context {
krb5_creds cred;
krb5_kdc_req *request;
krb5_kdc_rep *reply;
- krb5_data *encoded_request_body;
+ /**
+ * Stores the outer request body in order to feed into FAST for checksumming.
+ * This is maintained even if FAST is not used. This is not used for
+ * preauth: that requires the inner request body. For AS-only FAST it would
+ * be better for krb5int_fast_prep_req() to simply generate this. However
+ * for TGS FAST, the client needs to supply the to_be_checksummed
+ * data. Whether this should be refactored should be revisited as TGS fast
+ * is integrated.
+ */
+ krb5_data *outer_request_body;
+ krb5_data *inner_request_body; /**< For preauth */
krb5_data *encoded_previous_request;
struct krb5int_fast_request_state *fast_state;
krb5_pa_data **preauth_to_use;
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
index 6877f54..5aa3155 100644
--- a/src/plugins/preauth/pkinit/pkinit_clnt.c
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -41,8 +41,6 @@
#include "pkinit.h"
-/* Remove when FAST PKINIT is settled. */
-#include "../fast_factor.h"
/*
* It is anticipated that all the special checks currently
@@ -1036,18 +1034,10 @@ pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
int processing_request = 0;
pkinit_context plgctx = (pkinit_context)moddata;
pkinit_req_context reqctx = (pkinit_req_context)modreq;
- krb5_keyblock *armor_key = NULL;
pkiDebug("pkinit_client_process %p %p %p %p\n",
context, plgctx, reqctx, request);
- /* Remove (along with armor_key) when FAST PKINIT is settled. */
- retval = fast_get_armor_key(context, get_data_proc, rock, &armor_key);
- if (retval == 0 && armor_key != NULL) {
- /* Don't use PKINIT if also using FAST. */
- krb5_free_keyblock(context, armor_key);
- return EINVAL;
- }
if (plgctx == NULL || reqctx == NULL)
return EINVAL;
diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c
index a74268c..09a6200 100644
--- a/src/plugins/preauth/pkinit/pkinit_srv.c
+++ b/src/plugins/preauth/pkinit/pkinit_srv.c
@@ -37,8 +37,6 @@
#include <k5-int.h>
#include "pkinit.h"
-/* Remove when FAST PKINIT is settled. */
-#include "../fast_factor.h"
static krb5_error_code
pkinit_init_kdc_req_context(krb5_context, pkinit_kdc_req_context *blob);
@@ -109,18 +107,9 @@ pkinit_server_get_edata(krb5_context context,
{
krb5_error_code retval = 0;
pkinit_kdc_context plgctx = NULL;
- krb5_keyblock *armor_key = NULL;
pkiDebug("pkinit_server_get_edata: entered!\n");
- /* Remove (along with armor_key) when FAST PKINIT is settled. */
- retval = fast_kdc_get_armor_key(context, server_get_entry_data, request,
- client, &armor_key);
- if (retval == 0 && armor_key != NULL) {
- /* Don't advertise PKINIT if the client used FAST. */
- krb5_free_keyblock(context, armor_key);
- return EINVAL;
- }
/*
* If we don't have a realm context for the given realm,
@@ -315,20 +304,11 @@ pkinit_server_verify_padata(krb5_context context,
krb5_kdc_req *tmp_as_req = NULL;
krb5_data k5data;
int is_signed = 1;
- krb5_keyblock *armor_key;
pkiDebug("pkinit_verify_padata: entered!\n");
if (data == NULL || data->length <= 0 || data->contents == NULL)
return 0;
- /* Remove (along with armor_key) when FAST PKINIT is settled. */
- retval = fast_kdc_get_armor_key(context, server_get_entry_data, request,
- client, &armor_key);
- if (retval == 0 && armor_key != NULL) {
- /* Don't allow PKINIT if the client used FAST. */
- krb5_free_keyblock(context, armor_key);
- return EINVAL;
- }
if (moddata == NULL || e_data == NULL)
return EINVAL;
@@ -464,19 +444,8 @@ pkinit_server_verify_padata(krb5_context context,
"value not supported."));
goto cleanup;
}
- /*
- * The KDC may have modified the request after decoding it.
- * We need to compute the checksum on the data that
- * came from the client. Therefore, we use the original
- * packet contents.
- */
- retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req);
- if (retval) {
- pkiDebug("decode_krb5_as_req returned %d\n", (int)retval);
- goto cleanup;
- }
-
- retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req);
+ retval = server_get_entry_data(context, request, client,
+ krb5_kdcpreauth_request_body, &der_req);
if (retval) {
pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
goto cleanup;
diff --git a/src/tests/t_anonpkinit.py b/src/tests/t_anonpkinit.py
index ddb9691..064269e 100644
--- a/src/tests/t_anonpkinit.py
+++ b/src/tests/t_anonpkinit.py
@@ -42,6 +42,8 @@ realm = K5Realm(krb5_conf=pkinit_krb5_conf, kdc_conf=restrictive_kdc_conf,
create_user=False)
realm.addprinc('WELLKNOWN/ANONYMOUS')
realm.kinit('@%s' % realm.realm, flags=['-n'])
+# now try FAST
+realm.kinit('@%s' % realm.realm, flags=['-n', '-T', realm.ccache])
realm.run_as_client([kvno, realm.host_princ], expected_code=1)
success('Anonymous PKINIT.')
--
1.7.5.4
More information about the krbdev
mailing list