fast negotiation projec
authorSam Hartman <hartmans@mit.edu>
Mon, 14 Dec 2009 18:28:16 +0000 (18:28 +0000)
committerSam Hartman <hartmans@mit.edu>
Mon, 14 Dec 2009 18:28:16 +0000 (18:28 +0000)
Merge branches/fast-negotiate into trunk.
This implements http://k5wiki.kerberos.org/wiki/Projects/Fast_negotiation

Additional changes:
* krb5_c_make_checksum with checksum type 0 uses mandatory checksum for given key enctype

Conflicts:
src/lib/crypto/krb/make_checksum.c

ticket: 6595
Tags: enhancement

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

22 files changed:
src/clients/kinit/kinit.c
src/clients/klist/klist.c
src/include/k5-int.h
src/include/krb5/krb5.hin
src/kdc/do_as_req.c
src/kdc/do_tgs_req.c
src/kdc/fast_util.c
src/kdc/kdc_preauth.c
src/kdc/kdc_util.c
src/kdc/kdc_util.h
src/lib/crypto/krb/make_checksum.c
src/lib/krb5/asn.1/asn1_k_decode.c
src/lib/krb5/asn.1/asn1_k_encode.c
src/lib/krb5/asn.1/krb5_decode.c
src/lib/krb5/ccache/ccapi/stdcc.c
src/lib/krb5/ccache/ccfns.c
src/lib/krb5/krb/fast.c
src/lib/krb5/krb/fast.h
src/lib/krb5/krb/get_in_tkt.c
src/lib/krb5/krb/gic_opt.c
src/lib/krb5/krb/init_creds_ctx.h
src/lib/krb5/libkrb5.exports

index 96bb9cdccedb638a694b35b89baff72a0a9b1a47..fdfae88286edc64550b6d2b777cd4761bdec5767 100644 (file)
@@ -629,6 +629,9 @@ k5_kinit(opts, k5)
             goto cleanup;
         }
     }
+    code = krb5_get_init_creds_opt_set_out_ccache(k5->ctx, options, k5->cc);
+    if (code)
+        goto cleanup;
 
     switch (opts->action) {
     case INIT_PW:
@@ -678,20 +681,21 @@ k5_kinit(opts, k5)
         goto cleanup;
     }
 
