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