xref: /freebsd/crypto/krb5/src/lib/krb5/krb/fast.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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 #include "int-proto.h"
29 
30 /*
31  * It is possible to support sending a request that includes both a FAST and
32  * normal version.  This would complicate the pre-authentication logic
33  * significantly.  You would need to maintain two contexts, one for FAST and
34  * one for normal use.  In adition, you would need to manage the security
35  * issues surrounding downgrades.  However trying FAST at all requires an armor
36  * key.  Generally in obtaining the armor key, the client learns enough to know
37  * that FAST is supported.  If not, the client can see FAST in the
38  * preauth_required error's padata and retry with FAST.  So, this
39  * implementation does not support FAST+normal.
40  *
41  * We store the outer version of the request to use.  The caller stores the
42  * inner version.  We handle the encoding of the request body (and request) and
43  * provide encoded request bodies for the caller to use as these may be used
44  * for checksums.  In the AS case we also evaluate whether to continue a
45  * conversation as one of the important questions there is the presence of a
46  * cookie.
47  */
48 #include "fast.h"
49 #include "int-proto.h"
50 
51 static krb5_error_code
fast_armor_ap_request(krb5_context context,struct krb5int_fast_request_state * state,krb5_ccache ccache,krb5_principal target_principal)52 fast_armor_ap_request(krb5_context context,
53                       struct krb5int_fast_request_state *state,
54                       krb5_ccache ccache, krb5_principal target_principal)
55 {
56     krb5_error_code retval = 0;
57     krb5_creds creds, *out_creds = NULL;
58     krb5_auth_context authcontext = NULL;
59     krb5_data encoded_authenticator;
60     krb5_fast_armor *armor = NULL;
61     krb5_keyblock *subkey = NULL, *armor_key = NULL;
62 
63     encoded_authenticator.data = NULL;
64     memset(&creds, 0, sizeof(creds));
65     creds.server = target_principal;
66     retval = krb5_cc_get_principal(context, ccache, &creds.client);
67     if (retval == 0)
68         retval = krb5_get_credentials(context, 0, ccache,  &creds, &out_creds);
69     if (retval == 0) {
70         TRACE_FAST_ARMOR_CCACHE_KEY(context, &out_creds->keyblock);
71         retval = krb5_mk_req_extended(context, &authcontext,
72                                       AP_OPTS_USE_SUBKEY, NULL /*data*/,
73                                       out_creds, &encoded_authenticator);
74     }
75     if (retval == 0)
76         retval = krb5_auth_con_getsendsubkey(context, authcontext, &subkey);
77     if (retval == 0)
78         retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor",
79                                       &out_creds->keyblock, "ticketarmor",
80                                       &armor_key);
81     if (retval == 0) {
82         TRACE_FAST_ARMOR_KEY(context, armor_key);
83         armor = calloc(1, sizeof(krb5_fast_armor));
84         if (armor == NULL)
85             retval = ENOMEM;
86     }
87     if (retval == 0) {
88         armor->armor_type = KRB5_FAST_ARMOR_AP_REQUEST;
89         armor->armor_value = encoded_authenticator;
90         encoded_authenticator.data = NULL;
91         encoded_authenticator.length = 0;
92         state->armor = armor;
93         armor = NULL;
94         state->armor_key = armor_key;
95         armor_key = NULL;
96     }
97     krb5_free_keyblock(context, armor_key);
98     krb5_free_keyblock(context, subkey);
99     if (out_creds)
100         krb5_free_creds(context, out_creds);
101     /* target_principal is owned by caller. */
102     creds.server = NULL;
103     krb5_free_cred_contents(context, &creds);
104     if (encoded_authenticator.data)
105         krb5_free_data_contents(context, &encoded_authenticator);
106     krb5_auth_con_free(context, authcontext);
107     return retval;
108 }
109 
110 krb5_error_code
krb5int_fast_tgs_armor(krb5_context context,struct krb5int_fast_request_state * state,krb5_keyblock * subkey,krb5_keyblock * session_key,krb5_ccache ccache,krb5_data * target_realm)111 krb5int_fast_tgs_armor(krb5_context context,
112                        struct krb5int_fast_request_state *state,
113                        krb5_keyblock *subkey, krb5_keyblock *session_key,
114                        krb5_ccache ccache, krb5_data *target_realm)
115 {
116     krb5_principal target_principal = NULL;
117     krb5_keyblock *existing_armor = NULL;
118     krb5_error_code retval = 0;
119 
120     if (ccache) {
121         retval = krb5int_tgtname(context, target_realm, target_realm,
122                                  &target_principal);
123         if (retval == 0)
124             retval = fast_armor_ap_request(context, state, ccache,
125                                            target_principal);
126         if (retval == 0) {
127             existing_armor = state->armor_key;
128             state->armor_key = NULL;
129             retval = krb5_c_fx_cf2_simple(context, existing_armor,
130                                           "explicitarmor", subkey,
131                                           "tgsarmor", &state->armor_key);
132         }
133     } else {
134         retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor",
135                                       session_key, "ticketarmor",
136                                       &state->armor_key);
137     }
138     if (target_principal)
139         krb5_free_principal(context, target_principal);
140     krb5_free_keyblock(context, existing_armor);
141     return retval;
142 }
143 
144 krb5_error_code
krb5int_fast_prep_req_body(krb5_context context,struct krb5int_fast_request_state * state,krb5_kdc_req * request,krb5_data ** encoded_request_body)145 krb5int_fast_prep_req_body(krb5_context context,
146                            struct krb5int_fast_request_state *state,
147                            krb5_kdc_req *request,
148                            krb5_data **encoded_request_body)
149 {
150     krb5_error_code retval = 0;
151     krb5_data *local_encoded_request_body = NULL;
152 
153     assert(state != NULL);
154     *encoded_request_body = NULL;
155     if (state->armor_key == NULL)
156         return encode_krb5_kdc_req_body(request, encoded_request_body);
157     state->fast_outer_request = *request;
158     state->fast_outer_request.padata = NULL;
159     if (retval == 0)
160         retval = encode_krb5_kdc_req_body(&state->fast_outer_request,
161                                           &local_encoded_request_body);
162     if (retval == 0) {
163         *encoded_request_body = local_encoded_request_body;
164         local_encoded_request_body = NULL;
165     }
166     if (local_encoded_request_body != NULL)
167         krb5_free_data(context, local_encoded_request_body);
168     return retval;
169 }
170 
171 krb5_error_code
krb5int_fast_as_armor(krb5_context context,struct krb5int_fast_request_state * state,krb5_get_init_creds_opt * opt,krb5_kdc_req * request)172 krb5int_fast_as_armor(krb5_context context,
173                       struct krb5int_fast_request_state *state,
174                       krb5_get_init_creds_opt *opt, krb5_kdc_req *request)
175 {
176     krb5_error_code retval = 0;
177     krb5_ccache ccache = NULL;
178     krb5_principal target_principal = NULL;
179     krb5_data *target_realm;
180     const char *ccname = k5_gic_opt_get_fast_ccache_name(opt);
181     krb5_flags fast_flags;
182 
183     krb5_clear_error_message(context);
184     target_realm = &request->server->realm;
185     if (ccname != NULL) {
186         TRACE_FAST_ARMOR_CCACHE(context, ccname);
187         state->fast_state_flags |= KRB5INT_FAST_ARMOR_AVAIL;
188         retval = krb5_cc_resolve(context, ccname, &ccache);
189         if (retval == 0) {
190             retval = krb5int_tgtname(context, target_realm, target_realm,
191                                      &target_principal);
192         }
193         if (retval == 0) {
194             krb5_data config_data;
195             config_data.data = NULL;
196             retval = krb5_cc_get_config(context, ccache, target_principal,
197                                         KRB5_CC_CONF_FAST_AVAIL, &config_data);
198             if ((retval == 0) && config_data.data) {
199                 TRACE_FAST_CCACHE_CONFIG(context);
200                 state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
201             }
202             krb5_free_data_contents(context, &config_data);
203             retval = 0;
204         }
205         fast_flags = k5_gic_opt_get_fast_flags(opt);
206         if (fast_flags & KRB5_FAST_REQUIRED) {
207             TRACE_FAST_REQUIRED(context);
208             state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
209         }
210         if (retval == 0 && (state->fast_state_flags & KRB5INT_FAST_DO_FAST)) {
211             retval = fast_armor_ap_request(context, state, ccache,
212                                            target_principal);
213         }
214         if (retval != 0) {
215             k5_prependmsg(context, retval,
216                           _("Error constructing AP-REQ armor"));
217         }
218     }
219     if (ccache)
220         krb5_cc_close(context, ccache);
221     if (target_principal)
222         krb5_free_principal(context, target_principal);
223     return retval;
224 }
225 
226 /*
227  * Construct a list of outer request padata for a TGS request.  Since we do
228  * FAST TGS even when we don't have reason to believe the KDC supports FAST,
229  * the outer padata has to contain duplicates of the inner padata (such as
230  * S4U2Self padata) as well as the PA-TGS-REQ and PA-FX-FAST padata.  The
231  * caller must free *out_padata with free() as it is not a deep copy.
232  */
233 static krb5_error_code
make_tgs_outer_padata(krb5_pa_data * tgs,krb5_pa_data * fast,krb5_pa_data ** other,krb5_pa_data *** out_padata)234 make_tgs_outer_padata(krb5_pa_data *tgs, krb5_pa_data *fast,
235                       krb5_pa_data **other, krb5_pa_data ***out_padata)
236 {
237     krb5_pa_data **pa_list;
238     size_t i;
239 
240     *out_padata = NULL;
241     for (i = 0; other[i] != NULL; i++);
242     pa_list = calloc(i + 3, sizeof(*pa_list));
243     if (pa_list == NULL)
244         return ENOMEM;
245     pa_list[0] = tgs;
246     pa_list[1] = fast;
247     for (i = 0; other[i] != NULL; i++)
248         pa_list[i + 2] = other[i];
249     *out_padata = pa_list;
250     return 0;
251 }
252 
253 krb5_error_code
krb5int_fast_prep_req(krb5_context context,struct krb5int_fast_request_state * state,krb5_kdc_req * request,const krb5_data * to_be_checksummed,kdc_req_encoder_proc encoder,krb5_data ** encoded_request)254 krb5int_fast_prep_req(krb5_context context,
255                       struct krb5int_fast_request_state *state,
256                       krb5_kdc_req *request,
257                       const krb5_data *to_be_checksummed,
258                       kdc_req_encoder_proc encoder,
259                       krb5_data **encoded_request)
260 {
261     krb5_error_code retval = 0;
262     krb5_pa_data *pa_array[2], **pa_tgs_array = NULL;
263     krb5_pa_data pa[2];
264     krb5_fast_req fast_req;
265     krb5_pa_data *tgs = NULL;
266     krb5_fast_armored_req *armored_req = NULL;
267     krb5_data *encoded_fast_req = NULL;
268     krb5_data *encoded_armored_req = NULL;
269     krb5_data *local_encoded_result = NULL;
270     int i, j;
271 
272     assert(state != NULL);
273     assert(state->fast_outer_request.padata == NULL);
274     memset(pa_array, 0, sizeof(pa_array));
275     if (state->armor_key == NULL) {
276         return encoder(request, encoded_request);
277     }
278 
279     TRACE_FAST_ENCODE(context);
280     state->nonce = request->nonce;
281     fast_req.req_body = request;
282     if (fast_req.req_body->padata == NULL) {
283         fast_req.req_body->padata = calloc(1, sizeof(krb5_pa_data *));
284         if (fast_req.req_body->padata == NULL)
285             retval = ENOMEM;
286     }
287     fast_req.fast_options = state->fast_options;
288     if (retval == 0
289         && (tgs = krb5int_find_pa_data(context, fast_req.req_body->padata,
290                                        KRB5_PADATA_AP_REQ)) != NULL) {
291         krb5_pa_data **paptr = &fast_req.req_body->padata[0];
292         for (i = 0, j = 0; paptr[j] != NULL; j++) {
293             if (paptr[j]->pa_type == KRB5_PADATA_AP_REQ)
294                 paptr[j] = NULL;
295             else
296                 paptr[i++] = paptr[j];
297         }
298         paptr[i] = NULL;
299     }
300     if (retval == 0)
301         retval = encode_krb5_fast_req(&fast_req, &encoded_fast_req);
302     if (retval == 0) {
303         armored_req = calloc(1, sizeof(krb5_fast_armored_req));
304         if (armored_req == NULL)
305             retval = ENOMEM;
306     }
307     if (retval == 0)
308         armored_req->armor = state->armor;
309     if (retval ==0)
310         retval = krb5_c_make_checksum(context, 0, state->armor_key,
311                                       KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
312                                       to_be_checksummed,
313                                       &armored_req->req_checksum);
314     if (retval == 0)
315         retval = krb5_encrypt_helper(context, state->armor_key,
316                                      KRB5_KEYUSAGE_FAST_ENC, encoded_fast_req,
317                                      &armored_req->enc_part);
318     if (retval == 0)
319         retval = encode_krb5_pa_fx_fast_request(armored_req,
320                                                 &encoded_armored_req);
321     if (retval == 0) {
322         pa[0].pa_type = KRB5_PADATA_FX_FAST;
323         pa[0].contents = (unsigned char *) encoded_armored_req->data;
324         pa[0].length = encoded_armored_req->length;
325         if (tgs) {
326             retval = make_tgs_outer_padata(tgs, pa, request->padata,
327                                            &pa_tgs_array);
328             state->fast_outer_request.padata = pa_tgs_array;
329         } else {
330             pa_array[0] = &pa[0];
331             state->fast_outer_request.padata = pa_array;
332         }
333     }
334     if (retval == 0)
335         retval = encoder(&state->fast_outer_request, &local_encoded_result);
336     if (retval == 0) {
337         *encoded_request = local_encoded_result;
338         local_encoded_result = NULL;
339     }
340     if (encoded_armored_req)
341         krb5_free_data(context, encoded_armored_req);
342     if (armored_req) {
343         armored_req->armor = NULL; /*owned by state*/
344         krb5_free_fast_armored_req(context, armored_req);
345     }
346     if (encoded_fast_req)
347         krb5_free_data(context, encoded_fast_req);
348     if (local_encoded_result)
349         krb5_free_data(context, local_encoded_result);
350     if (tgs) {
351         free(tgs->contents);
352         free(tgs);
353     }
354     state->fast_outer_request.padata = NULL;
355     free(pa_tgs_array);
356     return retval;
357 }
358 
359 static krb5_error_code
decrypt_fast_reply(krb5_context context,struct krb5int_fast_request_state * state,krb5_pa_data ** in_padata,krb5_fast_response ** response)360 decrypt_fast_reply(krb5_context context,
361                    struct krb5int_fast_request_state *state,
362                    krb5_pa_data **in_padata,
363                    krb5_fast_response **response)
364 {
365     krb5_error_code retval = 0;
366     krb5_data scratch;
367     krb5_enc_data *encrypted_response = NULL;
368     krb5_pa_data *fx_reply = NULL;
369     krb5_fast_response *local_resp = NULL;
370 
371     assert(state != NULL);
372     assert(state->armor_key);
373     fx_reply = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_FAST);
374     if (fx_reply == NULL)
375         retval = KRB5_ERR_FAST_REQUIRED;
376     TRACE_FAST_DECODE(context);
377     if (retval == 0) {
378         scratch.data = (char *) fx_reply->contents;
379         scratch.length = fx_reply->length;
380         retval = decode_krb5_pa_fx_fast_reply(&scratch, &encrypted_response);
381     }
382     scratch.data = NULL;
383     if (retval == 0) {
384         scratch.data = malloc(encrypted_response->ciphertext.length);
385         if (scratch.data == NULL)
386             retval = ENOMEM;
387         scratch.length = encrypted_response->ciphertext.length;
388     }
389     if (retval == 0)
390         retval = krb5_c_decrypt(context, state->armor_key,
391                                 KRB5_KEYUSAGE_FAST_REP, NULL,
392                                 encrypted_response, &scratch);
393     if (retval != 0)
394         k5_prependmsg(context, retval, _("Failed to decrypt FAST reply"));
395     if (retval == 0)
396         retval = decode_krb5_fast_response(&scratch, &local_resp);
397     if (retval == 0) {
398         if (local_resp->nonce != state->nonce) {
399             retval = KRB5_KDCREP_MODIFIED;
400             k5_setmsg(context, retval, _("nonce modified in FAST response: "
401                                          "KDC response modified"));
402         }
403     }
404     if (retval == 0) {
405         *response = local_resp;
406         local_resp = NULL;
407     }
408     if (scratch.data)
409         free(scratch.data);
410     if (encrypted_response)
411         krb5_free_enc_data(context, encrypted_response);
412     if (local_resp)
413         krb5_free_fast_response(context, local_resp);
414     return retval;
415 }
416 
417 /*
418  * If state contains an armor key and *err_replyptr contains a FAST error,
419  * decode it and set *err_replyptr to the inner error and *out_padata to the
420  * padata in the FAST response.  Otherwise, leave *err_replyptr alone and set
421  * *out_padata to the error e_data decoded as pa-data or typed-data, or to NULL
422  * if it doesn't decode as either.  In either case, set *retry to indicate
423  * whether the client should try to make a follow-up request.
424  */
425 krb5_error_code
krb5int_fast_process_error(krb5_context context,struct krb5int_fast_request_state * state,krb5_error ** err_replyptr,krb5_pa_data *** out_padata,krb5_boolean * retry)426 krb5int_fast_process_error(krb5_context context,
427                            struct krb5int_fast_request_state *state,
428                            krb5_error **err_replyptr,
429                            krb5_pa_data ***out_padata,
430                            krb5_boolean *retry)
431 {
432     krb5_error_code retval = 0;
433     krb5_error *err_reply = *err_replyptr;
434     krb5_pa_data *fx_error_pa;
435     krb5_pa_data **result = NULL;
436     krb5_data scratch = empty_data();
437     krb5_error *fx_error = NULL;
438     krb5_fast_response *fast_response = NULL;
439 
440     if (out_padata)
441         *out_padata = NULL;
442     if (retry)
443         *retry = 0;
444 
445     if (state->armor_key) {
446         retval = decode_krb5_padata_sequence(&err_reply->e_data, &result);
447         if (retval == 0)
448             retval = decrypt_fast_reply(context, state, result,
449                                         &fast_response);
450         if (retval) {
451             /*
452              * This can happen if the KDC does not understand FAST. We don't
453              * expect that, but treating it as the fatal error indicated by the
454              * KDC seems reasonable.
455              */
456             if (retry != NULL)
457                 *retry = 0;
458             krb5_free_pa_data(context, result);
459             return 0;
460         }
461         if (retval == 0) {
462             fx_error_pa = krb5int_find_pa_data(context, fast_response->padata,
463                                                KRB5_PADATA_FX_ERROR);
464             if (fx_error_pa == NULL) {
465                 k5_setmsg(context, KRB5KDC_ERR_PREAUTH_FAILED,
466                           _("Expecting FX_ERROR pa-data inside FAST "
467                             "container"));
468                 retval = KRB5KDC_ERR_PREAUTH_FAILED;
469             }
470         }
471         if (retval == 0) {
472             scratch = make_data(fx_error_pa->contents, fx_error_pa->length);
473             retval = decode_krb5_error(&scratch, &fx_error);
474         }
475         if (retval == 0) {
476             krb5_free_error(context, err_reply);
477             *err_replyptr = fx_error;
478             fx_error = NULL;
479             if (out_padata) {
480                 *out_padata = fast_response->padata;
481                 fast_response->padata = NULL;
482             }
483             /*
484              * If there is more than the fx_error padata, then we want
485              * to retry the error if a cookie is present
486              */
487             if (retry != NULL) {
488                 *retry = (*out_padata)[1] != NULL;
489                 if (krb5int_find_pa_data(context, *out_padata,
490                                          KRB5_PADATA_FX_COOKIE) == NULL)
491                     *retry = 0;
492             }
493         }
494     } else { /*not FAST*/
495         /* Possibly retry if there's any e_data to process. */
496         if (retry)
497             *retry = (err_reply->e_data.length > 0);
498         /* Try to decode e_data as pa-data or typed-data for out_padata. */
499         if (out_padata) {
500             retval = decode_krb5_padata_sequence(&err_reply->e_data,
501                                                  out_padata);
502             if (retval != 0) {
503                 (void)decode_krb5_typed_data(&err_reply->e_data, out_padata);
504                 retval = 0;
505             }
506         }
507     }
508     krb5_free_pa_data(context, result);
509     krb5_free_fast_response(context, fast_response);
510     if (fx_error)
511         krb5_free_error(context, fx_error);
512     return retval;
513 }
514 
515 
516 krb5_error_code
krb5int_fast_process_response(krb5_context context,struct krb5int_fast_request_state * state,krb5_kdc_rep * resp,krb5_keyblock ** strengthen_key)517 krb5int_fast_process_response(krb5_context context,
518                               struct krb5int_fast_request_state *state,
519                               krb5_kdc_rep *resp,
520                               krb5_keyblock **strengthen_key)
521 {
522     krb5_error_code retval = 0;
523     krb5_fast_response *fast_response = NULL;
524     krb5_data *encoded_ticket = NULL;
525     krb5_boolean cksum_valid;
526 
527     krb5_clear_error_message(context);
528     *strengthen_key = NULL;
529     if (state->armor_key == 0)
530         return 0;
531     retval = decrypt_fast_reply(context, state, resp->padata,
532                                 &fast_response);
533     if (retval == 0) {
534         if (fast_response->finished == 0) {
535             retval = KRB5_KDCREP_MODIFIED;
536             k5_setmsg(context, retval,
537                       _("FAST response missing finish message in KDC reply"));
538         }
539     }
540     if (retval == 0)
541         retval = encode_krb5_ticket(resp->ticket, &encoded_ticket);
542     if (retval == 0)
543         retval = krb5_c_verify_checksum(context, state->armor_key,
544                                         KRB5_KEYUSAGE_FAST_FINISHED,
545                                         encoded_ticket,
546                                         &fast_response->finished->ticket_checksum,
547                                         &cksum_valid);
548     if (retval == 0 && cksum_valid == 0) {
549         retval = KRB5_KDCREP_MODIFIED;
550         k5_setmsg(context, retval, _("Ticket modified in KDC reply"));
551     }
552     if (retval == 0) {
553         krb5_free_principal(context, resp->client);
554         resp->client = fast_response->finished->client;
555         fast_response->finished->client = NULL;
556         *strengthen_key = fast_response->strengthen_key;
557         fast_response->strengthen_key = NULL;
558         krb5_free_pa_data(context, resp->padata);
559         resp->padata = fast_response->padata;
560         fast_response->padata = NULL;
561     }
562     if (fast_response)
563         krb5_free_fast_response(context, fast_response);
564     if (encoded_ticket)
565         krb5_free_data(context, encoded_ticket);
566     return retval;
567 }
568 
569 krb5_error_code
krb5int_fast_reply_key(krb5_context context,const krb5_keyblock * strengthen_key,const krb5_keyblock * existing_key,krb5_keyblock * out_key)570 krb5int_fast_reply_key(krb5_context context,
571                        const krb5_keyblock *strengthen_key,
572                        const krb5_keyblock *existing_key,
573                        krb5_keyblock *out_key)
574 {
575     krb5_keyblock *key = NULL;
576     krb5_error_code retval = 0;
577     krb5_free_keyblock_contents(context, out_key);
578     if (strengthen_key) {
579         retval = krb5_c_fx_cf2_simple(context, (krb5_keyblock *)strengthen_key,
580                                       "strengthenkey",
581                                       (krb5_keyblock *)existing_key,
582                                       "replykey", &key);
583         if (retval == 0) {
584             TRACE_FAST_REPLY_KEY(context, key);
585             *out_key = *key;
586             free(key);
587         }
588     } else {
589         retval = krb5_copy_keyblock_contents(context, existing_key, out_key);
590     }
591     return retval;
592 }
593 
594 
595 krb5_error_code
krb5int_fast_make_state(krb5_context context,struct krb5int_fast_request_state ** state)596 krb5int_fast_make_state(krb5_context context,
597                         struct krb5int_fast_request_state **state)
598 {
599     struct krb5int_fast_request_state *local_state ;
600 
601     local_state = malloc(sizeof *local_state);
602     if (local_state == NULL)
603         return ENOMEM;
604     memset(local_state, 0, sizeof(*local_state));
605     *state = local_state;
606     return 0;
607 }
608 
609 void
krb5int_fast_free_state(krb5_context context,struct krb5int_fast_request_state * state)610 krb5int_fast_free_state(krb5_context context,
611                         struct krb5int_fast_request_state *state)
612 {
613     if (state == NULL)
614         return;
615     /*We are responsible for none of the store in the fast_outer_req*/
616     krb5_free_keyblock(context, state->armor_key);
617     krb5_free_fast_armor(context, state->armor);
618     free(state);
619 }
620 
621 /*
622  * Implement FAST negotiation as specified in RFC 6806 section 11.  If
623  * the encrypted part of rep sets the enc-pa-rep flag, look for and
624  * verify a PA-REQ-ENC-PA-REP entry in the encrypted padata.  If a
625  * PA-FX-FAST entry is also present in the encrypted padata, set
626  * *fast_avail to true.  This will result in a fast_avail config entry
627  * being written to the credential cache, if an output ccache was
628  * specified using krb5_get_init_creds_opt_set_out_ccache().  That
629  * entry will be detected in the armor ccache by
630  * krb5int_fast_as_armor(), allowing us to use FAST without a
631  * round-trip for the KDC to indicate support, and without a downgrade
632  * attack.
633  */
634 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)635 krb5int_fast_verify_nego(krb5_context context,
636                          struct krb5int_fast_request_state *state,
637                          krb5_kdc_rep *rep, krb5_data *request,
638                          krb5_keyblock *decrypting_key,
639                          krb5_boolean *fast_avail)
640 {
641     krb5_error_code retval = 0;
642     krb5_checksum *checksum = NULL;
643     krb5_pa_data *pa;
644     krb5_data scratch;
645     krb5_boolean valid;
646 
647     *fast_avail = FALSE;
648     if (rep->enc_part2->flags& TKT_FLG_ENC_PA_REP) {
649         pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
650                                   KRB5_ENCPADATA_REQ_ENC_PA_REP);
651         if (pa == NULL)
652             retval = KRB5_KDCREP_MODIFIED;
653         else {
654             scratch.data = (char *) pa->contents;
655             scratch.length = pa->length;
656         }
657         if (retval == 0)
658             retval = decode_krb5_checksum(&scratch, &checksum);
659         if (retval == 0)
660             retval = krb5_c_verify_checksum(context, decrypting_key,
661                                             KRB5_KEYUSAGE_AS_REQ,
662                                             request, checksum, &valid);
663         if (retval == 0 &&valid == 0)
664             retval = KRB5_KDCREP_MODIFIED;
665         if (retval == 0) {
666             pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
667                                       KRB5_PADATA_FX_FAST);
668             *fast_avail = (pa != NULL);
669         }
670     }
671     TRACE_FAST_NEGO(context, *fast_avail);
672     if (checksum)
673         krb5_free_checksum(context, checksum);
674     return retval;
675 }
676 
677 krb5_boolean
k5_upgrade_to_fast_p(krb5_context context,struct krb5int_fast_request_state * state,krb5_pa_data ** padata)678 k5_upgrade_to_fast_p(krb5_context context,
679                      struct krb5int_fast_request_state *state,
680                      krb5_pa_data **padata)
681 {
682     if (state->armor_key != NULL)
683         return FALSE; /* Already using FAST. */
684     if (!(state->fast_state_flags & KRB5INT_FAST_ARMOR_AVAIL))
685         return FALSE;
686     if (krb5int_find_pa_data(context, padata, KRB5_PADATA_FX_FAST) != NULL)
687         return TRUE;
688     return FALSE;
689 }
690