ticket: new
authorSam Hartman <hartmans@mit.edu>
Wed, 23 Nov 2011 01:00:27 +0000 (01:00 +0000)
committerSam Hartman <hartmans@mit.edu>
Wed, 23 Nov 2011 01:00:27 +0000 (01:00 +0000)
    subject: FAST PKINIT
    target_version: 1.10
    tags: pullup

    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.

    Also, use the reply key *before* the strengthen key is applied when
    verifying the PADATA_PKINIT_KX.

    Add FAST pkinit test.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25486 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/krb5/krb/fast.c
src/lib/krb5/krb/get_in_tkt.c
src/lib/krb5/krb/init_creds_ctx.h
src/plugins/preauth/pkinit/pkinit_clnt.c
src/plugins/preauth/pkinit/pkinit_srv.c
src/tests/t_anonpkinit.py

index a0db841d50db32da01847693d0fda9b8aea0dc0d..e5eb960be67a64e640933b0d81c2c81f58dfb040 100644 (file)
@@ -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,14 +216,7 @@ 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;
-    }
+    state->nonce = request->nonce;
     fast_req.req_body =  request;
     if (fast_req.req_body->padata == NULL) {
         fast_req.req_body->padata = calloc(1, sizeof(krb5_pa_data *));
index 4237915d8453e2a4e9bd5c665aad816a112dad5d..8351dfd3008bcbed609d9b6b76600b127ae9500c 100644 (file)
@@ -530,7 +530,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);
@@ -708,9 +709,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)) {
@@ -775,7 +776,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:
@@ -1104,17 +1105,39 @@ init_creds_step_request(krb5_context context,
 {
     krb5_error_code code;
     krb5_boolean got_real;
+    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;
+    /*
+     * 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.
+     */
+    ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
+    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,
@@ -1135,7 +1158,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,
@@ -1167,7 +1190,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)
@@ -1366,7 +1389,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 +1476,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;
 
index 604f3f89a580d2a674f61183538743c50a9f2064..48376fccd4edca3c1b92100f2f8d893118593668 100644 (file)
@@ -22,7 +22,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;
index ad354cf0bda29262d72b520785bc16b97c753263..e574c3c8f9a2193956214fd85a2ad4e56f7efb51 100644 (file)
@@ -1029,15 +1029,11 @@ 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 = cb->fast_armor(context, rock), as_key;
+    krb5_keyblock as_key;
 
     pkiDebug("pkinit_client_process %p %p %p %p\n",
              context, plgctx, reqctx, request);
 
-    /* Remove (along with armor_key) when FAST PKINIT is settled. */
-    /* Don't use PKINIT if also using FAST. */
-    if (armor_key != NULL)
-        return EINVAL;
 
     if (plgctx == NULL || reqctx == NULL)
         return EINVAL;
index 44e310cf98e2743c9373a2218a0faf57bb2e69aa..3322310bf5ad9b5d9801e668de3abbf8cfd1f1ce 100644 (file)
@@ -107,16 +107,9 @@ pkinit_server_get_edata(krb5_context context,
 {
     krb5_error_code retval = 0;
     pkinit_kdc_context plgctx = NULL;
-    krb5_keyblock *armor_key = cb->fast_armor(context, rock);
 
     pkiDebug("pkinit_server_get_edata: entered!\n");
 
-    /* Remove (along with armor_key) when FAST PKINIT is settled. */
-    /* Don't advertise PKINIT if the client used FAST. */
-    if (armor_key != NULL) {
-        (*respond)(arg, EINVAL, NULL);
-        return;
-    }
 
     /*
      * If we don't have a realm context for the given realm,
@@ -309,7 +302,6 @@ 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 = cb->fast_armor(context, rock);
     krb5_pa_data **e_data = NULL;
     krb5_kdcpreauth_modreq modreq = NULL;
 
@@ -319,12 +311,6 @@ pkinit_server_verify_padata(krb5_context context,
         return;
     }
 
-    /* Remove (along with armor_key) when FAST PKINIT is settled. */
-    /* Don't allow PKINIT if the client used FAST. */
-    if (armor_key != NULL) {
-        (*respond)(arg, EINVAL, NULL, NULL, NULL);
-        return;
-    }
 
     if (moddata == NULL) {
         (*respond)(arg, EINVAL, NULL, NULL, NULL);
@@ -462,23 +448,7 @@ 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);
-        if (retval) {
-            pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval);
-            goto cleanup;
-        }
+        der_req = cb->request_body(context, rock);
         retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL,
                                       0, der_req, &cksum);
         if (retval) {
@@ -566,8 +536,6 @@ cleanup:
     case KRB5_PADATA_PK_AS_REQ:
         free_krb5_pa_pk_as_req(&reqp);
         free(cksum.contents);
-        if (der_req != NULL)
-            krb5_free_data(context, der_req);
         break;
     case KRB5_PADATA_PK_AS_REP_OLD:
     case KRB5_PADATA_PK_AS_REQ_OLD:
index 5b2368e12dde42a2b730e3615cc9f85a60128486..7ae955d17e2d3b8167433291eec9ddc7d07452ca 100644 (file)
@@ -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')