/ kdc / fast.c
fast.c
  1  /*
  2   * Copyright (c) 1997-2011 Kungliga Tekniska Högskolan
  3   * (Royal Institute of Technology, Stockholm, Sweden).
  4   * All rights reserved.
  5   *
  6   * Portions Copyright (c) 2010 - 2011 Apple Inc. All rights reserved.
  7   *
  8   * Redistribution and use in source and binary forms, with or without
  9   * modification, are permitted provided that the following conditions
 10   * are met:
 11   *
 12   * 1. Redistributions of source code must retain the above copyright
 13   *    notice, this list of conditions and the following disclaimer.
 14   *
 15   * 2. Redistributions in binary form must reproduce the above copyright
 16   *    notice, this list of conditions and the following disclaimer in the
 17   *    documentation and/or other materials provided with the distribution.
 18   *
 19   * 3. Neither the name of the Institute nor the names of its contributors
 20   *    may be used to endorse or promote products derived from this software
 21   *    without specific prior written permission.
 22   *
 23   * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 24   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 25   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 26   * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 27   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 29   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 30   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 31   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 33   * SUCH DAMAGE.
 34   */
 35  
 36  #include "kdc_locl.h"
 37  
 38  #define FAST_COOKIE_VERSION2 2
 39  
 40  static krb5_error_code
 41  get_fastuser_crypto(kdc_request_t r, const char *realm, krb5_enctype enctype, krb5_crypto *crypto)
 42  {
 43      krb5_principal fast_princ;
 44      hdb_entry_ex *fast_user = NULL;
 45      Key *cookie_key = NULL;
 46      krb5_error_code ret;
 47  
 48      *crypto = NULL;
 49  
 50      ret = krb5_make_principal(r->context, &fast_princ, realm,
 51  			      KRB5_WELLKNOWN_NAME, KRB5_FAST_COOKIE, NULL);
 52      if (ret)
 53  	goto out;
 54  
 55      ret = _kdc_db_fetch(r->context, r->config, fast_princ,
 56  			HDB_F_GET_CLIENT, NULL, NULL, &fast_user);
 57      krb5_free_principal(r->context, fast_princ);
 58      if (ret) {
 59  	/*
 60  	 * Fall back to krbtgt
 61  	 */
 62  	ret = krb5_make_principal(r->context, &fast_princ, realm,
 63  				  KRB5_TGS_NAME, KRB5_FAST_COOKIE, NULL);
 64  	if (ret)
 65  	    goto out;
 66  
 67  	ret = _kdc_db_fetch(r->context, r->config, fast_princ,
 68  			    HDB_F_GET_CLIENT, NULL, NULL, &fast_user);
 69  	krb5_free_principal(r->context, fast_princ);
 70  	if (ret)
 71  	    goto out;
 72      }
 73  
 74      if (enctype == KRB5_ENCTYPE_NULL)
 75  	ret = _kdc_get_preferred_key(r->context, r->config, fast_user,
 76  				     "fast-cookie", &enctype, &cookie_key);
 77      else
 78  	ret = hdb_enctype2key(r->context, &fast_user->entry,
 79  			      enctype, &cookie_key);
 80      if (ret)
 81  	goto out;
 82  
 83      ret = krb5_crypto_init(r->context, &cookie_key->key, 0, crypto);
 84      if (ret)
 85  	goto out;
 86  
 87   out:
 88      if (fast_user)
 89  	_kdc_free_ent(r->context, fast_user);
 90  
 91      return ret;
 92  }
 93  
 94  
 95  static krb5_error_code
 96  fast_parse_cookie(kdc_request_t r, const PA_DATA *pa)
 97  {
 98      krb5_crypto crypto = NULL;
 99      krb5_error_code ret;
100      KDCFastCookie data;
101      krb5_data d1;
102      size_t len;
103  
104      ret = decode_KDCFastCookie(pa->padata_value.data,
105  			       pa->padata_value.length,
106  			       &data, &len);
107      if (ret)
108  	return ret;
109  
110      if (len != pa->padata_value.length || data.version != FAST_COOKIE_VERSION2) {
111  	free_KDCFastCookie(&data);
112  	return KRB5KDC_ERR_POLICY;
113      }
114  
115      ret = get_fastuser_crypto(r, data.realm, data.cookie.etype, &crypto);
116      if (ret)
117  	goto out;
118  
119      ret = krb5_decrypt_EncryptedData(r->context, crypto,
120  				     KRB5_KU_H5L_COOKIE,
121  				     &data.cookie, &d1);
122      krb5_crypto_destroy(r->context, crypto);
123      if (ret)
124  	goto out;
125  
126      ret = decode_KDCFastState(d1.data, d1.length, &r->fast, &len);
127      krb5_data_free(&d1);
128      if (ret)
129  	goto out;
130  
131      if (r->fast.expiration < kdc_time) {
132  	kdc_log(r->context, r->config, 0, "fast cookie expired");
133  	ret = KRB5KDC_ERR_POLICY;
134  	free_KDCFastState(&r->fast);
135  	goto out;
136      }
137  
138   out:
139      free_KDCFastCookie(&data);
140  
141      return ret;
142  }
143  
144  static krb5_error_code
145  add_fast_cookie(kdc_request_t r, const char *realm, METHOD_DATA *method_data)
146  {
147      krb5_crypto crypto = NULL;
148      KDCFastCookie shell;
149      krb5_error_code ret;
150      krb5_data data;
151      size_t size = 0;
152  
153      memset(&shell, 0, sizeof(shell));
154      krb5_data_zero(&data);
155  
156      r->fast.expiration = kdc_time + FAST_EXPIRATION_TIME;
157  
158      ASN1_MALLOC_ENCODE(KDCFastState, data.data, data.length, 
159  		       &r->fast, &size, ret);
160      if (ret)
161  	return ret;
162      heim_assert(size == data.length, "internal asn1 encoder error");
163  
164      ret = get_fastuser_crypto(r, realm, KRB5_ENCTYPE_NULL, &crypto);
165      if (ret) {
166  	kdc_log(r->context, r->config, 0, "Failed to find fastuser for cookie encryption: %d", ret);
167  	goto out;
168      }
169  
170      ret = krb5_encrypt_EncryptedData(r->context, crypto,
171  				     KRB5_KU_H5L_COOKIE,
172  				     data.data, data.length, 0,
173  				     &shell.cookie);
174      krb5_crypto_destroy(r->context, crypto);
175      if (ret)
176  	goto out;
177      
178      free(data.data);
179      data.data = NULL;
180  
181      shell.version = FAST_COOKIE_VERSION2;
182      shell.realm = rk_UNCONST(realm);
183  
184      ASN1_MALLOC_ENCODE(KDCFastCookie, data.data, data.length, 
185  		       &shell, &size, ret);
186      free_EncryptedData(&shell.cookie);
187      if (ret)
188  	goto out;
189      heim_assert(size == data.length, "internal asn1 encoder error");
190      
191      ret = krb5_padata_add(r->context, method_data,
192  			  KRB5_PADATA_FX_COOKIE,
193  			  data.data, data.length);
194      if (ret == 0)
195  	data.data = NULL;
196  
197   out:
198      if (data.data)
199  	free(data.data);
200      return ret;
201  }
202  
203  krb5_error_code
204  _kdc_fast_mk_response(krb5_context context,
205  		      krb5_crypto armor_crypto,
206  		      METHOD_DATA *pa_data,
207  		      krb5_keyblock *strengthen_key,
208  		      KrbFastFinished *finished,
209  		      krb5uint32 nonce,
210  		      krb5_data *data)
211  {
212      PA_FX_FAST_REPLY fxfastrep;
213      KrbFastResponse fastrep;
214      krb5_error_code ret;
215      krb5_data buf;
216      size_t size = 0;
217  
218      memset(&fxfastrep, 0, sizeof(fxfastrep));
219      memset(&fastrep, 0, sizeof(fastrep));
220      krb5_data_zero(data);
221  
222      if (pa_data) {
223  	fastrep.padata.val = pa_data->val;
224  	fastrep.padata.len = pa_data->len;
225      }
226      fastrep.strengthen_key = strengthen_key;
227      fastrep.finished = finished;
228      fastrep.nonce = nonce;
229  
230      ASN1_MALLOC_ENCODE(KrbFastResponse, buf.data, buf.length,
231  		       &fastrep, &size, ret);
232      if (ret)
233  	return ret;
234      if (buf.length != size)
235  	krb5_abortx(context, "internal asn.1 error");
236      
237      fxfastrep.element = choice_PA_FX_FAST_REPLY_armored_data;
238  
239      ret = krb5_encrypt_EncryptedData(context,
240  				     armor_crypto,
241  				     KRB5_KU_FAST_REP,
242  				     buf.data,
243  				     buf.length,
244  				     0,
245  				     &fxfastrep.u.armored_data.enc_fast_rep);
246      krb5_data_free(&buf);
247      if (ret)
248  	return ret;
249  
250      ASN1_MALLOC_ENCODE(PA_FX_FAST_REPLY, data->data, data->length,
251  		       &fxfastrep, &size, ret);
252      free_PA_FX_FAST_REPLY(&fxfastrep);
253      if (ret)
254  	return ret;
255      if (data->length != size)
256  	krb5_abortx(context, "internal asn.1 error");
257      
258      return 0;
259  }
260  
261  
262  krb5_error_code
263  _kdc_fast_mk_error(krb5_context context,
264  		   kdc_request_t r,
265  		   METHOD_DATA *error_method,
266  		   krb5_crypto armor_crypto,
267  		   const KDC_REQ_BODY *req_body,
268  		   krb5_error_code outer_error,
269  		   const char *e_text,
270  		   krb5_principal error_client,
271  		   krb5_principal error_server,
272  		   time_t *csec, int *cusec,
273  		   krb5_data *error_msg)
274  {
275      krb5_error_code ret;
276      krb5_data e_data;
277      size_t size = 0;
278  
279      krb5_data_zero(&e_data);
280  
281      if (armor_crypto) {
282  	PA_FX_FAST_REPLY fxfastrep;
283  	KrbFastResponse fastrep;
284  
285  	memset(&fxfastrep, 0, sizeof(fxfastrep));
286  	memset(&fastrep, 0, sizeof(fastrep));
287  	    
288  	/* first add the KRB-ERROR to the fast errors */
289  
290  	ret = krb5_mk_error(context,
291  			    outer_error,
292  			    e_text,
293  			    NULL,
294  			    error_client,
295  			    error_server,
296  			    NULL,
297  			    NULL,
298  			    &e_data);
299  	if (ret)
300  	    return ret;
301  
302  	ret = krb5_padata_add(context, error_method,
303  			      KRB5_PADATA_FX_ERROR,
304  			      e_data.data, e_data.length);
305  	if (ret) {
306  	    krb5_data_free(&e_data);
307  	    return ret;
308  	}
309  
310  	ret = add_fast_cookie(r, r->server_princ->realm, error_method);
311  	if (ret) {
312  	    kdc_log(r->context, r->config, 0, "failed to add fast cookie for FAST with: %d", ret);
313  	    free_METHOD_DATA(error_method);
314  	    return ret;
315  	}
316  
317  	outer_error = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
318  	e_text = NULL;
319  	if (r->fast.flags.requested_hidden_names) {
320  	    error_client = NULL;
321  	    error_server = NULL;
322  	}
323  	csec = 0;
324  	cusec = 0;
325  
326  	ret = _kdc_fast_mk_response(context, armor_crypto,
327  				    error_method, NULL, NULL, 
328  				    req_body->nonce, &e_data);
329  	free_METHOD_DATA(error_method);
330  	if (ret)
331  	    return ret;
332  	
333  	ret = krb5_padata_add(context, error_method,
334  			      KRB5_PADATA_FX_FAST,
335  			      e_data.data, e_data.length);
336  	if (ret)
337  	    return ret;
338  
339      } else if (r->use_fast_cookie) {
340  
341  	ret = add_fast_cookie(r, r->server_princ->realm, error_method);
342  	if (ret) {
343  	    kdc_log(r->context, r->config, 0, "failed to add fast cookie forced cookie with: %d", ret);
344  	    free_METHOD_DATA(error_method);
345  	    return ret;
346  	}
347      }
348  
349      if (error_method && error_method->len) {
350  	ASN1_MALLOC_ENCODE(METHOD_DATA, e_data.data, e_data.length, 
351  			   error_method, &size, ret);
352  	if (ret)
353  	    return ret;
354  	if (e_data.length != size)
355  	    krb5_abortx(context, "internal asn.1 error");
356      }
357      
358      ret = krb5_mk_error(context,
359  			outer_error,
360  			e_text,
361  			(e_data.length ? &e_data : NULL),
362  			error_client,
363  			error_server,
364  			csec,
365  			cusec,
366  			error_msg);
367      krb5_data_free(&e_data);
368  
369      return ret;
370  }
371  
372  krb5_error_code
373  _kdc_fast_unwrap_request(kdc_request_t r,
374  			 krb5_ticket *tgs_ticket,
375  			 krb5_auth_context tgs_ac)
376  {
377      krb5_principal armor_server = NULL;
378      hdb_entry_ex *armor_user = NULL;
379      PA_FX_FAST_REQUEST fxreq;
380      krb5_auth_context ac = NULL;
381      krb5_ticket *ticket = NULL;
382      krb5_flags ap_req_options;
383      Key *armor_key = NULL;
384      krb5_keyblock armorkey;
385      krb5_error_code ret;
386      krb5_ap_req ap_req;
387      unsigned char *buf;
388      KrbFastReq fastreq;
389      const PA_DATA *pa;
390      krb5_data data;
391      size_t len;
392      int i = 0;
393  
394      i = 0;
395      pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST);
396      if (pa == NULL) {
397  	kdc_log(r->context, r->config, 10,
398  		"Not a fast request");
399  
400  	/*
401  	 * Check for fx cookie in the outside (for SRP pa outside of FAST)
402  	 */
403  	i = 0;
404  	pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE);
405  	if (pa) {
406  	    ret = fast_parse_cookie(r, pa);
407  	    if (ret)
408  		goto out;
409  	}
410  
411  	return 0;
412      }
413  
414      ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data,
415  				    pa->padata_value.length,
416  				    &fxreq,
417  				    &len);
418      if (ret) {
419  	kdc_log(r->context, r->config, 0,
420  		"Failed to decode FX_FAST_REQUEST: %d", ret);
421  	goto out;
422      }
423  
424      if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) {
425  	kdc_log(r->context, r->config, 0,
426  		"FAST contain unknown type: %d", (int)fxreq.element);
427  	ret = KRB5KDC_ERR_PREAUTH_FAILED;
428  	goto out;
429      }
430  
431      /*
432       * If check for armor data or its not a TGS-REQ with implicit
433       * armor.
434       */
435      if (fxreq.u.armored_data.armor == NULL && tgs_ac == NULL) {
436  	kdc_log(r->context, r->config, 0,
437  		"AS-REQ armor missing");
438  	ret = KRB5KDC_ERR_PREAUTH_FAILED;
439  	goto out;
440      }
441  
442      /*
443       *
444       */
445  
446      if (tgs_ac == NULL) {
447  
448  	if (fxreq.u.armored_data.armor->armor_type != 1) {
449  	    kdc_log(r->context, r->config, 0,
450  		    "AS-REQ armor type not ap-req");
451  	    ret = KRB5KDC_ERR_PREAUTH_FAILED;
452  	    goto out;
453  	}
454  	    
455  	ret = krb5_decode_ap_req(r->context,
456  				 &fxreq.u.armored_data.armor->armor_value,
457  				 &ap_req);
458  	if(ret) {
459  	    kdc_log(r->context, r->config, 0, "AP-REQ decode failed");
460  	    goto out;
461  	}
462  
463  	/* Save that principal that was in the request */
464  	ret = _krb5_principalname2krb5_principal(r->context,
465  						 &armor_server,
466  						 ap_req.ticket.sname,
467  						 ap_req.ticket.realm);
468  	if (ret) {
469  	    free_AP_REQ(&ap_req);
470  	    goto out;
471  	}
472  
473  	ret = _kdc_db_fetch(r->context, r->config, armor_server,
474  			    HDB_F_GET_SERVER, NULL, NULL, &armor_user);
475  	if(ret == HDB_ERR_NOT_FOUND_HERE) {
476  	    kdc_log(r->context, r->config, 5,
477  		    "Armor key does not have secrets at this KDC, "
478  		    "need to proxy");
479  	    goto out;
480  	} else if (ret) {
481  	    free_AP_REQ(&ap_req);
482  	    ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
483  	    goto out;
484  	}
485  	
486  	ret = hdb_enctype2key(r->context, &armor_user->entry,
487  			      ap_req.ticket.enc_part.etype,
488  			      &armor_key);
489  	if (ret) {
490  	    free_AP_REQ(&ap_req);
491  	    goto out;
492  	}
493  	
494  	ret = krb5_verify_ap_req2(r->context, &ac, 
495  				  &ap_req,
496  				  armor_server,
497  				  &armor_key->key,
498  				  0,
499  				  &ap_req_options,
500  				  &ticket, 
501  				  KRB5_KU_AP_REQ_AUTH);
502  	free_AP_REQ(&ap_req);
503  	if (ret)
504  	    goto out;
505      } else {
506  	heim_assert(tgs_ticket != NULL, "tgs_ac but not ticket ?");
507  	ac = tgs_ac;
508  	ticket = tgs_ticket;
509      }
510  
511      if (ac->remote_subkey == NULL) {
512  	krb5_auth_con_free(r->context, ac);
513  	kdc_log(r->context, r->config, 0,
514  		"FAST AP-REQ remote subkey missing");
515  	ret = KRB5KDC_ERR_PREAUTH_FAILED;
516  	goto out;
517      }		
518  
519      ret = _krb5_fast_armor_key(r->context,
520  			       ac->remote_subkey,
521  			       &ticket->ticket.key,
522  			       &armorkey,
523  			       &r->armor_crypto);
524  
525      if (ret)
526  	goto out;
527  
528      krb5_free_keyblock_contents(r->context, &armorkey);
529  
530      ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto,
531  				     KRB5_KU_FAST_ENC,
532  				     &fxreq.u.armored_data.enc_fast_req,
533  				     &data);
534      if (ret) {
535  	kdc_log(r->context, r->config, 0,
536  		"Failed to decrypt FAST request");
537  	goto out;
538      }
539  
540      ret = decode_KrbFastReq(data.data, data.length, &fastreq, NULL);
541      krb5_data_free(&data);
542      if (ret)
543  	goto out;
544  
545      /*
546       * Now look for the FX-COOKIE in the inner padata.
547       */
548  
549      i = 0;
550      pa = krb5_find_padata(fastreq.padata.val, fastreq.padata.len, KRB5_PADATA_FX_COOKIE, &i);
551      if (pa) {
552  	ret = fast_parse_cookie(r, pa);
553  	if (ret)
554  	    goto out;
555      }
556  
557      /*
558       * verify req-checksum of the outer body
559       */
560      if (tgs_ac) {
561  	
562  	/*
563  	 * -- For TGS, contains the checksum performed over the type
564  	 * -- AP-REQ in the PA-TGS-REQ padata.
565  	 */
566  
567  	i = 0;
568  	pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_TGS_REQ);
569  	if (pa == NULL) {
570  	    kdc_log(r->context, r->config, 0,
571  		    "FAST TGS request missing TGS-REQ padata");
572  	    ret = KRB5KRB_ERR_GENERIC;
573  	    goto out;
574  	}
575  
576  	ret = krb5_verify_checksum(r->context, r->armor_crypto,
577  				   KRB5_KU_FAST_REQ_CHKSUM,
578  				   pa->padata_value.data, pa->padata_value.length, 
579  				   &fxreq.u.armored_data.req_checksum);
580  	if (ret) {
581  	    kdc_log(r->context, r->config, 0,
582  		    "FAST TGS request have a bad checksum");
583  	    goto out;
584  	}
585  
586      } else {
587  	size_t size = 0;
588  
589  	/*
590  	 * -- For AS, contains the checksum performed over the type
591  	 * -- KDC-REQ-BODY for the req-body field of the KDC-REQ
592  	 * -- structure;
593  	 */
594  	ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &r->req.req_body, &size, ret);
595  	if (ret)
596  	    goto out;
597  	heim_assert(size == len, "internal asn1 error");
598  
599  	ret = krb5_verify_checksum(r->context, r->armor_crypto,
600  				   KRB5_KU_FAST_REQ_CHKSUM,
601  				   buf, len, 
602  				   &fxreq.u.armored_data.req_checksum);
603  	free(buf);
604  	if (ret) {
605  	    kdc_log(r->context, r->config, 0,
606  		    "FAST AS request have a bad checksum");
607  	    goto out;
608  	}
609      }
610  
611      /*
612       * check for unsupported mandatory options
613       */
614      if (FastOptions2int(fastreq.fast_options) & 0xfffc) {
615  	kdc_log(r->context, r->config, 0,
616  		"FAST unsupported mandatory option set");
617  	ret = KRB5KDC_ERR_PREAUTH_FAILED;
618  	goto out;
619      }
620  
621      r->fast.flags.requested_hidden_names = fastreq.fast_options.hide_client_names;
622  
623      /* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */
624      if (r->req.padata)
625  	free_METHOD_DATA(r->req.padata);
626      else
627  	ALLOC(r->req.padata);
628  
629      ret = copy_METHOD_DATA(&fastreq.padata, r->req.padata);
630      if (ret)
631  	goto out;
632  
633      free_KDC_REQ_BODY(&r->req.req_body);
634      ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body);
635      if (ret)
636  	goto out;
637  
638      free_KrbFastReq(&fastreq);
639      free_PA_FX_FAST_REQUEST(&fxreq);
640  
641      kdc_log(r->context, r->config, 0, "Client uses FAST");
642   out:
643      if (ac && ac != tgs_ac)
644  	krb5_auth_con_free(r->context, ac);
645      if (ticket && ticket != tgs_ticket)
646  	krb5_free_ticket(r->context, ticket);
647  
648      if (armor_server)
649  	krb5_free_principal(r->context, armor_server);
650      if(armor_user)
651  	_kdc_free_ent(r->context, armor_user);
652  
653      return ret;
654  }
655  
656  krb5_error_code
657  _kdc_fast_strengthen_reply_key(kdc_request_t r)
658  {
659      if (r->armor_crypto) {
660  	krb5_keyblock new_reply_key;
661  	krb5_error_code ret;
662  
663  	kdc_log(r->context, r->config, 0,
664  		"FAST strengthen reply key with strengthen-key");
665  
666  	heim_assert(r->reply_key.keytype != KRB5_ENCTYPE_NULL, "null enctype ?");
667  
668  	ret = krb5_generate_random_keyblock(r->context, r->reply_key.keytype, &r->strengthen_key);
669  	if (ret)
670  	    krb5_abortx(r->context, "random generator fail");
671  
672  	_krb5_debug_keyblock(r->context, 10, "fast: strengthen_key", &r->strengthen_key);
673  	_krb5_debug_keyblock(r->context, 10, "fast: old reply_key", &r->reply_key);
674  
675  	ret = _krb5_fast_cf2(r->context,
676  			     &r->strengthen_key, "strengthenkey",
677  			     &r->reply_key, "replykey",
678  			     &new_reply_key, NULL);
679  	if (ret)
680  	    return ret;
681  	
682  	krb5_free_keyblock_contents(r->context, &r->reply_key);
683  	ret = krb5_copy_keyblock_contents(r->context, &new_reply_key, &r->reply_key);
684  	krb5_free_keyblock_contents(r->context, &new_reply_key);
685  	if (ret)
686  	    return ret;
687      }
688  
689      _krb5_debug_keyblock(r->context, 10, "fast: reply_key", &r->reply_key);
690  
691      return 0;
692  }