krb5 commit: Add PKINIT client support for freshness token

Greg Hudson ghudson at mit.edu
Mon Mar 19 17:12:18 EDT 2018


https://github.com/krb5/krb5/commit/085785362e01467cb25c79a90dcebfba9ea019d8
commit 085785362e01467cb25c79a90dcebfba9ea019d8
Author: Greg Hudson <ghudson at mit.edu>
Date:   Tue Jan 31 17:02:34 2017 -0500

    Add PKINIT client support for freshness token
    
    Send an empty PA_AS_FRESHNESS padata item in unauthenticated AS
    requests to indicate support for RFC 8070.  If the KDC includes a
    PA_AS_FRESHNESS value in its method data, echo it back in the new
    freshnessToken field of pkAuthenticator
    
    ticket: 8648

 doc/user/user_commands/kinit.rst          |    3 +++
 src/include/k5-int-pkinit.h               |    1 +
 src/include/krb5/krb5.hin                 |    1 +
 src/lib/krb5/asn.1/asn1_k_encode.c        |    5 ++++-
 src/lib/krb5/krb/get_in_tkt.c             |   12 ++++++++----
 src/lib/krb5/krb/init_creds_ctx.h         |    2 +-
 src/plugins/preauth/pkinit/pkinit.h       |    3 +++
 src/plugins/preauth/pkinit/pkinit_clnt.c  |   19 ++++++++++++++++++-
 src/plugins/preauth/pkinit/pkinit_lib.c   |    3 +++
 src/plugins/preauth/pkinit/pkinit_trace.h |    2 ++
 src/tests/asn.1/ktest.c                   |    4 ++++
 src/tests/asn.1/pkinit_encode.out         |    2 +-
 src/tests/asn.1/pkinit_trval.out          |    1 +
 13 files changed, 50 insertions(+), 8 deletions(-)

diff --git a/doc/user/user_commands/kinit.rst b/doc/user/user_commands/kinit.rst
index 3f9d534..1f69692 100644
--- a/doc/user/user_commands/kinit.rst
+++ b/doc/user/user_commands/kinit.rst
@@ -197,6 +197,9 @@ OPTIONS
         specify use of RSA, rather than the default Diffie-Hellman
         protocol
 
+    **disable_freshness**\ [**=yes**]
+        disable sending freshness tokens (for testing purposes only)
+
 
 ENVIRONMENT
 -----------
diff --git a/src/include/k5-int-pkinit.h b/src/include/k5-int-pkinit.h
index 7b2f595..4622a62 100644
--- a/src/include/k5-int-pkinit.h
+++ b/src/include/k5-int-pkinit.h
@@ -42,6 +42,7 @@ typedef struct _krb5_pk_authenticator {
     krb5_timestamp  ctime;
     krb5_int32      nonce;  /* (0..4294967295) */
     krb5_checksum   paChecksum;
+    krb5_data      *freshnessToken;
 } krb5_pk_authenticator;
 
 /* PKAuthenticator draft9 */
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 55135fc..bebd9a5 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -1873,6 +1873,7 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype,
 #define KRB5_PADATA_OTP_PIN_CHANGE      144 /**< RFC 6560 section 4.3 */
 #define KRB5_PADATA_PKINIT_KX           147 /**< RFC 6112 */
 #define KRB5_ENCPADATA_REQ_ENC_PA_REP   149 /**< RFC 6806 */
+#define KRB5_PADATA_AS_FRESHNESS        150 /**< RFC 8070 */
 
 #define KRB5_SAM_USE_SAD_AS_KEY         0x80000000
 #define KRB5_SAM_SEND_ENCRYPTED_SAD     0x40000000
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
index 476e2a5..29f6b90 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.c
+++ b/src/lib/krb5/asn.1/asn1_k_encode.c
@@ -1442,9 +1442,12 @@ DEFFIELD(pk_authenticator_1, krb5_pk_authenticator, ctime, 1, kerberos_time);
 DEFFIELD(pk_authenticator_2, krb5_pk_authenticator, nonce, 2, int32);
 DEFFIELD(pk_authenticator_3, krb5_pk_authenticator, paChecksum, 3,
          ostring_checksum);
