Trace logging
[krb5.git] / src / lib / krb5 / krb / fast.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * lib/krb5/krb/fast.c
4  *
5  * Copyright (C) 2009 by the Massachusetts Institute of Technology.
6  * All rights reserved.
7  *
8  * Export of this software from the United States of America may
9  *   require a specific license from the United States Government.
10  *   It is the responsibility of any person or organization contemplating
11  *   export to obtain such a license before exporting.
12  *
13  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14  * distribute this software and its documentation for any purpose and
15  * without fee is hereby granted, provided that the above copyright
16  * notice appear in all copies and that both that copyright notice and
17  * this permission notice appear in supporting documentation, and that
18  * the name of M.I.T. not be used in advertising or publicity pertaining
19  * to distribution of the software without specific, written prior
20  * permission.  Furthermore if you modify this software you must label
21  * your software as modified software and not distribute it in such a
22  * fashion that it might be confused with the original M.I.T. software.
23  * M.I.T. makes no representations about the suitability of
24  * this software for any purpose.  It is provided "as is" without express
25  * or implied warranty.
26  *
27  *
28  *
29  */
30
31 #include <k5-int.h>
32
33 /*
34  * It is possible to support sending a request that includes both a FAST and
35  * normal version.  This would complicate the pre-authentication logic
36  * significantly.  You would need to maintain two contexts, one for FAST and
37  * one for normal use.  In adition, you would need to manage the security
38  * issues surrounding downgrades.  However trying FAST at all requires an armor
39  * key.  Generally in obtaining the armor key, the client learns enough to know
40  * that FAST is supported.  If not, the client can see FAST in the
41  * preauth_required error's padata and retry with FAST.  So, this
42  * implementation does not support FAST+normal.
43  *
44  * We store the outer version of the request to use.  The caller stores the
45  * inner version.  We handle the encoding of the request body (and request) and
46  * provide encoded request bodies for the caller to use as these may be used
47  * for checksums.  In the AS case we also evaluate whether to continue a
48  * conversation as one of the important questions there is the presence of a
49  * cookie.
50  */
51 #include "fast.h"
52 #include "int-proto.h"
53
54 static krb5_error_code
55 fast_armor_ap_request(krb5_context context,
56                       struct krb5int_fast_request_state *state,
57                       krb5_ccache ccache, krb5_principal target_principal)
58 {
59     krb5_error_code retval = 0;
60     krb5_creds creds, *out_creds = NULL;
61     krb5_auth_context authcontext = NULL;
62     krb5_data encoded_authenticator;
63     krb5_fast_armor *armor = NULL;
64     krb5_keyblock *subkey = NULL, *armor_key = NULL;
65
66     encoded_authenticator.data = NULL;
67     memset(&creds, 0, sizeof(creds));
68     creds.server = target_principal;
69     retval = krb5_cc_get_principal(context, ccache, &creds.client);
70     if (retval == 0)
71         retval = krb5_get_credentials(context, 0, ccache,  &creds, &out_creds);
72     if (retval == 0) {
73         TRACE_FAST_ARMOR_CCACHE_KEY(context, &out_creds->keyblock);
74         retval = krb5_mk_req_extended(context, &authcontext,
75                                       AP_OPTS_USE_SUBKEY, NULL /*data*/,
76                                       out_creds, &encoded_authenticator);
77     }
78     if (retval == 0)
79         retval = krb5_auth_con_getsendsubkey(context, authcontext, &subkey);
80     if (retval == 0)
81         retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor",
82                                       &out_creds->keyblock, "ticketarmor",
83                                       &armor_key);
84     if (retval == 0) {
85         TRACE_FAST_ARMOR_KEY(context, armor_key);
86         armor = calloc(1, sizeof(krb5_fast_armor));
87         if (armor == NULL)
88             retval = ENOMEM;
89     }
90     if (retval == 0) {
91         armor->armor_type = KRB5_FAST_ARMOR_AP_REQUEST;
92         armor->armor_value = encoded_authenticator;
93         encoded_authenticator.data = NULL;
94         encoded_authenticator.length = 0;
95         state->armor = armor;
96         armor = NULL;
97         state->armor_key = armor_key;
98         armor_key = NULL;
99     }
100     krb5_free_keyblock(context, armor_key);
101     krb5_free_keyblock(context, subkey);
102     if (out_creds)
103         krb5_free_creds(context, out_creds);
104     /* target_principal is owned by caller. */
105     creds.server = NULL;
106     krb5_free_cred_contents(context, &creds);
107     if (encoded_authenticator.data)
108         krb5_free_data_contents(context, &encoded_authenticator);
109     krb5_auth_con_free(context, authcontext);
110     return retval;
111 }
112
113 krb5_error_code
114 krb5int_fast_prep_req_body(krb5_context context,
115                            struct krb5int_fast_request_state *state,
116                            krb5_kdc_req *request,
117                            krb5_data **encoded_request_body)
118 {
119     krb5_error_code retval = 0;
120     krb5_data *local_encoded_request_body = NULL;
121
122     assert(state != NULL);
123     *encoded_request_body = NULL;
124     if (state->armor_key == NULL)
125         return encode_krb5_kdc_req_body(request, encoded_request_body);
126     state->fast_outer_request = *request;
127     state->fast_outer_request.padata = NULL;
128     if (retval == 0)
129         retval = encode_krb5_kdc_req_body(&state->fast_outer_request,
130                                           &local_encoded_request_body);
131     if (retval == 0) {
132         *encoded_request_body = local_encoded_request_body;
133         local_encoded_request_body = NULL;
134     }
135     if (local_encoded_request_body != NULL)
136         krb5_free_data(context, local_encoded_request_body);
137     return retval;
138 }
139
140 krb5_error_code
141 krb5int_fast_as_armor(krb5_context context,
142                       struct krb5int_fast_request_state *state,
143                       krb5_gic_opt_ext *opte,
144                       krb5_kdc_req *request)
145 {
146     krb5_error_code retval = 0;
147     krb5_ccache ccache = NULL;
148     krb5_principal target_principal = NULL;
149     krb5_data *target_realm;
150
151     krb5_clear_error_message(context);
152     target_realm = krb5_princ_realm(context, request->server);
153     if (opte->opt_private->fast_ccache_name) {
154         TRACE_FAST_ARMOR_CCACHE(context, opte->opt_private->fast_ccache_name);
155         state->fast_state_flags |= KRB5INT_FAST_ARMOR_AVAIL;
156         retval = krb5_cc_resolve(context, opte->opt_private->fast_ccache_name,
157                                  &ccache);
158         if (retval == 0) {
159             retval = krb5int_tgtname(context, target_realm, target_realm,
160                                   &target_principal);
161         }
162         if (retval == 0) {
163             krb5_data config_data;
164             config_data.data = NULL;
165             retval = krb5_cc_get_config(context, ccache, target_principal,
166                                         KRB5_CONF_FAST_AVAIL, &config_data);
167             if ((retval == 0) && config_data.data) {
168                 TRACE_FAST_CCACHE_CONFIG(context);
169                 state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
170             }
171             krb5_free_data_contents(context, &config_data);
172             retval = 0;
173         }
174         if (opte->opt_private->fast_flags & KRB5_FAST_REQUIRED) {
175             TRACE_FAST_REQUIRED(context);
176             state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
177         }
178         if (retval == 0 && (state->fast_state_flags & KRB5INT_FAST_DO_FAST)) {
179             retval = fast_armor_ap_request(context, state, ccache,
180                                            target_principal);
181         }
182         if (retval != 0) {
183             const char * errmsg;
184             errmsg = krb5_get_error_message(context, retval);
185             if (errmsg) {
186                 krb5_set_error_message(context, retval,
187                                        "%s constructing AP-REQ armor", errmsg);
188                 krb5_free_error_message(context, errmsg);
189             }
190         }
191     }
192     if (ccache)
193         krb5_cc_close(context, ccache);
194     if (target_principal)
195         krb5_free_principal(context, target_principal);
196     return retval;
197 }
198
199
200 krb5_error_code
201 krb5int_fast_prep_req(krb5_context context,
202                       struct krb5int_fast_request_state *state,
203                       krb5_kdc_req *request,
204                       const krb5_data *to_be_checksummed,
205                       kdc_req_encoder_proc encoder,
206                       krb5_data **encoded_request)
207 {
208     krb5_error_code retval = 0;
209     krb5_pa_data *pa_array[2];
210     krb5_pa_data pa[2];
211     krb5_fast_req fast_req;
212     krb5_fast_armored_req *armored_req = NULL;
213     krb5_data *encoded_fast_req = NULL;
214     krb5_data *encoded_armored_req = NULL;
215     krb5_data *local_encoded_result = NULL;
216     krb5_cksumtype cksumtype;
217     krb5_data random_data;
218     char random_buf[4];
219
220     assert(state != NULL);
221     assert(state->fast_outer_request.padata == NULL);
222     memset(pa_array, 0, sizeof pa_array);
223     if (state->armor_key == NULL) {
224         return encoder(request, encoded_request);
225     }
226
227     TRACE_FAST_ENCODE(context);
228     /* Fill in a fresh random nonce for each inner request*/
229     random_data.length = 4;
230     random_data.data = (char *)random_buf;
231     retval = krb5_c_random_make_octets(context, &random_data);
232     if (retval == 0) {
233         request->nonce = 0x7fffffff & load_32_n(random_buf);
234         state->nonce = request->nonce;
235     }
236     fast_req.req_body =  request;
237     if (fast_req.req_body->padata == NULL) {
238         fast_req.req_body->padata = calloc(1, sizeof(krb5_pa_data *));
239         if (fast_req.req_body->padata == NULL)
240             retval = ENOMEM;
241     }
242     fast_req.fast_options = state->fast_options;
243     if (retval == 0)
244         retval = encode_krb5_fast_req(&fast_req, &encoded_fast_req);
245     if (retval == 0) {
246         armored_req = calloc(1, sizeof(krb5_fast_armored_req));
247         if (armored_req == NULL)
248             retval = ENOMEM;
249     }
250     if (retval == 0)
251         armored_req->armor = state->armor;
252     if (retval == 0)
253         retval = krb5int_c_mandatory_cksumtype(context,
254                                                state->armor_key->enctype,
255                                                &cksumtype);
256     /* DES enctypes have unkeyed mandatory checksums; need a keyed one. */
257     if (retval == 0 && !krb5_c_is_keyed_cksum(cksumtype))
258         cksumtype = CKSUMTYPE_RSA_MD5_DES;
259     if (retval ==0)
260         retval = krb5_c_make_checksum(context, cksumtype, state->armor_key,
261                                       KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
262                                       to_be_checksummed,
263                                       &armored_req->req_checksum);
264     if (retval == 0)
265         retval = krb5_encrypt_helper(context, state->armor_key,
266                                      KRB5_KEYUSAGE_FAST_ENC, encoded_fast_req,
267                                      &armored_req->enc_part);
268     if (retval == 0)
269         retval = encode_krb5_pa_fx_fast_request(armored_req,
270                                                 &encoded_armored_req);
271     if (retval==0) {
272         pa[0].pa_type = KRB5_PADATA_FX_FAST;
273         pa[0].contents = (unsigned char *) encoded_armored_req->data;
274         pa[0].length = encoded_armored_req->length;
275         pa_array[0] = &pa[0];
276     }
277     state->fast_outer_request.padata = pa_array;
278     if(retval == 0)
279         retval = encoder(&state->fast_outer_request, &local_encoded_result);
280     if (retval == 0) {
281         *encoded_request = local_encoded_result;
282         local_encoded_result = NULL;
283     }
284     if (encoded_armored_req)
285         krb5_free_data(context, encoded_armored_req);
286     if (armored_req) {
287         armored_req->armor = NULL; /*owned by state*/
288         krb5_free_fast_armored_req(context, armored_req);
289     }
290     if (encoded_fast_req)
291         krb5_free_data(context, encoded_fast_req);
292     if (local_encoded_result)
293         krb5_free_data(context, local_encoded_result);
294     state->fast_outer_request.padata = NULL;
295     return retval;
296 }
297
298 static krb5_error_code
299 decrypt_fast_reply(krb5_context context,
300                    struct krb5int_fast_request_state *state,
301                    krb5_pa_data **in_padata,
302                    krb5_fast_response **response)
303 {
304     krb5_error_code retval = 0;
305     krb5_data scratch;
306     krb5_enc_data *encrypted_response = NULL;
307     krb5_pa_data *fx_reply = NULL;
308     krb5_fast_response *local_resp = NULL;
309
310     assert(state != NULL);
311     assert(state->armor_key);
312     fx_reply = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_FAST);
313     if (fx_reply == NULL)
314         retval = KRB5_ERR_FAST_REQUIRED;
315     TRACE_FAST_DECODE(context);
316     if (retval == 0) {
317         scratch.data = (char *) fx_reply->contents;
318         scratch.length = fx_reply->length;
319         retval = decode_krb5_pa_fx_fast_reply(&scratch, &encrypted_response);
320     }
321     scratch.data = NULL;
322     if (retval == 0) {
323         scratch.data = malloc(encrypted_response->ciphertext.length);
324         if (scratch.data == NULL)
325             retval = ENOMEM;
326         scratch.length = encrypted_response->ciphertext.length;
327     }
328     if (retval == 0)
329         retval = krb5_c_decrypt(context, state->armor_key,
330                                 KRB5_KEYUSAGE_FAST_REP, NULL,
331                                 encrypted_response, &scratch);
332     if (retval != 0) {
333         const char * errmsg;
334         errmsg = krb5_get_error_message(context, retval);
335         krb5_set_error_message(context, retval,
336                                "%s while decrypting FAST reply", errmsg);
337         krb5_free_error_message(context, errmsg);
338     }
339     if (retval == 0)
340         retval = decode_krb5_fast_response(&scratch, &local_resp);
341     if (retval == 0) {
342         if (local_resp->nonce != state->nonce) {
343             retval = KRB5_KDCREP_MODIFIED;
344             krb5_set_error_message(context, retval, "nonce modified in FAST "
345                                    "response: KDC response modified");
346         }
347     }
348     if (retval == 0) {
349         *response = local_resp;
350         local_resp = NULL;
351     }
352     if (scratch.data)
353         free(scratch.data);
354     if (encrypted_response)
355         krb5_free_enc_data(context, encrypted_response);
356     if (local_resp)
357         krb5_free_fast_response(context, local_resp);
358     return retval;
359 }
360
361 /*
362  * FAST separates two concepts: the set of padata we're using to
363  * decide what pre-auth mechanisms to use and the set of padata we're
364  * making available to mechanisms in order for them to respond to an
365  * error.  The plugin interface in March 2009 does not permit
366  * separating these concepts for the plugins.  This function makes
367  * both available for future revisions to the plugin interface.  It
368  * also re-encodes the padata from the current error as a encoded
369  * typed-data and puts that in the e_data field.  That will allow
370  * existing plugins with the old interface to find the error data.
371  * The output parameter out_padata contains the padata from the error
372  * whenever padata  is available (all the time with fast).
373  */
374 krb5_error_code
375 krb5int_fast_process_error(krb5_context context,
376                            struct krb5int_fast_request_state *state,
377                            krb5_error **err_replyptr,
378                            krb5_pa_data ***out_padata,
379                            krb5_boolean *retry)
380 {
381     krb5_error_code retval = 0;
382     krb5_error *err_reply = *err_replyptr;
383
384     *out_padata = NULL;
385     *retry = 0;
386     if (state->armor_key) {
387         krb5_pa_data *fx_error_pa;
388         krb5_pa_data **result = NULL;
389         krb5_data scratch, *encoded_td = NULL;
390         krb5_error *fx_error = NULL;
391         krb5_fast_response *fast_response = NULL;
392
393         retval = decode_krb5_padata_sequence(&err_reply->e_data, &result);
394         if (retval == 0)
395             retval = decrypt_fast_reply(context, state, result,
396                                         &fast_response);
397         if (retval) {
398             /*
399              * This can happen if the KDC does not understand FAST. We don't
400              * expect that, but treating it as the fatal error indicated by the
401              * KDC seems reasonable.
402              */
403             *retry = 0;
404             krb5_free_pa_data(context, result);
405             return 0;
406         }
407         krb5_free_pa_data(context, result);
408         result = NULL;
409         if (retval == 0) {
410             fx_error_pa = krb5int_find_pa_data(context, fast_response->padata,
411                                                KRB5_PADATA_FX_ERROR);
412             if (fx_error_pa == NULL) {
413                 krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
414                                        "Expecting FX_ERROR pa-data inside "
415                                        "FAST container");
416                 retval = KRB5KDC_ERR_PREAUTH_FAILED;
417             }
418         }
419         if (retval == 0) {
420             scratch.data = (char *) fx_error_pa->contents;
421             scratch.length = fx_error_pa->length;
422             retval = decode_krb5_error(&scratch, &fx_error);
423         }
424         /*
425          * krb5_pa_data and krb5_typed_data are safe to cast between:
426          * they have the same type fields in the same order.
427          * (krb5_preauthtype is a krb5_int32).  If krb5_typed_data is
428          * ever changed then this will need to be a copy not a cast.
429          */
430         if (retval == 0)
431             retval = encode_krb5_typed_data((const krb5_typed_data **)
432                                             fast_response->padata,
433                                             &encoded_td);
434         if (retval == 0) {
435             fx_error->e_data = *encoded_td;
436             free(encoded_td); /*contents owned by fx_error*/
437             encoded_td = NULL;
438             krb5_free_error(context, err_reply);
439             *err_replyptr = fx_error;
440             fx_error = NULL;
441             *out_padata = fast_response->padata;
442             fast_response->padata = NULL;
443             /*
444              * If there is more than the fx_error padata, then we want
445              * to retry the error if a cookie is present
446              */
447             *retry = (*out_padata)[1] != NULL;
448             if (krb5int_find_pa_data(context, *out_padata,
449                                      KRB5_PADATA_FX_COOKIE) == NULL)
450                 *retry = 0;
451         }
452         if (fx_error)
453             krb5_free_error(context, fx_error);
454         krb5_free_fast_response(context, fast_response);
455     } else { /*not FAST*/
456         *retry = (err_reply->e_data.length > 0);
457         if ((err_reply->error == KDC_ERR_PREAUTH_REQUIRED ||
458              err_reply->error == KDC_ERR_PREAUTH_FAILED) &&
459             err_reply->e_data.length) {
460             krb5_pa_data **result = NULL;
461             retval = decode_krb5_padata_sequence(&err_reply->e_data, &result);
462             if (retval == 0) {
463                 *out_padata = result;
464                 return 0;
465             }
466             krb5_free_pa_data(context, result);
467             krb5_set_error_message(context, retval,
468                                    "Error decoding padata in error reply");
469             return retval;
470         }
471     }
472     return retval;
473 }
474
475
476 krb5_error_code
477 krb5int_fast_process_response(krb5_context context,
478                               struct krb5int_fast_request_state *state,
479                               krb5_kdc_rep *resp,
480                               krb5_keyblock **strengthen_key)
481 {
482     krb5_error_code retval = 0;
483     krb5_fast_response *fast_response = NULL;
484     krb5_data *encoded_ticket = NULL;
485     krb5_boolean cksum_valid;
486
487     krb5_clear_error_message(context);
488     *strengthen_key = NULL;
489     if (state->armor_key == 0)
490         return 0;
491     retval = decrypt_fast_reply(context, state, resp->padata,
492                                 &fast_response);
493     if (retval == 0) {
494         if (fast_response->finished == 0) {
495             retval = KRB5_KDCREP_MODIFIED;
496             krb5_set_error_message(context, retval, "FAST response missing "
497                                    "finish message in KDC reply");
498         }
499     }
500     if (retval == 0)
501         retval = encode_krb5_ticket(resp->ticket, &encoded_ticket);
502     if (retval == 0)
503         retval = krb5_c_verify_checksum(context, state->armor_key,
504                                         KRB5_KEYUSAGE_FAST_FINISHED,
505                                         encoded_ticket,
506                                         &fast_response->finished->ticket_checksum,
507                                         &cksum_valid);
508     if (retval == 0 && cksum_valid == 0) {
509         retval = KRB5_KDCREP_MODIFIED;
510         krb5_set_error_message(context, retval,
511                                "ticket modified in KDC reply");
512     }
513     if (retval == 0) {
514         krb5_free_principal(context, resp->client);
515         resp->client = fast_response->finished->client;
516         fast_response->finished->client = NULL;
517         *strengthen_key = fast_response->strengthen_key;
518         fast_response->strengthen_key = NULL;
519         krb5_free_pa_data(context, resp->padata);
520         resp->padata = fast_response->padata;
521         fast_response->padata = NULL;
522     }
523     if (fast_response)
524         krb5_free_fast_response(context, fast_response);
525     if (encoded_ticket)
526         krb5_free_data(context, encoded_ticket);
527     return retval;
528 }
529
530 krb5_error_code
531 krb5int_fast_reply_key(krb5_context context,
532                        krb5_keyblock *strengthen_key,
533                        krb5_keyblock *existing_key,
534                        krb5_keyblock *out_key)
535 {
536     krb5_keyblock *key = NULL;
537     krb5_error_code retval = 0;
538     krb5_free_keyblock_contents(context, out_key);
539     if (strengthen_key) {
540         retval = krb5_c_fx_cf2_simple(context, strengthen_key,
541                                       "strengthenkey", existing_key,
542                                       "replykey", &key);
543         if (retval == 0) {
544             TRACE_FAST_REPLY_KEY(context, key);
545             *out_key = *key;
546             free(key);
547         }
548     } else {
549         retval = krb5_copy_keyblock_contents(context, existing_key, out_key);
550     }
551     return retval;
552 }
553
554
555 krb5_error_code
556 krb5int_fast_make_state(krb5_context context,
557                         struct krb5int_fast_request_state **state)
558 {
559     struct krb5int_fast_request_state *local_state ;
560
561     local_state = malloc(sizeof *local_state);
562     if (local_state == NULL)
563         return ENOMEM;
564     memset(local_state, 0, sizeof(*local_state));
565     *state = local_state;
566     return 0;
567 }
568
569 void
570 krb5int_fast_free_state(krb5_context context,
571                         struct krb5int_fast_request_state *state)
572 {
573     if (state == NULL)
574         return;
575     /*We are responsible for none of the store in the fast_outer_req*/
576     krb5_free_keyblock(context, state->armor_key);
577     krb5_free_fast_armor(context, state->armor);
578     free(state);
579 }
580
581 krb5_pa_data *
582 krb5int_find_pa_data(krb5_context context, krb5_pa_data *const *padata,
583                      krb5_preauthtype pa_type)
584 {
585     krb5_pa_data * const *tmppa;
586
587     if (padata == NULL)
588         return NULL;
589
590     for (tmppa = padata; *tmppa != NULL; tmppa++) {
591         if ((*tmppa)->pa_type == pa_type)
592             break;
593     }
594
595     return *tmppa;
596 }
597
598
599 krb5_error_code
600 krb5int_fast_verify_nego(krb5_context context,
601                          struct krb5int_fast_request_state *state,
602                          krb5_kdc_rep *rep, krb5_data *request,
603                          krb5_keyblock *decrypting_key,
604                          krb5_boolean *fast_avail)
605 {
606     krb5_error_code retval = 0;
607     krb5_checksum *checksum = NULL;
608     krb5_pa_data *pa;
609     krb5_data scratch;
610     krb5_boolean valid;
611
612     *fast_avail = FALSE;
613     if (rep->enc_part2->flags& TKT_FLG_ENC_PA_REP) {
614         pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
615                                   KRB5_ENCPADATA_REQ_ENC_PA_REP);
616         if (pa == NULL)
617             retval = KRB5_KDCREP_MODIFIED;
618         else {
619             scratch.data = (char *) pa->contents;
620             scratch.length = pa->length;
621         }
622         if (retval == 0)
623             retval = decode_krb5_checksum(&scratch, &checksum);
624         if (retval == 0)
625             retval = krb5_c_verify_checksum(context, decrypting_key,
626                                             KRB5_KEYUSAGE_AS_REQ,
627                                             request, checksum, &valid);
628         if (retval == 0 &&valid == 0)
629             retval = KRB5_KDCREP_MODIFIED;
630         if (retval == 0) {
631             pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
632                                       KRB5_PADATA_FX_FAST);
633             *fast_avail = (pa != NULL);
634         }
635     }
636     TRACE_FAST_NEGO(context, *fast_avail);
637     if (checksum)
638         krb5_free_checksum(context, checksum);
639     return retval;
640 }
641
642 krb5_boolean
643 krb5int_upgrade_to_fast_p(krb5_context context,
644                           struct krb5int_fast_request_state *state,
645                           krb5_pa_data **padata)
646 {
647     if (state->armor_key != NULL)
648         return FALSE; /* Already using FAST. */
649     if (!(state->fast_state_flags & KRB5INT_FAST_ARMOR_AVAIL))
650         return FALSE;
651     if (krb5int_find_pa_data(context, padata, KRB5_PADATA_FX_FAST) != NULL) {
652         TRACE_FAST_PADATA_UPGRADE(context);
653         state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
654         return TRUE;
655     }
656     return FALSE;
657 }