-    code = krb5_cc_initialize(k5->ctx, k5->cc,
-                              opts->canonicalize ? my_creds.client : k5->me);
-    if (code) {
-        com_err(progname, code, "when initializing cache %s",
-                opts->k5_cache_name?opts->k5_cache_name:"");
-        goto cleanup;
-    }
+    if ((opts->action != INIT_PW) && (opts->action != INIT_KT)) {
+        code = krb5_cc_initialize(k5->ctx, k5->cc, opts->canonicalize ?
+                                  my_creds.client : k5->me);
+        if (code) {
+            com_err(progname, code, "when initializing cache %s",
+                    opts->k5_cache_name?opts->k5_cache_name:"");
+            goto cleanup;
+        }
 
-    code = krb5_cc_store_cred(k5->ctx, k5->cc, &my_creds);
-    if (code) {
-        com_err(progname, code, "while storing credentials");
-        goto cleanup;
+        code = krb5_cc_store_cred(k5->ctx, k5->cc, &my_creds);
+        if (code) {
+            com_err(progname, code, "while storing credentials");
+            goto cleanup;
+        }
     }
-
     notix = 0;
 
 cleanup:
index 1a6309eb13204ae2e999d11936641db556c84ead..df4dbd5332dcaf496de47c0d1b1a4b80b0cb5777 100644 (file)
@@ -284,7 +284,7 @@ void do_keytab(name)
         if (show_keys) {
             printf(" (0x");
             {
-                int i;
+                unsigned int i;
                 for (i = 0; i < entry.key.length; i++)
                     printf("%02x", entry.key.contents[i]);
             }
@@ -382,6 +382,8 @@ void do_ccache(name)
         exit(1);
     }
     while (!(code = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) {
+        if (krb5_is_config_principal(kcontext, creds.server))
+            continue;
         if (status_only) {
             if (exit_status && creds.server->length == 2 &&
                 strcmp(creds.server->realm.data, princ->realm.data) == 0 &&
index e0637e69d489536cd0770c3547c62b01a7613a83..f1b48c0afd6984c8acc33ac4119f983211dca377 100644 (file)
@@ -257,6 +257,7 @@ typedef INT64_TYPE krb5_int64;
 #define KRB5_CONF_V4_INSTANCE_CONVERT         "v4_instance_convert"
 #define KRB5_CONF_V4_REALM                    "v4_realm"
 #define KRB5_CONF_ASTERISK                    "*"
+#define KRB5_CONF_FAST_AVAIL                  "fast_avail"
 
 /* Error codes used in KRB_ERROR protocol messages.
    Return values of library routines are based on a different error table
@@ -1129,6 +1130,8 @@ typedef struct _krb5_gic_opt_private {
     int num_preauth_data;
     krb5_gic_opt_pa_data *preauth_data;
     char * fast_ccache_name;
+    krb5_ccache out_ccache;
+    krb5_flags fast_flags;
 } krb5_gic_opt_private;
 
 /*
@@ -1623,6 +1626,8 @@ encode_krb5_enc_priv_part(const krb5_priv_enc_part *rep, krb5_data **code);
 
 krb5_error_code
 encode_krb5_cred(const krb5_cred *rep, krb5_data **code);
+krb5_error_code
+encode_krb5_checksum(const krb5_checksum *, krb5_data **);
 
 krb5_error_code
 encode_krb5_enc_cred_part(const krb5_cred_enc_part *rep, krb5_data **code);
@@ -1851,6 +1856,8 @@ decode_krb5_priv(const krb5_data *output, krb5_priv **rep);
 
 krb5_error_code
 decode_krb5_enc_priv_part(const krb5_data *output, krb5_priv_enc_part **rep);
+krb5_error_code
+decode_krb5_checksum(const krb5_data *, krb5_checksum **);
 
 krb5_error_code
 decode_krb5_cred(const krb5_data *output, krb5_cred **rep);
index 675917052bae43c8a44ac260ea393f7580bb2c33..9e5c3853f5fc4b148155595e9d6acb9765641d72 100644 (file)
@@ -636,8 +636,8 @@ krb5_c_keyed_checksum_types(krb5_context context, krb5_enctype enctype,
 #define KRB5_KEYUSAGE_FAST_FINISHED 53
 #define KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT 54
 #define KRB5_KEYUSAGE_ENC_CHALLENGE_KDC 55
+#define KRB5_KEYUSAGE_AS_REQ 56
 
-#define KRB5_KEYUSAGE_FAST_REP 52
 krb5_boolean KRB5_CALLCONV krb5_c_valid_enctype(krb5_enctype ktype);
 krb5_boolean KRB5_CALLCONV krb5_c_valid_cksumtype(krb5_cksumtype ctype);
 krb5_boolean KRB5_CALLCONV krb5_c_is_coll_proof_cksum(krb5_cksumtype ctype);
@@ -930,7 +930,7 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype,
 #define TKT_FLG_TRANSIT_POLICY_CHECKED  0x00080000
 #define TKT_FLG_OK_AS_DELEGATE          0x00040000
 #define TKT_FLG_ANONYMOUS               0x00020000
-/* #define      TKT_FLG_RESERVED        0x00010000 */
+#define TKT_FLG_ENC_PA_REP              0x00010000
 /* #define      TKT_FLG_RESERVED        0x00008000 */
 /* #define      TKT_FLG_RESERVED        0x00004000 */
 /* #define      TKT_FLG_RESERVED        0x00002000 */
@@ -1033,6 +1033,7 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype,
 #define KRB5_PADATA_FX_FAST  136
 #define KRB5_PADATA_FX_ERROR 137
 #define KRB5_PADATA_ENCRYPTED_CHALLENGE 138
+#define KRB5_ENCPADATA_REQ_ENC_PA_REP 149
 
 #define KRB5_SAM_USE_SAD_AS_KEY         0x80000000
 #define KRB5_SAM_SEND_ENCRYPTED_SAD     0x40000000
@@ -1835,6 +1836,20 @@ krb5_cc_default(krb5_context, krb5_ccache *);
 krb5_error_code KRB5_CALLCONV
 krb5_cc_copy_creds(krb5_context context, krb5_ccache incc, krb5_ccache outcc);
 
+krb5_error_code KRB5_CALLCONV
+krb5_cc_get_config(krb5_context, krb5_ccache,
+                   krb5_const_principal,
+                   const char *, krb5_data *);
+
+krb5_error_code KRB5_CALLCONV
+krb5_cc_set_config(krb5_context, krb5_ccache,
+                   krb5_const_principal,
+                   const char *, krb5_data *);
+
+krb5_boolean KRB5_CALLCONV
+krb5_is_config_principal(krb5_context,
+                         krb5_const_principal);
+
 /* krb5_free.c */
 void KRB5_CALLCONV krb5_free_principal(krb5_context, krb5_principal );
 void KRB5_CALLCONV krb5_free_authenticator(krb5_context,
@@ -2255,17 +2270,47 @@ krb5_get_init_creds_opt_set_pa(krb5_context context,
                                krb5_get_init_creds_opt *opt, const char *attr,
                                const char *value);
 
+/**
+ * This API sets a ccache name that will contain some TGT on calls to
+ * t_init_creds functions.  If set, this ccache will be used for FAST
+ * (draft-ietf-krb-wg-preauth-framework) to protect the AS-REQ from observation
+ * and active attack.  If the fast_ccache_name is set, then FAST may be
+ * required by the client library.  In this and future versions, FAST will be
+ * used if available; krb5_get_init_creds_opt_set_fast_flags() may be used to
+ * require that the request fail is FAST is unavailable.  In MIT Kerberos 1.7
+ * setting the fast ccache at all required that FAST be present or the request
+ * would fail.
+ */
 krb5_error_code KRB5_CALLCONV
 krb5_get_init_creds_opt_set_fast_ccache_name(krb5_context context,
                                              krb5_get_init_creds_opt *opt,
                                              const char *fast_ccache_name);
 
-/*   This API sets a ccache name that will contain some TGT on
-     calls to get_init_creds functions.   If set, this ccache will
-     be used for FAST (draft-ietf-krb-wg-preauth-framework) to
-     protect the AS-REQ from observation and active attack.  If
-     the fast_ccache_name is set, then FAST may be required by the
-     client library.  In this version FAST is required.*/
+/**
+ * Set a ccache where resulting credentials will be stored.  If set, then the
+ * krb5_get_init_creds family of APIs will write out credentials to the given
+ * ccache.  Setting an output ccache is desirable both because it simplifies
+ * calling code and because it permits the krb5_get_init_creds APIs to write
+ * out configuration information about the realm to the ccache.
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_out_ccache(krb5_context context,
+                                       krb5_get_init_creds_opt *opt,
+                                       krb5_ccache ccache);
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_fast_flags(krb5_context context,
+                                       krb5_get_init_creds_opt *opt,
+                                       krb5_flags flags);
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_get_fast_flags(krb5_context context,
+                                       krb5_get_init_creds_opt *opt,
+                                       krb5_flags *out_flags);
+
+/* Fast flags*/
+#define KRB5_FAST_REQUIRED 1l<<0 /*!< Require KDC to support FAST*/
+
 krb5_error_code KRB5_CALLCONV
 krb5_get_init_creds_password(krb5_context context, krb5_creds *creds,
                              krb5_principal client, char *password,
index 45ae4963321cbfcbf2ca772bedcfa2cd07ce7cd5..23f1ddcb83808fd367965ee97fab1b9c6dbb35d6 100644 (file)
@@ -310,6 +310,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
     enc_tkt_reply.times.authtime = authtime;
 
     setflag(enc_tkt_reply.flags, TKT_FLG_INITIAL);
+    setflag(enc_tkt_reply.flags, TKT_FLG_ENC_PA_REP);
 
     /*
      * It should be noted that local policy may affect the
@@ -556,12 +557,6 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
                reply.client->realm.data, reply.client->data->data);
 #endif /* APPLE_PKINIT */
 
-    errcode = return_svr_referral_data(kdc_context,
-                                       &server, &reply_encpart);
-    if (errcode) {
-        status = "KDC_RETURN_ENC_PADATA";
-        goto errout;
-    }
 
 
     errcode = handle_authdata(kdc_context,
@@ -607,6 +602,13 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
         status = "generating reply key";
         goto errout;
     }
+    errcode = return_enc_padata(kdc_context, req_pkt, request,
+                                as_encrypting_key, &server, &reply_encpart);
+    if (errcode) {
+        status = "KDC_RETURN_ENC_PADATA";
+        goto errout;
+    }
+
     errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP, &reply_encpart,
                                   0, as_encrypting_key,  &reply, response);
     reply.enc_part.kvno = client_key->key_data_kvno;
index 778a3e87c109d42597d837d5da813aec84aaed3a..75d4132509033627c0055b70a21ce3381acc8623 100644 (file)
@@ -454,6 +454,7 @@ tgt_again:
      */
     if (!(header_enc_tkt->times.starttime))
         header_enc_tkt->times.starttime = authtime;
+    setflag(enc_tkt_reply.flags, TKT_FLG_ENC_PA_REP);
 
     /* don't use new addresses unless forwarded, see below */
 
@@ -755,14 +756,6 @@ tgt_again:
         goto cleanup;
     }
 
-    if (is_referral && isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE)) {
-        errcode = return_svr_referral_data(kdc_context,
-                                           &server, &reply_encpart);
-        if (errcode) {
-            status = "KDC_RETURN_ENC_PADATA";
-            goto cleanup;
-        }
-    }
 
     /*
      * Only add the realm of the presented tgt to the transited list if
@@ -954,6 +947,31 @@ tgt_again:
         status  = "generating reply key";
         goto cleanup;
     }
+    if (is_referral && isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE)) {
+        int idx = 0;
+
+        errcode = return_enc_padata(kdc_context, pkt, request,
+                                    reply_key, &server, &reply_encpart);
+        if (errcode) {
+            status = "KDC_RETURN_ENC_PADATA";
+            goto cleanup;
+        }
+        /* Not referral. */
+        reply_encpart.enc_padata = calloc(3, sizeof(krb5_pa_data *));
+        if (reply_encpart.enc_padata == NULL) {
+            errcode = ENOMEM;
+            status = "Allocating enc_padata";
+            goto cleanup;
+        }
+        errcode = kdc_handle_protected_negotiation(pkt, request, reply_key,
+                                                   reply_encpart.enc_padata,
+                                                   &idx);
+        if (errcode != 0) {
+            status = "protected negotiation";
+            goto cleanup;
+        }
+    }
+
     errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart,
                                   subkey ? 1 : 0,
                                   reply_key,
index a47c512d490cb186a4a88e1ca9b4b2f0c3d52821..e6503cf8ac4decbe52f393dbd3b8475d081d8ade 100644 (file)
@@ -128,7 +128,7 @@ kdc_find_fast(krb5_kdc_req **requestptr,
               struct kdc_request_state *state)
 {
     krb5_error_code retval = 0;
-    krb5_pa_data *fast_padata, *cookie_padata;
+    krb5_pa_data *fast_padata, *cookie_padata = NULL;
     krb5_data scratch;
     krb5_fast_req * fast_req = NULL;
     krb5_kdc_req *request = *requestptr;
index 05b109b4eb07fccffbac64a362a3333441abf2a7..2262c89560a4a5820694fdcba2962a1ca6b6bd2b 100644 (file)
@@ -3064,27 +3064,29 @@ include_pac_p(krb5_context context, krb5_kdc_req *request)
 }
 
 krb5_error_code
-return_svr_referral_data(krb5_context context,
-                         krb5_db_entry *server,
-                         krb5_enc_kdc_rep_part *reply_encpart)
+return_enc_padata(krb5_context context, krb5_data *req_pkt,
+                  krb5_kdc_req *request, krb5_keyblock *reply_key,
+                  krb5_db_entry *server, krb5_enc_kdc_rep_part *reply_encpart)
 {
     krb5_error_code             code;
     krb5_tl_data                tl_data;
     krb5_pa_data                *pa_data;
+    int idx = 0;
 
-    /* This should be initialized and only used for Win2K compat */
+    /* This should be initialized and only used for Win2K compat and other
+     * specific standardized uses such as FAST negotiation. */
     assert(reply_encpart->enc_padata == NULL);
-
+    reply_encpart->enc_padata = calloc(4, sizeof(krb5_pa_data *));
+    if (reply_encpart->enc_padata == NULL)
+        return ENOMEM;
     tl_data.tl_data_type = KRB5_TL_SVR_REFERRAL_DATA;
-
     code = krb5_dbe_lookup_tl_data(context, server, &tl_data);
     if (code || tl_data.tl_data_length == 0)
-        return 0; /* no server referrals to return */
+        goto negotiate; /* no server referrals to return */
 
     pa_data = (krb5_pa_data *)malloc(sizeof(*pa_data));
     if (pa_data == NULL)
         return ENOMEM;
-
     pa_data->magic = KV5M_PA_DATA;
     pa_data->pa_type = KRB5_PADATA_SVR_REFERRAL_INFO;
     pa_data->length = tl_data.tl_data_length;
@@ -3095,17 +3097,12 @@ return_svr_referral_data(krb5_context context,
     }
     memcpy(pa_data->contents, tl_data.tl_data_contents, tl_data.tl_data_length);
 
-    reply_encpart->enc_padata = (krb5_pa_data **)calloc(2, sizeof(krb5_pa_data *));
-    if (reply_encpart->enc_padata == NULL) {
-        free(pa_data->contents);
-        free(pa_data);
-        return ENOMEM;
-    }
 
-    reply_encpart->enc_padata[0] = pa_data;
+    reply_encpart->enc_padata[idx++] = pa_data;
     reply_encpart->enc_padata[1] = NULL;
-
-    return 0;
+negotiate:
+    return kdc_handle_protected_negotiation(req_pkt, request, reply_key,
+                                            reply_encpart->enc_padata, &idx);
 }
 
 #if 0
index 39c6be60004e588ddfe812570b9a41caaef211e4..475265e716acb2a504cfd288100657ea2761ad3e 100644 (file)
@@ -2651,3 +2651,64 @@ kdc_get_ticket_endtime(krb5_context context,
 
     *out_endtime = starttime + life;
 }
+
+/**
+ * Handle protected negotiation of FAST using enc_padata
+ * - If ENCPADATA_REQ_ENC_PA_REP is present, then:
+ * - Return ENCPADATA_REQ_ENC_PA_REP with checksum of AS-REQ from client
+ * - Include PADATA_FX_FAST in the enc_padata to indicate FAST
+ * @pre @c out_enc_padata has space for at least two more padata
+ * @param index in/out index into @c out_enc_padata for next item
+ */
+krb5_error_code
+kdc_handle_protected_negotiation(krb5_data *req_pkt, krb5_kdc_req *request,
+                                 const krb5_keyblock *reply_key,
+                                 krb5_pa_data **out_enc_padata, int *idx)
+{
+    krb5_error_code retval = 0;
+    krb5_checksum checksum;
+    krb5_data *out = NULL;
+    krb5_pa_data *pa;
+    assert(out_enc_padata != NULL);
+    pa = krb5int_find_pa_data(kdc_context, request->padata,
+                              KRB5_ENCPADATA_REQ_ENC_PA_REP);
+    if (pa == NULL)
+        return 0;
+    checksum.contents = NULL;
+    pa = malloc(sizeof(krb5_pa_data));
+    if (pa == NULL)
+        return ENOMEM;
+    pa->magic = KV5M_PA_DATA;
+    pa->pa_type = KRB5_ENCPADATA_REQ_ENC_PA_REP;
+    retval = krb5_c_make_checksum(kdc_context,0, reply_key,
+                                  KRB5_KEYUSAGE_AS_REQ, req_pkt, &checksum);
+    if (retval != 0)
+        goto cleanup;
+    retval = encode_krb5_checksum(&checksum, &out);
+    if (retval != 0)
+        goto cleanup;
+    pa->contents = (krb5_octet *) out->data;
+    pa->length = out->length;
+    out_enc_padata[(*idx)++] = pa;
+    pa = NULL;
+    out->data = NULL;
+    pa = malloc(sizeof(krb5_pa_data));
+    if (pa == NULL) {
+        retval = ENOMEM;
+        goto cleanup;
+    }
+    pa->magic = KV5M_PA_DATA;
+    pa->pa_type = KRB5_PADATA_FX_FAST;
+    pa->length = 0;
+    pa->contents = NULL;
+    out_enc_padata[(*idx)++] = pa;
+    pa = NULL;
+cleanup:
+    if (checksum.contents)
+        krb5_free_checksum_contents(kdc_context, &checksum);
+    if (out != NULL)
+        krb5_free_data(kdc_context, out);
+    if (pa != NULL)
+        free(pa);
+    return retval;
+}
index a2347208c8040012656afba21d89dd02ed90f184..353bbfc5d675f3bf663096f41f86293827596733 100644 (file)
@@ -250,9 +250,11 @@ krb5_boolean
 include_pac_p(krb5_context context, krb5_kdc_req *request);
 
 krb5_error_code
-return_svr_referral_data (krb5_context context,
-                          krb5_db_entry *server,
-                          krb5_enc_kdc_rep_part *reply_encpart);
+return_enc_padata(krb5_context context,
+                  krb5_data *req_pkt, krb5_kdc_req *request,
+                  krb5_keyblock *reply_key,
+                  krb5_db_entry *server,
+                  krb5_enc_kdc_rep_part *reply_encpart);
 
 krb5_error_code
 sign_db_authdata (krb5_context context,
@@ -392,7 +394,10 @@ krb5_error_code kdc_fast_handle_reply_key(struct kdc_request_state *state,
 
 krb5_error_code kdc_preauth_get_cookie(struct kdc_request_state *state,
                                        krb5_pa_data **cookie);
-
+krb5_error_code
+kdc_handle_protected_negotiation( krb5_data *req_pkt, krb5_kdc_req *request,
+                                  const krb5_keyblock *reply_key,
+                                  krb5_pa_data **out_enc_padata, int *idx);
 
 
 
index aa4545780f68caa18c1bb144220eb54a75450b35..d0dc6223706395fe500b280cedb8e802a10e2b61 100644 (file)
@@ -30,6 +30,8 @@
 #include "etypes.h"
 #include "dk.h"
 
+/* A 0 checksum type means use the mandatory checksum. */
+
 krb5_error_code KRB5_CALLCONV
 krb5_k_make_checksum(krb5_context context, krb5_cksumtype cksumtype,
                      krb5_key key, krb5_keyusage usage,
@@ -41,6 +43,12 @@ krb5_k_make_checksum(krb5_context context, krb5_cksumtype cksumtype,
     krb5_octet *trunc;
     krb5_error_code ret;
 
+    if (cksumtype == 0) {
+        ret = krb5int_c_mandatory_cksumtype(context, key->keyblock.enctype,
+                                            &cksumtype);
+        if (ret != 0)
+            return ret;
+    }
     ctp = find_cksumtype(cksumtype);
     if (ctp == NULL)
         return KRB5_BAD_ENCTYPE;
index 0257a8b0c0ed1d4d23a0667e6f31020113c7615f..60d9455a13826eca22ea2a481c5a26183deec108 100644 (file)
@@ -658,6 +658,7 @@ asn1_decode_enc_kdc_rep_part(asn1buf *buf, krb5_enc_kdc_rep_part *val)
     val->last_req = NULL;
     val->server = NULL;
     val->caddrs = NULL;
+    val->enc_padata = NULL;
     { begin_structure();
         get_field(val->session,0,asn1_decode_encryption_key_ptr);
         get_field(val->last_req,1,asn1_decode_last_req);
index 1cd6c8c24d04029423364d87256a893a2fc560ef..a35f561e68e8952a39d4f595b5e4e7f459c6ec42 100644 (file)
@@ -1424,6 +1424,8 @@ MAKE_FULL_ENCODER(encode_krb5_safe_with_body, krb5_safe_with_body);
 
 MAKE_FULL_ENCODER(encode_krb5_priv, krb5_priv);
 MAKE_FULL_ENCODER(encode_krb5_enc_priv_part, priv_enc_part);
+MAKE_FULL_ENCODER(encode_krb5_checksum, checksum);
+
 MAKE_FULL_ENCODER(encode_krb5_cred, krb5_cred);
 MAKE_FULL_ENCODER(encode_krb5_enc_cred_part, enc_cred_part);
 MAKE_FULL_ENCODER(encode_krb5_error, krb5_error);
index e255551f50ab66be736d72d8eafde5e1bfab4ea1..542a626da2211d674415dda254a96216c8ef4a92 100644 (file)
@@ -702,6 +702,16 @@ error_out:
     return retval;
 }
 
+krb5_error_code
+decode_krb5_checksum(const krb5_data *code, krb5_checksum **repptr)
+{
+    setup_buf_only(krb5_checksum *);
+    alloc_field(rep);
+    retval = asn1_decode_checksum(&buf, rep);
+    if (retval) clean_return(retval);
+    cleanup(free);
+}
+
 krb5_error_code
 decode_krb5_cred(const krb5_data *code, krb5_cred **repptr)
 {
index 33fb97c76d464b79bf58b472619b9e44fa8476bb..de2bc9d3206c36a8041671d5f5c6f7011dc6de6e 100644 (file)
@@ -805,7 +805,7 @@ krb5_stdccv3_get_flags (krb5_context context,
 krb5_error_code KRB5_CALLCONV
 krb5_stdccv3_remove (krb5_context context,
                      krb5_ccache id,
-                     krb5_flags flags,
+                     krb5_flags whichfields,
                      krb5_creds *in_creds)
 {
     krb5_error_code err = 0;
@@ -836,7 +836,10 @@ krb5_stdccv3_remove (krb5_context context,
                                                    credentials->data, &creds);
 
             if (!err) {
-                found = krb5_creds_compare (context, in_creds, &creds);
+                found = krb5int_cc_creds_match_request(context,
+                                                       whichfields,
+                                                       in_creds,
+                                                       &creds);
                 krb5_free_cred_contents (context, &creds);
             }
 
index e12dd563fc9ed885fe0d538a7b37d5057a9baffe..f08bfb98d6ecf95217bfabbbb1108f71fe9cb66f 100644 (file)
@@ -191,3 +191,162 @@ krb5_cc_unlock (krb5_context context, krb5_ccache ccache)
 {
     return ccache->ops->unlock(context, ccache);
 }
+
+static const char conf_realm[] = "X-CACHECONF:";
+static const char conf_name[] = "krb5_ccache_conf_data";
+
+static krb5_error_code
+build_conf_principals (krb5_context context, krb5_ccache id,
+                       krb5_const_principal principal,
+                       const char *name, krb5_creds *cred)
+{
+    krb5_principal client;
+    krb5_error_code ret;
+    char *pname = NULL;
+
+    memset(cred, 0, sizeof(*cred));
+
+    ret = krb5_cc_get_principal(context, id, &client);
+    if (ret)
+        return ret;
+
+    if (principal) {
+        ret = krb5_unparse_name(context, principal, &pname);
+        if (ret)
+            return ret;
+    }
+
+    ret = krb5_build_principal(context, &cred->server,
+                               sizeof(conf_realm) - 1, conf_realm,
+                               conf_name, name, pname, (char *)NULL);
+    free(pname);
+    if (ret) {
+        krb5_free_principal(context, client);
+        return ret;
+    }
+    ret = krb5_copy_principal(context, client, &cred->client);
+    krb5_free_principal(context, client);
+    return ret;
+}
+
+/*!
+ * \param context a Keberos context
+ * \param principal principal to check if it a configuration principal
+ *
+ * \brief Return TRUE (non zero) if the principal is a configuration
+ *        principal (generated part of krb5_cc_set_config()). Returns
+ *        FALSE (zero) if not a configuration principal.
+ *
+ */
+
+krb5_boolean KRB5_CALLCONV
+krb5_is_config_principal (krb5_context context,
+                          krb5_const_principal principal)
+{
+    const krb5_data *realm;
+
+    realm = krb5_princ_realm(context, principal);
+
+    if (realm->length != sizeof(conf_realm) - 1 ||
+        memcmp(realm->data, conf_realm, sizeof(conf_realm) - 1) != 0)
+        return FALSE;
+
+    if (principal->length == 0 ||
+        principal->data[0].length != (sizeof(conf_name) - 1) ||
+        memcmp(principal->data[0].data, conf_name, sizeof(conf_name) - 1) != 0)
+        return FALSE;
+
+    return TRUE;
+}
+
+/*!
+ * \param context a Keberos context
+ * \param id the credential cache to store the data for
+ * \param principal configuration for a specific principal, if
+ * NULL, global for the whole cache.
+ * \param key name under which the configuraion is stored.
+ * \param data data to store
+ *
+ * \brief Store some configuration for the credential cache in the
+ *        cache.  Existing configuration under the same key is
+ *        over-written.
+ *
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_cc_set_config (krb5_context context, krb5_ccache id,
+                    krb5_const_principal principal,
+                    const char *key, krb5_data *data)
+{
+    krb5_error_code ret;
+    krb5_creds cred;
+    memset(&cred, 0, sizeof(cred));
+
+    ret = build_conf_principals(context, id, principal, key, &cred);
+    if (ret)
+        goto out;
+
+    ret = krb5_cc_remove_cred(context, id, 0, &cred);
+    if (ret && ret != KRB5_CC_NOTFOUND && ret != KRB5_CC_NOSUPP)
+        goto out;
+
+    cred.ticket.data = malloc(data->length);
+    if (cred.ticket.data == NULL) {
+        krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+        return ENOMEM;
+    }
+    cred.ticket.length = data->length;
+    memcpy(cred.ticket.data, data->data, data->length);
+
+    ret = krb5_cc_store_cred(context, id, &cred);
+
+out:
+    krb5_free_cred_contents(context, &cred);
+    return ret;
+}
+
+/*!
+ * \param context a Keberos context
+ * \param id the credential cache to store the data for
+ * \param principal configuration for a specific principal, if
+ *        NULL, global for the whole cache.
+ * \param key name under which the configuraion is stored.
+ * \param data data to fetched, free with krb5_data_free()
+ *
+ * \brief Get some configuration for the credential cache in the cache.
+ */
+
+
+krb5_error_code KRB5_CALLCONV
+krb5_cc_get_config (krb5_context context, krb5_ccache id,
+                    krb5_const_principal principal,
+                    const char *key, krb5_data *data)
+{
+    krb5_creds mcred, cred;
+    krb5_error_code ret;
+
+    memset(&cred, 0, sizeof(cred));
+    memset(data, 0, sizeof(*data));
+
+    ret = build_conf_principals(context, id, principal, key, &mcred);
+    if (ret)
+        goto out;
+
+    ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
+    if (ret)
+        goto out;
+
+    data->data = malloc(cred.ticket.length);
+    if (data->data == NULL) {
+        ret = ENOMEM;
+        krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+        goto out;
+    }
+    data->length = cred.ticket.length;
+    memcpy(data->data, cred.ticket.data, data->length);
+
+out:
+    krb5_free_cred_contents(context, &cred);
+    krb5_free_cred_contents(context, &mcred);
+    return ret;
+}
index 884a4ed82db30e5f170b99ba018dc13a6138fd0c..279165124f8218651fb4937fc720cdefcb56832a 100644 (file)
@@ -56,7 +56,7 @@
 static krb5_error_code
 fast_armor_ap_request(krb5_context context,
                       struct krb5int_fast_request_state *state,
-                      krb5_ccache ccache, krb5_data *target_realm)
+                      krb5_ccache ccache, krb5_principal target_principal)
 {
     krb5_error_code retval = 0;
     krb5_creds creds, *out_creds = NULL;
@@ -66,9 +66,8 @@ fast_armor_ap_request(krb5_context context,
     krb5_keyblock *subkey = NULL, *armor_key = NULL;
     encoded_authenticator.data = NULL;
     memset(&creds, 0, sizeof(creds));
-    retval = krb5_tgtname(context, target_realm, target_realm, &creds.server);
-    if (retval ==0)
-        retval = krb5_cc_get_principal(context, ccache, &creds.client);
+    creds.server = target_principal;
+    retval = krb5_cc_get_principal(context, ccache, &creds.client);
     if (retval == 0)
         retval = krb5_get_credentials(context, 0, ccache,  &creds, &out_creds);
     if (retval == 0)
@@ -98,6 +97,8 @@ fast_armor_ap_request(krb5_context context,
     krb5_free_keyblock(context, subkey);
     if (out_creds)
         krb5_free_creds(context, out_creds);
+    /* target_principal is owned by caller. */
+    creds.server = NULL;
     krb5_free_cred_contents(context, &creds);
     if (encoded_authenticator.data)
         krb5_free_data_contents(context, &encoded_authenticator);
@@ -138,13 +139,34 @@ krb5int_fast_as_armor(krb5_context context,
 {
     krb5_error_code retval = 0;
     krb5_ccache ccache = NULL;
+    krb5_principal target_principal = NULL;
+    krb5_data *target_realm;
     krb5_clear_error_message(context);
+    target_realm = krb5_princ_realm(context, request->server);
     if (opte->opt_private->fast_ccache_name) {
+        state->fast_state_flags |= KRB5INT_FAST_ARMOR_AVAIL;
         retval = krb5_cc_resolve(context, opte->opt_private->fast_ccache_name,
                                  &ccache);
-        if (retval==0)
+        if (retval == 0) {
+            retval = krb5_tgtname(context, target_realm, target_realm,
+                                  &target_principal);
+        }
+        if (retval == 0) {
+            krb5_data config_data;
+            config_data.data = NULL;
+            retval = krb5_cc_get_config(context, ccache, target_principal,
+                                        KRB5_CONF_FAST_AVAIL, &config_data);
+            if ((retval == 0) && config_data.data )
+                state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
+            krb5_free_data_contents(context, &config_data);
+            retval = 0;
+        }
+        if (opte->opt_private->fast_flags& KRB5_FAST_REQUIRED)
+            state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
+        if (retval == 0 && (state->fast_state_flags & KRB5INT_FAST_DO_FAST)) {
             retval = fast_armor_ap_request(context, state, ccache,
-                                           krb5_princ_realm(context, request->server));
+                                           target_principal);
+        }
         if (retval != 0) {
             const char * errmsg;
             errmsg = krb5_get_error_message(context, retval);
@@ -156,6 +178,8 @@ krb5int_fast_as_armor(krb5_context context,
     }
     if (ccache)
         krb5_cc_close(context, ccache);
+    if (target_principal)
+        krb5_free_principal(context, target_principal);
     return retval;
 }
 
@@ -528,3 +552,61 @@ krb5int_find_pa_data(krb5_context context, krb5_pa_data *const *padata,
 
     return *tmppa;
 }
+
+
+krb5_error_code
+krb5int_fast_verify_nego(krb5_context context,
+                         struct krb5int_fast_request_state *state,
+                         krb5_kdc_rep *rep, krb5_data *request,
+                         krb5_keyblock *decrypting_key,
+                         krb5_boolean *fast_avail)
+{
+    krb5_error_code retval = 0;
+    krb5_checksum *checksum = NULL;
+    krb5_pa_data *pa;
+    krb5_data scratch;
+    krb5_boolean valid;
+
+    if (rep->enc_part2->flags& TKT_FLG_ENC_PA_REP) {
+        pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
+                                  KRB5_ENCPADATA_REQ_ENC_PA_REP);
+        if (pa == NULL)
+            retval = KRB5_KDCREP_MODIFIED;
+        else {
+            scratch.data = (char *) pa->contents;
+            scratch.length = pa->length;
+        }
+        if (retval == 0)
+            retval = decode_krb5_checksum(&scratch, &checksum);
+        if (retval == 0)
+            retval = krb5_c_verify_checksum(context, decrypting_key,
+                                            KRB5_KEYUSAGE_AS_REQ,
+                                            request, checksum, &valid);
+        if (retval == 0 &&valid == 0)
+            retval = KRB5_KDCREP_MODIFIED;
+        if (retval == 0) {
+            pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
+                                      KRB5_PADATA_FX_FAST);
+            *fast_avail = (pa != NULL);
+        }
+    }
+    if (checksum)
+        krb5_free_checksum(context, checksum);
+    return retval;
+}
+
+krb5_boolean
+krb5int_upgrade_to_fast_p(krb5_context context,
+                          struct krb5int_fast_request_state *state,
+                          krb5_pa_data **padata)
+{
+    if (state->armor_key != NULL)
+        return 0; /*already using FAST*/
+    if (!(state->fast_state_flags & KRB5INT_FAST_ARMOR_AVAIL))
+        return 0;
+    if (krb5int_find_pa_data(context, padata, KRB5_PADATA_FX_FAST) != NULL) {
+        state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
+        return 1;
+    }
+    return 0;
+}
index 443f3e1962c04823d9fddda8c80e0ef8d305379a..66dc98523e5ee9f79f479a24466a6d5c76e26271 100644 (file)
@@ -42,6 +42,9 @@ struct krb5int_fast_request_state {
     krb5_int32 nonce;
 };
 
+#define KRB5INT_FAST_DO_FAST     (1l<<0)  /* Perform FAST */
+#define KRB5INT_FAST_ARMOR_AVAIL (1l<<1)
+
 krb5_error_code
 krb5int_fast_prep_req_body(krb5_context context, struct krb5int_fast_request_state *state,
                            krb5_kdc_req *request, krb5_data **encoded_req_body);
@@ -79,5 +82,16 @@ krb5_error_code krb5int_fast_reply_key(krb5_context context,
                                        krb5_keyblock *output_key);
 
 
+krb5_error_code
+krb5int_fast_verify_nego(krb5_context context,
+                         struct krb5int_fast_request_state *state,
+                         krb5_kdc_rep *rep, krb5_data *request,
+                         krb5_keyblock *decrypting_key,
+                         krb5_boolean *fast_avail);
+
+krb5_boolean
+krb5int_upgrade_to_fast_p(krb5_context context,
+                          struct krb5int_fast_request_state *state,
+                          krb5_pa_data **padata);
 
 #endif
index 472801588eaa00aeede021f6d8979130b6515af5..b13c9a94c8c5683d72d5fcf4b10f5f8fcfe1de41 100644 (file)
@@ -545,6 +545,30 @@ tgt_is_local_realm(krb5_creds *tgt)
             && data_eq(tgt->server->realm, tgt->client->realm));
 }
 
+static krb5_error_code
+request_enc_pa_rep(krb5_pa_data ***padptr)
+{
+    size_t size = 0;
+    krb5_pa_data **pad = *padptr;
+    krb5_pa_data *pa= NULL;
+    if (pad)
+        for (size=0; pad[size]; size++);
+    pad = realloc(pad, sizeof(*pad)*(size+2));
+
+    if (pad == NULL)
+        return ENOMEM;
+    pad[size+1] = NULL;
+    pa = malloc(sizeof(krb5_pa_data));
+    if (pa == NULL)
+        return ENOMEM;
+    pa->contents = NULL;
+    pa->length = 0;
+    pa->pa_type = KRB5_ENCPADATA_REQ_ENC_PA_REP;
+    pad[size] = pa;
+    *padptr = pad;
+    return 0;
+}
+
 krb5_error_code KRB5_CALLCONV
 krb5_get_in_tkt(krb5_context context,
                 krb5_flags options,
@@ -1234,6 +1258,110 @@ cleanup:
     return code;
 }
 
+/**
+ * Throw away any state related to specific realm either at the beginning of a
+ * request, or when a realm changes, or when we start to use FAST after
+ * assuming we would not do so.
+ *
+ * @param padata padata from an error if an error from the realm we now expect
+ * to talk to caused the restart.  Used to infer negotiation characteristics
+ * such as whether FAST is used.
+ */
+static krb5_error_code
+restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
+                        krb5_pa_data **padata)
+{
+    krb5_error_code code = 0;
+    unsigned char random_buf[4];
+    krb5_data random_data;
+    if (ctx->preauth_to_use) {
+        krb5_free_pa_data(context, ctx->preauth_to_use);
+        ctx->preauth_to_use = NULL;
+    }
+
+    if (ctx->fast_state) {
+        krb5int_fast_free_state(context, ctx->fast_state);
+        ctx->fast_state = NULL;
+    }
+    code = krb5int_fast_make_state(context, &ctx->fast_state);
+    if (code != 0)
+        goto cleanup;
+    ctx->get_data_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->opte &&
+        (ctx->opte->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
+        if ((code = make_preauth_list(context, ctx->opte->preauth_list,
+                                      ctx->opte->preauth_list_length,
+                                      &ctx->preauth_to_use)))
+            goto cleanup;
+    }
+
+    /* Set the request nonce. */
+    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_principal(context, ctx->request->server);
+    ctx->request->server = NULL;
+
+    code = build_in_tkt_name(context, ctx->in_tkt_service,
+                             ctx->request->client,
+                             &ctx->request->server);
+    if (code != 0)
+        goto cleanup;
+
+    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;
+    if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
+        code = krb5int_fast_as_armor(context, ctx->fast_state,
+                                     ctx->opte, ctx->request);
+        if (code != 0)
+            goto cleanup;
+    }
+    /* give the preauth plugins a chance to prep the request body */
+    krb5_preauth_prepare_request(context, ctx->opte, ctx->request);
+
+    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;
+    code = krb5int_fast_prep_req_body(context, ctx->fast_state,
+                                      ctx->request,
+                                      &ctx->encoded_request_body);
+    if (code != 0)
+        goto cleanup;
+cleanup:
+    return code;
+}
+
 krb5_error_code KRB5_CALLCONV
 krb5_init_creds_init(krb5_context context,
                      krb5_principal client,
@@ -1256,7 +1384,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 = 1;
     code = krb5_copy_principal(context, client, &ctx->request->client);
     if (code != 0)
         goto cleanup;
@@ -1282,13 +1410,8 @@ krb5_init_creds_init(krb5_context context,
 
     opte = ctx->opte;
 
-    code = krb5int_fast_make_state(context, &ctx->fast_state);
-    if (code != 0)
-        goto cleanup;
-
     ctx->get_data_rock.magic = CLIENT_ROCK_MAGIC;
     ctx->get_data_rock.etype = &ctx->etype;
-    ctx->get_data_rock.fast_state = ctx->fast_state;
 
     /* Initialise request parameters as per krb5_get_init_creds() */
     ctx->request->kdc_options = context->kdc_default_options;
@@ -1388,8 +1511,8 @@ krb5_init_creds_init(krb5_context context,
         if (code != 0)
             goto cleanup;
     } else if (krb5_libdefault_boolean(context, &ctx->request->client->realm,
-                                     KRB5_CONF_NOADDRESSES, &tmp) != 0
-             || tmp) {
+                                       KRB5_CONF_NOADDRESSES, &tmp) != 0
+               || tmp) {
         ctx->request->addresses = NULL;
     } else {
         code = krb5_os_localaddr(context, &ctx->request->addresses);
@@ -1397,18 +1520,6 @@ krb5_init_creds_init(krb5_context context,
             goto cleanup;
     }
 
-    /* initial preauth state */
-    krb5_preauth_request_context_init(context);
-
-    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)
@@ -1418,33 +1529,7 @@ krb5_init_creds_init(krb5_context context,
         ctx->salt.data = NULL;
     }
 
-    /* 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;
-        }
-    }
-
-    ctx->loopcount = 0;
+    code = restart_init_creds_loop(context, ctx, NULL);
 
     *pctx = ctx;
 
@@ -1471,7 +1556,8 @@ krb5_init_creds_set_service(krb5_context context,
     free(ctx->in_tkt_service);
     ctx->in_tkt_service = s;
 
-    return 0;
+    krb5_preauth_request_context_fini(context);
+    return restart_init_creds_loop(context, ctx, NULL);
 }
 
 static krb5_error_code
@@ -1552,49 +1638,7 @@ init_creds_step_request(krb5_context context,
 {
     krb5_error_code code;
 
-    krb5_free_principal(context, ctx->request->server);
-    ctx->request->server = NULL;
-
-    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;
-
-        /* 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;
-
-        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) {
+    if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
         code = KRB5_GET_IN_TKT_LOOP;
         goto cleanup;
     }
@@ -1657,7 +1701,12 @@ init_creds_step_request(krb5_context context,
         krb5_free_data(context, ctx->encoded_previous_request);
         ctx->encoded_previous_request = NULL;
     }
-
+    if (ctx->request->padata)
+        ctx->sent_nontrivial_preauth = 1;
+    if (ctx->enc_pa_rep_permitted)
+        code = request_enc_pa_rep(&ctx->request->padata);
+    if (code)
+        goto cleanup;
     code = krb5int_fast_prep_req(context, ctx->fast_state,
                                  ctx->request, ctx->encoded_request_body,
                                  encode_krb5_as_req,
@@ -1675,6 +1724,53 @@ cleanup:
     return code;
 }
 
+/*
+ * The control flow is complicated.  In order to switch from non-FAST mode to
+ * FAST mode, we need to reset our pre-authentication state.  FAST negotiation
+ * attempts to make sure we rarely have to do this.  When FAST negotiation is
+ * working, we record whether FAST is available when we obtain an armor ticket;
+ * if so, we start out with FAST enabled .  There are two complicated
+ * situations.
+ *
+ * First, if we get a PREAUTH_REQUIRED error including PADATA_FX_FAST back from
+ * a KDC in a case where we were not expecting to use FAST, and we have an
+ * armor ticket available, then we want to use FAST.  That involves clearing
+ * out the pre-auth state, reinitializing the plugins and trying again with an
+ * armor key.
+ *
+ * Secondly, using the negotiation can cause problems with some older KDCs.
+ * Negotiation involves including a special padata item.  Some KDCs, including
+ * MIT prior to 1.7, will return PREAUTH_FAILED rather than PREAUTH_REQUIRED in
+ * pre-authentication is required and unknown padata are included in the
+ * request.  To make matters worse, these KDCs typically do not include a list
+ * of padata in PREAUTH_FAILED errors.  So, if we get PREAUTH_FAILED and we
+ * generated no pre-authentication other than the negotiation then we want to
+ * retry without negotiation.  In this case it is probably also desirable to
+ * retry with the preauth plugin state cleared.
+ *
+ * In all these cases we should not start over more than once.  Control flow is
+ * managed by several variables.
+ *
+ *   sent_nontrivial_preauth: if true, we sent preauth other than negotiation;
+ *   no restart on PREAUTH_FAILED
+ *
+ *   KRB5INT_FAST_ARMOR_AVAIL: fast_state_flag if desired we could generate
+ *   armor; if not set, then we can't use FAST even if the KDC wants to.
+ *
+ *   have_restarted: true if we've already restarted
+ */
+static krb5_boolean
+negotiation_requests_restart(krb5_context context, krb5_init_creds_context ctx,
+                             krb5_pa_data **padata)
+{
+    if (!ctx->have_restarted &&
+        (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata) ||
+         (ctx->err_reply->error == KDC_ERR_PREAUTH_FAILED &&
+          !ctx->sent_nontrivial_preauth)))
+        return 1;
+    return 0;
+}
+
 static krb5_error_code
 init_creds_step_reply(krb5_context context,
                       krb5_init_creds_context ctx,
@@ -1687,6 +1783,7 @@ init_creds_step_reply(krb5_context context,
     int canon_flag = 0;
     krb5_keyblock *strengthen_key = NULL;
     krb5_keyblock encrypting_key;
+    krb5_boolean fast_avail;
 
     encrypting_key.length = 0;
     encrypting_key.contents = NULL;
@@ -1705,8 +1802,16 @@ init_creds_step_reply(krb5_context context,
                                           &ctx->err_reply, &padata, &retry);
         if (code != 0)
             goto cleanup;
-
-        if (ctx->err_reply->error == KDC_ERR_PREAUTH_REQUIRED && retry) {
+        if (negotiation_requests_restart(context, ctx, padata)) {
+            ctx->have_restarted = 1;
+            krb5_preauth_request_context_fini(context);
+            if ((ctx->fast_state->fast_state_flags & KRB5INT_FAST_DO_FAST) ==0)
+                ctx->enc_pa_rep_permitted = 0;
+            code = restart_init_creds_loop(context, ctx, padata);
+            krb5_free_error(context, ctx->err_reply);
+            ctx->err_reply = NULL;
+        } else 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;
@@ -1732,13 +1837,20 @@ init_creds_step_reply(krb5_context context,
             /* this will trigger a new call to krb5_do_preauth() */
             krb5_free_error(context, ctx->err_reply);
             ctx->err_reply = NULL;
+            krb5_preauth_request_context_fini(context);
+            /* Permit another negotiation based restart. */
+            ctx->have_restarted = 0;
+            ctx->sent_nontrivial_preauth = 0;
+            code = restart_init_creds_loop(context, ctx, NULL);
+            if (code != 0)
+                goto cleanup;
         } else {
             if (retry) {
                 code = 0;
             } else {
                 /* error + no hints = give up */
                 code = (krb5_error_code)ctx->err_reply->error +
-                       ERROR_TABLE_BASE_krb5;
+                    ERROR_TABLE_BASE_krb5;
             }
         }
 
@@ -1812,7 +1924,7 @@ init_creds_step_reply(krb5_context context,
        again.  */
     if (ctx->as_key.length) {
         code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
-                                     &encrypting_key);
+                                      &encrypting_key);
         if (code != 0)
             goto cleanup;
         code = decrypt_as_reply(context, NULL, ctx->reply, NULL, NULL,
@@ -1843,6 +1955,11 @@ init_creds_step_reply(krb5_context context,
             goto cleanup;
     }
 
+    code = krb5int_fast_verify_nego(context, ctx->fast_state,
+                                    ctx->reply, ctx->encoded_previous_request,
+                                    &encrypting_key, &fast_avail);
+    if (code)
+        goto cleanup;
     code = verify_as_reply(context, ctx->request_time,
                            ctx->request, ctx->reply);
     if (code != 0)
@@ -1852,6 +1969,30 @@ init_creds_step_reply(krb5_context context,
                           ctx->reply, &ctx->cred, NULL);
     if (code != 0)
         goto cleanup;
+    if (ctx->opte && ctx->opte->opt_private->out_ccache) {
+        krb5_ccache out_ccache = ctx->opte->opt_private->out_ccache;
+        krb5_data config_data;
+        code = krb5_cc_initialize(context, out_ccache, ctx->cred.client);
+        if (code != 0)
+            goto cc_cleanup;
+        code = krb5_cc_store_cred(context, out_ccache, &ctx->cred);
+        if (code != 0)
+            goto cc_cleanup;
+        if (fast_avail) {
+            config_data.data = "yes";
+            config_data.length = strlen(config_data.data);
+            code = krb5_cc_set_config(context, out_ccache, ctx->cred.server,
+                                      KRB5_CONF_FAST_AVAIL, &config_data);
+        }
+    cc_cleanup:
+        if (code !=0) {
+            const char *msg;
+            msg = krb5_get_error_message(context, code);
+            krb5_set_error_message(context, code,
+                                   "%s while storing credentials", msg);
+            krb5_free_error_message(context, msg);
+        }
+    }
 
     krb5_preauth_request_context_fini(context);
 
@@ -2002,4 +2143,3 @@ cleanup:
 
     return code;
 }
-
index bff45392f14ba2b0c42ebe87195c3d8666c2d451..f4cfd9220386cfc447b1c4d8b3177f3456c804ae 100644 (file)
@@ -149,6 +149,8 @@ krb5int_gic_opte_private_free(krb5_context context, krb5_gic_opt_ext *opte)
         free_gic_opt_ext_preauth_data(context, opte);
     if (opte->opt_private->fast_ccache_name)
         free(opte->opt_private->fast_ccache_name);
+    if (opte->opt_private->out_ccache)
+        krb5_cc_close(context, opte->opt_private->out_ccache);
     free(opte->opt_private);
     opte->opt_private = NULL;
     return 0;
@@ -486,3 +488,59 @@ krb5_error_code KRB5_CALLCONV krb5_get_init_creds_opt_set_fast_ccache_name
         retval = ENOMEM;
     return retval;
 }
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_out_ccache(krb5_context context,
+                                       krb5_get_init_creds_opt *opt,
+                                       krb5_ccache ccache)
+{
+    krb5_error_code retval = 0;
+    krb5_gic_opt_ext *opte;
+
+    retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0,
+                                     "krb5_get_init_creds_opt_set_out_ccache");
+    if (retval)
+        return retval;
+    if (opte->opt_private->out_ccache) {
+        krb5_cc_close(context,  opte->opt_private->out_ccache);
+        opte->opt_private->out_ccache = NULL;
+    }
+    retval = krb5_cc_resolve(context, krb5_cc_get_name(context, ccache),
+                            &opte->opt_private->out_ccache);
+        return retval;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_fast_flags(krb5_context context,
+                                       krb5_get_init_creds_opt *opt,
+                                       krb5_flags flags)
+{
+    krb5_error_code retval = 0;
+    krb5_gic_opt_ext *opte;
+
+    retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0,
+                                     "krb5_get_init_creds_opt_set_fast_flags");
+    if (retval)
+        return retval;
+    opte->opt_private->fast_flags = flags;
+        return retval;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_get_fast_flags(krb5_context context,
+                                       krb5_get_init_creds_opt *opt,
+                                       krb5_flags *out_flags)
+{
+    krb5_error_code retval = 0;
+    krb5_gic_opt_ext *opte;
+    if (out_flags == NULL)
+        return EINVAL;
+    *out_flags = 0;
+    retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0,
+                                     "krb5_get_init_creds_opt_get_fast_flags");
+    if (retval)
+        return retval;
+    *out_flags = opte->opt_private->fast_flags;
+        return retval;
+}
+
index b830684f2dcfc077de1c59982b5467a8d09bfd54..1d41a44f4365141ce35fcf3cc369c74aeb9eb0de 100644 (file)
@@ -30,6 +30,9 @@ struct _krb5_init_creds_context {
     krb5_keyblock as_key;
     krb5_enctype etype;
     krb5_preauth_client_rock get_data_rock;
+    krb5_boolean enc_pa_rep_permitted;
+    krb5_boolean have_restarted;
+    krb5_boolean sent_nontrivial_preauth;
 };
 
 #define KRB5_INIT_CREDS_STEP_FLAG_COMPLETE          0x1
@@ -46,4 +49,3 @@ krb5_get_as_key_password(krb5_context context,
                          void *gak_data);
 
 #endif /* !KRB5_INIT_CREDS_CONTEXT */
-
index 3de9915c3fa8f1607212f9359e6ce268e242d9af..15a887b2f8215a05f46f45a83c5cfe2750e3f25b 100644 (file)
@@ -53,6 +53,7 @@ encode_krb5_as_rep
 encode_krb5_as_req
 encode_krb5_authdata
 encode_krb5_authenticator
+encode_krb5_checksum
 encode_krb5_cred
 encode_krb5_enc_cred_part
 encode_krb5_enc_data
@@ -171,6 +172,7 @@ krb5_cc_dfl_ops
 krb5_cc_end_seq_get
 krb5_cc_file_ops
 krb5_cc_gen_new
+krb5_cc_get_config
 krb5_cc_get_name
 krb5_cc_get_principal
 krb5_cc_get_type
@@ -182,6 +184,7 @@ krb5_cc_remove_cred
 krb5_cc_resolve
 krb5_cc_retrieve_cred
 krb5_cc_retrieve_cred_default
+krb5_cc_set_config
 krb5_cc_set_default_name
 krb5_cc_set_flags
 krb5_cc_start_seq_get
@@ -330,6 +333,7 @@ krb5_get_init_creds_keytab
 krb5_get_init_creds_opt_alloc
 krb5_get_init_creds_opt_free
 krb5_get_init_creds_opt_free_pa
+krb5_get_init_creds_opt_get_fast_flags
 krb5_get_init_creds_opt_get_pa
 krb5_get_init_creds_opt_init
 krb5_get_init_creds_opt_set_address_list
@@ -337,7 +341,9 @@ krb5_get_init_creds_opt_set_canonicalize
 krb5_get_init_creds_opt_set_change_password_prompt
 krb5_get_init_creds_opt_set_etype_list
 krb5_get_init_creds_opt_set_fast_ccache_name
+krb5_get_init_creds_opt_set_fast_flags
 krb5_get_init_creds_opt_set_forwardable
+krb5_get_init_creds_opt_set_out_ccache
 krb5_get_init_creds_opt_set_pa
 krb5_get_init_creds_opt_set_preauth_list
 krb5_get_init_creds_opt_set_proxiable
@@ -371,6 +377,7 @@ krb5_init_creds_store_creds
 krb5_init_keyblock
 krb5_init_secure_context
 krb5_internalize_opaque
+krb5_is_config_principal
 krb5_is_permitted_enctype
 krb5_is_referral_realm
 krb5_is_thread_safe