+DEFFIELD(pk_authenticator_4, krb5_pk_authenticator, freshnessToken, 4,
+         opt_ostring_data_ptr);
 static const struct atype_info *pk_authenticator_fields[] = {
     &k5_atype_pk_authenticator_0, &k5_atype_pk_authenticator_1,
-    &k5_atype_pk_authenticator_2, &k5_atype_pk_authenticator_3
+    &k5_atype_pk_authenticator_2, &k5_atype_pk_authenticator_3,
+    &k5_atype_pk_authenticator_4
 };
 DEFSEQTYPE(pk_authenticator, krb5_pk_authenticator, pk_authenticator_fields);
 
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index 47a00bf..1d96ff1 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -895,7 +895,7 @@ krb5_init_creds_init(krb5_context context,
     ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
     if (code != 0)
         goto cleanup;
-    ctx->enc_pa_rep_permitted = TRUE;
+    ctx->info_pa_permitted = TRUE;
     code = krb5_copy_principal(context, client, &ctx->request->client);
     if (code != 0)
         goto cleanup;
@@ -1389,7 +1389,11 @@ init_creds_step_request(krb5_context context,
         krb5_free_data(context, ctx->encoded_previous_request);
         ctx->encoded_previous_request = NULL;
     }
-    if (ctx->enc_pa_rep_permitted) {
+    if (ctx->info_pa_permitted) {
+        code = add_padata(&ctx->request->padata, KRB5_PADATA_AS_FRESHNESS,
+                          NULL, 0);
+        if (code)
+            goto cleanup;
         code = add_padata(&ctx->request->padata, KRB5_ENCPADATA_REQ_ENC_PA_REP,
                           NULL, 0);
     }
@@ -1530,7 +1534,7 @@ init_creds_step_reply(krb5_context context,
                    ctx->selected_preauth_type == KRB5_PADATA_NONE) {
             /* The KDC didn't like our informational padata (probably a pre-1.7
              * MIT krb5 KDC).  Retry without it. */
-            ctx->enc_pa_rep_permitted = FALSE;
+            ctx->info_pa_permitted = FALSE;
             ctx->restarted = TRUE;
             code = restart_init_creds_loop(context, ctx, FALSE);
         } else if (reply_code == KDC_ERR_PREAUTH_EXPIRED) {
@@ -1574,7 +1578,7 @@ init_creds_step_reply(krb5_context context,
                 goto cleanup;
             /* Reset per-realm negotiation state. */
             ctx->restarted = FALSE;
-            ctx->enc_pa_rep_permitted = TRUE;
+            ctx->info_pa_permitted = TRUE;
             code = restart_init_creds_loop(context, ctx, FALSE);
         } else {
             if (retry && ctx->selected_preauth_type != KRB5_PADATA_NONE) {
diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h
index fe76968..b19410a 100644
--- a/src/lib/krb5/krb/init_creds_ctx.h
+++ b/src/lib/krb5/krb/init_creds_ctx.h
@@ -58,7 +58,7 @@ struct _krb5_init_creds_context {
     krb5_data s2kparams;
     krb5_keyblock as_key;
     krb5_enctype etype;
-    krb5_boolean enc_pa_rep_permitted;
+    krb5_boolean info_pa_permitted;
     krb5_boolean restarted;
     struct krb5_responder_context_st rctx;
     krb5_preauthtype selected_preauth_type;
diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h
index f3de9ad..8489a3e 100644
--- a/src/plugins/preauth/pkinit/pkinit.h
+++ b/src/plugins/preauth/pkinit/pkinit.h
@@ -148,6 +148,7 @@ typedef struct _pkinit_plg_opts {
     int allow_upn;	    /* allow UPN-SAN instead of pkinit-SAN */
     int dh_or_rsa;	    /* selects DH or RSA based pkinit */
     int require_crl_checking; /* require CRL for a CA (default is false) */
+    int disable_freshness;  /* disable freshness token on client for testing */
     int dh_min_bits;	    /* minimum DH modulus size allowed */
 } pkinit_plg_opts;
 
@@ -162,6 +163,7 @@ typedef struct _pkinit_req_opts {
     int require_crl_checking;
     int dh_size;	    /* initial request DH modulus size (default=1024) */
     int require_hostname_match;
+    int disable_freshness;
 } pkinit_req_opts;
 
 /*
@@ -214,6 +216,7 @@ struct _pkinit_req_context {
     int identity_initialized;
     int identity_prompted;
     krb5_error_code identity_prompt_retval;
+    krb5_data *freshness_token;
 };
 typedef struct _pkinit_req_context *pkinit_req_context;
 
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
index 2604800..613f391 100644
--- a/src/plugins/preauth/pkinit/pkinit_clnt.c
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -231,6 +231,8 @@ pkinit_as_req_create(krb5_context context,
         auth_pack.pkAuthenticator.cusec = cusec;
         auth_pack.pkAuthenticator.nonce = nonce;
         auth_pack.pkAuthenticator.paChecksum = *cksum;
+        if (!reqctx->opts->disable_freshness)
+            auth_pack.pkAuthenticator.freshnessToken = reqctx->freshness_token;
         auth_pack.clientDHNonce.length = 0;
         auth_pack.clientPublicValue = &info;
         auth_pack.supportedKDFs = (krb5_data **)supported_kdf_alg_ids;
@@ -1160,6 +1162,7 @@ pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
     pkinit_context plgctx = (pkinit_context)moddata;
     pkinit_req_context reqctx = (pkinit_req_context)modreq;
     krb5_keyblock as_key;
+    krb5_data d;
 
     pkiDebug("pkinit_client_process %p %p %p %p\n",
              context, plgctx, reqctx, request);
@@ -1172,6 +1175,12 @@ pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
     case KRB5_PADATA_PKINIT_KX:
         reqctx->rfc6112_kdc = 1;
         return 0;
+    case KRB5_PADATA_AS_FRESHNESS:
+        TRACE_PKINIT_CLIENT_FRESHNESS_TOKEN(context);
+        krb5_free_data(context, reqctx->freshness_token);
+        reqctx->freshness_token = NULL;
+        d = make_data(in_padata->contents, in_padata->length);
+        return krb5_copy_data(context, &d, &reqctx->freshness_token);
     case KRB5_PADATA_PK_AS_REQ:
         reqctx->rfc4556_kdc = 1;
         pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
@@ -1357,7 +1366,7 @@ cleanup:
 static int
 pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype)
 {
-    if (patype == KRB5_PADATA_PKINIT_KX)
+    if (patype == KRB5_PADATA_PKINIT_KX || patype == KRB5_PADATA_AS_FRESHNESS)
         return PA_INFO;
     return PA_REAL;
 }
@@ -1374,6 +1383,7 @@ static krb5_preauthtype supported_client_pa_types[] = {
     KRB5_PADATA_PK_AS_REP_OLD,
     KRB5_PADATA_PK_AS_REQ_OLD,
     KRB5_PADATA_PKINIT_KX,
+    KRB5_PADATA_AS_FRESHNESS,
     0
 };
 
@@ -1398,6 +1408,7 @@ pkinit_client_req_init(krb5_context context,
     reqctx->opts = NULL;
     reqctx->idctx = NULL;
     reqctx->idopts = NULL;
+    reqctx->freshness_token = NULL;
 
     retval = pkinit_init_req_opts(&reqctx->opts);
     if (retval)
@@ -1408,6 +1419,7 @@ pkinit_client_req_init(krb5_context context,
     reqctx->opts->dh_or_rsa = plgctx->opts->dh_or_rsa;
     reqctx->opts->allow_upn = plgctx->opts->allow_upn;
     reqctx->opts->require_crl_checking = plgctx->opts->require_crl_checking;
+    reqctx->opts->disable_freshness = plgctx->opts->disable_freshness;
 
     retval = pkinit_init_req_crypto(&reqctx->cryptoctx);
     if (retval)
@@ -1466,6 +1478,8 @@ pkinit_client_req_fini(krb5_context context, krb5_clpreauth_moddata moddata,
     if (reqctx->idopts != NULL)
         pkinit_fini_identity_opts(reqctx->idopts);
 
+    krb5_free_data(context, reqctx->freshness_token);
+
     free(reqctx);
     return;
 }
@@ -1578,6 +1592,9 @@ handle_gic_opt(krb5_context context,
             pkiDebug("Setting flag to use RSA_PROTOCOL\n");
             plgctx->opts->dh_or_rsa = RSA_PROTOCOL;
         }
+    } else if (strcmp(attr, "disable_freshness") == 0) {
+        if (strcmp(value, "yes") == 0)
+            plgctx->opts->disable_freshness = 1;
     }
     return 0;
 }
diff --git a/src/plugins/preauth/pkinit/pkinit_lib.c b/src/plugins/preauth/pkinit/pkinit_lib.c
index 2f88545..d5858c4 100644
--- a/src/plugins/preauth/pkinit/pkinit_lib.c
+++ b/src/plugins/preauth/pkinit/pkinit_lib.c
@@ -82,6 +82,8 @@ pkinit_init_plg_opts(pkinit_plg_opts **plgopts)
     opts->dh_or_rsa = DH_PROTOCOL;
     opts->allow_upn = 0;
     opts->require_crl_checking = 0;
+    opts->require_freshness = 0;
+    opts->disable_freshness = 0;
 
     opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS;
 
@@ -145,6 +147,7 @@ free_krb5_auth_pack(krb5_auth_pack **in)
         free((*in)->clientPublicValue);
     }
     free((*in)->pkAuthenticator.paChecksum.contents);
+    krb5_free_data(NULL, (*in)->pkAuthenticator.freshnessToken);
     if ((*in)->supportedCMSTypes != NULL)
         free_krb5_algorithm_identifiers(&((*in)->supportedCMSTypes));
     if ((*in)->supportedKDFs) {
diff --git a/src/plugins/preauth/pkinit/pkinit_trace.h b/src/plugins/preauth/pkinit/pkinit_trace.h
index d4eb39d..67e0cae 100644
--- a/src/plugins/preauth/pkinit/pkinit_trace.h
+++ b/src/plugins/preauth/pkinit/pkinit_trace.h
@@ -41,6 +41,8 @@
     TRACE(c, "PKINIT client found no acceptable EKU in KDC cert")
 #define TRACE_PKINIT_CLIENT_EKU_SKIP(c)                                 \
     TRACE(c, "PKINIT client skipping EKU check due to configuration")
+#define TRACE_PKINIT_CLIENT_FRESHNESS_TOKEN(c)                  \
+    TRACE(c, "PKINIT client received freshness token from KDC")
 #define TRACE_PKINIT_CLIENT_KDF_ALG(c, kdf, keyblock)                   \
     TRACE(c, "PKINIT client used KDF {hexdata} to compute reply key "   \
           "{keyblock}", kdf, keyblock)
diff --git a/src/tests/asn.1/ktest.c b/src/tests/asn.1/ktest.c
index 8487955..5bfdc5b 100644
--- a/src/tests/asn.1/ktest.c
+++ b/src/tests/asn.1/ktest.c
@@ -725,6 +725,8 @@ ktest_make_sample_pk_authenticator(krb5_pk_authenticator *p)
     ktest_make_sample_checksum(&p->paChecksum);
     /* We don't encode the checksum type, only the contents. */
     p->paChecksum.checksum_type = 0;
+    p->freshnessToken = ealloc(sizeof(krb5_data));
+    ktest_make_sample_data(p->freshnessToken);
 }
 
 static void
@@ -1711,6 +1713,8 @@ ktest_empty_pk_authenticator(krb5_pk_authenticator *p)
 {
     ktest_empty_checksum(&p->paChecksum);
     p->paChecksum.contents = NULL;
+    krb5_free_data(NULL, p->freshnessToken);
+    p->freshnessToken = NULL;
 }
 
 static void
diff --git a/src/tests/asn.1/pkinit_encode.out b/src/tests/asn.1/pkinit_encode.out
index 463128d..3b0f719 100644
--- a/src/tests/asn.1/pkinit_encode.out
+++ b/src/tests/asn.1/pkinit_encode.out
@@ -4,7 +4,7 @@ encode_krb5_pa_pk_as_rep(dhInfo): A0 28 30 26 80 08 6B 72 62 35 64 61 74 61 A1 0
 encode_krb5_pa_pk_as_rep(encKeyPack): 81 08 6B 72 62 35 64 61 74 61
 encode_krb5_pa_pk_as_rep_draft9(dhSignedData): 80 08 6B 72 62 35 64 61 74 61
 encode_krb5_pa_pk_as_rep_draft9(encKeyPack): 81 08 6B 72 62 35 64 61 74 61
-encode_krb5_auth_pack: 30 81 93 A0 29 30 27 A0 05 02 03 01 E2 40 A1 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A2 03 02 01 2A A3 06 04 04 31 32 33 34 A1 22 30 20 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 03 09 00 6B 72 62 35 64 61 74 61 A2 24 30 22 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 30 0B 06 09 2A 86 48 86 F7 12 01 02 02 A3 0A 04 08 6B 72 62 35 64 61 74 61 A4 10 30 0E 30 0C A0 0A 06 08 6B 72 62 35 64 61 74 61
+encode_krb5_auth_pack: 30 81 9F A0 35 30 33 A0 05 02 03 01 E2 40 A1 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A2 03 02 01 2A A3 06 04 04 31 32 33 34 A4 0A 04 08 6B 72 62 35 64 61 74 61 A1 22 30 20 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 03 09 00 6B 72 62 35 64 61 74 61 A2 24 30 22 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 30 0B 06 09 2A 86 48 86 F7 12 01 02 02 A3 0A 04 08 6B 72 62 35 64 61 74 61 A4 10 30 0E 30 0C A0 0A 06 08 6B 72 62 35 64 61 74 61
 encode_krb5_auth_pack_draft9: 30 75 A0 4F 30 4D A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 05 02 03 01 E2 40 A3 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A4 03 02 01 2A A1 22 30 20 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 03 09 00 6B 72 62 35 64 61 74 61
 encode_krb5_kdc_dh_key_info: 30 25 A0 0B 03 09 00 6B 72 62 35 64 61 74 61 A1 03 02 01 2A A2 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A
 encode_krb5_reply_key_pack: 30 26 A0 13 30 11 A0 03 02 01 01 A1 0A 04 08 31 32 33 34 35 36 37 38 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34
diff --git a/src/tests/asn.1/pkinit_trval.out b/src/tests/asn.1/pkinit_trval.out
index 58d8706..f9edbe1 100644
--- a/src/tests/asn.1/pkinit_trval.out
+++ b/src/tests/asn.1/pkinit_trval.out
@@ -57,6 +57,7 @@ encode_krb5_auth_pack:
 .  .  [1] [Generalized Time] "19940610060317Z"
 .  .  [2] [Integer] 42
 .  .  [3] [Octet String] "1234"
+.  .  [4] [Octet String] "krb5data"
 .  [1] [Sequence/Sequence Of]
 .  .  [Sequence/Sequence Of]
 .  .  .  [Object Identifier] <9>


More information about the cvs-krb5 mailing list