/ lib / krb5 / pfs.c
pfs.c
  1  /*
  2   * Copyright (c) 2014 Kungliga Tekniska Högskolan
  3   * (Royal Institute of Technology, Stockholm, Sweden).
  4   * All rights reserved.
  5   *
  6   * Portions Copyright (c) 2014 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 "krb5_locl.h"
 37  
 38  #include <corecrypto/ccec.h>
 39  #include <corecrypto/ccec25519.h>
 40  
 41  #include <CommonCrypto/CommonRandomSPI.h>
 42  
 43  #include <pkinit_asn1.h>
 44  
 45  
 46  struct _krb5_pfs_data {
 47      ccec25519secretkey x25519priv;
 48      ccec25519pubkey x25519pub;
 49      ccec_full_ctx_t p256;
 50      krb5_keyblock keyblock;
 51      AlgorithmIdentifier ai;
 52  
 53      int config;
 54  
 55      krb5_principal client;
 56      KRB5_PFS_GROUP group;
 57  };
 58  
 59  #define PFS_X25519	1
 60  #define PFS_P256	2
 61  
 62  /*
 63   *
 64   */
 65  
 66  static int
 67  eval_string(krb5_context context, int value, const char *s)
 68  {
 69      int negative = 0;
 70      int val = 0;
 71  
 72      if (s[0] == '-') {
 73  	negative = 1;
 74  	s++;
 75      }
 76  
 77      if (strcasecmp(s, "ALL") == 0) {
 78  	val = PFS_X25519 | PFS_P256;
 79      } else if (strcasecmp(s, "dh25519") == 0) {
 80  	val = PFS_X25519;
 81      } else if (strcasecmp(s, "nist-p256") == 0) {
 82  	val = PFS_P256;
 83      } else if (strcasecmp(s, "p256") == 0) {
 84  	val = PFS_P256;
 85      } else if (strcasecmp(s, "all-nist") == 0) {
 86  	val = PFS_P256;
 87      } else {
 88  	_krb5_debugx(context, 10, "unsupported dh curve(s): %s", s);
 89  	return value;
 90      }
 91      
 92      if (negative)
 93  	value &= ~val;
 94      else
 95  	value |= val;
 96  
 97      return value;
 98  }
 99  
100  static int
101  _krb5_pfs_parse_pfs_config(krb5_context context, const char *name, const char *defval)
102  {
103      char **strs, **s;
104      int val = 0;
105  
106      strs = krb5_config_get_strings(context, NULL, "libdefaults", name, NULL);
107      if (strs) {
108  	for(s = strs; s && *s; s++) {
109  	    val = eval_string(context, val, *s);
110  	}
111      } else {
112  	val = eval_string(context, 0, defval);
113      }
114      krb5_config_free_strings(strs);
115  
116      return val;
117  }
118  
119  /*
120   *
121   */
122  
123  krb5_error_code
124  _krb5_auth_con_setup_pfs(krb5_context context,
125  			 krb5_auth_context auth_context,
126  			 krb5_enctype enctype)
127  {
128      ccec_const_cp_t cp = ccec_cp_256();
129      struct ccrng_state *rng = ccDRBGGetRngState();
130      struct _krb5_pfs_data *pfs = NULL;
131  
132      _krb5_debugx(context, 20, "Setting up PFS for auth context");
133  
134      pfs = calloc(1, sizeof(*pfs));
135      if (pfs == NULL)
136  	return krb5_enomem(context);
137  
138      pfs->config = _krb5_pfs_parse_pfs_config(context, "ap-req-dh-groups", "ALL");
139      if (pfs->config == 0) {
140  	_krb5_debugx(context, 10, "No PFS configuration");
141  	free(pfs);
142  	return 0;
143      }
144  
145      switch (enctype) {
146      case KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96:
147      case KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96:
148      case KRB5_ENCTYPE_DES3_CBC_SHA1:
149      case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5:
150  	pfs->ai.algorithm = asn1_oid_id_pkinit_kdf_ah_sha512;
151  	pfs->ai.parameters = NULL;
152  	break;
153      default:
154  	free(pfs);
155  	return 0;
156      }
157  
158      pfs->p256 = (ccec_full_ctx_t)calloc(1, ccec_full_ctx_size(ccec_ccn_size(cp)));
159      if (pfs->p256 == NULL) {
160  	free(pfs);
161  	return krb5_enomem(context);
162      }
163      
164      cccurve25519_make_key_pair(rng, pfs->x25519pub, pfs->x25519priv);
165  
166      if (ccec_generate_key_fips(cp, rng, pfs->p256)) {
167  	krb5_free_keyblock_contents(context, &pfs->keyblock);
168  	free(pfs->p256);
169  	free(pfs);
170  	return krb5_enomem(context);
171      }
172  
173      auth_context->pfs = pfs;
174  
175      return 0;
176  }
177  
178  /*
179   *
180   */
181  
182  void
183  _krb5_auth_con_free_pfs(krb5_context context, krb5_auth_context auth_context)
184  {
185      if (auth_context->pfs) {
186  	free(auth_context->pfs->p256);
187  	krb5_free_keyblock_contents(context, &auth_context->pfs->keyblock);
188  	krb5_free_principal(context, auth_context->pfs->client);
189  
190  	memset(auth_context->pfs, 0, sizeof(*auth_context->pfs));
191  	free(auth_context->pfs);
192  	auth_context->pfs = NULL;
193      }
194  }
195  
196  /*
197   *
198   */
199  
200  static krb5_error_code
201  pfs_make_share_secret(krb5_context context,
202  		      krb5_auth_context auth_context,
203  		      krb5_enctype enctype,
204  		      KRB5_PFS_SELECTION *peer)
205  {
206      struct _krb5_pfs_data *pfs = auth_context->pfs;
207      krb5_data shared_secret;
208      krb5_error_code ret;
209  
210      heim_assert(pfs, "no PFS requestd");
211  
212      krb5_data_zero(&shared_secret);
213  	
214      if (peer->group == KRB5_PFS_X25519 && (pfs->config & PFS_X25519)) {
215  	if (peer->public_key.length != sizeof(ccec25519pubkey)) {
216  	    krb5_set_error_message(context, HEIM_PFS_GROUP_INVALID,
217  				   "public key of wrong length");
218  	    return HEIM_PFS_GROUP_INVALID;
219  	}
220  
221  	ret = krb5_data_alloc(&shared_secret, sizeof(ccec25519key));
222  	if (ret)
223  	    goto out;
224  
225  	cccurve25519(shared_secret.data,
226  		     pfs->x25519priv,
227  		     peer->public_key.data);
228  
229      } else if (peer->group == KRB5_PFS_NIST_P256 && (pfs->config & PFS_P256)) {
230  	ccec_const_cp_t cp = ccec_cp_256();
231  #ifdef __clang__
232  #pragma clang diagnostic push
233  #pragma clang diagnostic ignored "-Wvla"
234  #endif
235  	ccec_pub_ctx_decl_cp(cp, pubkey);
236  #ifdef __clang__
237  #pragma clang diagnostic pop
238  #endif
239  
240  	if (ccec_import_pub(cp, peer->public_key.length, peer->public_key.data, pubkey)) {
241  	    krb5_set_error_message(context, HEIM_PFS_GROUP_INVALID,
242  				   "failed to import public key");
243  	    return HEIM_PFS_GROUP_INVALID;
244  	}
245  
246  	ret = krb5_data_alloc(&shared_secret, ccec_ccn_size(cp));
247  	if (ret)
248  	    goto out;
249  
250  	if (ccec_compute_key(pfs->p256, pubkey, &shared_secret.length, shared_secret.data)) {
251  	    krb5_set_error_message(context, HEIM_PFS_GROUP_INVALID,
252  				   "Failed to complete share key");
253  	    return HEIM_PFS_GROUP_INVALID;
254  	}
255  	
256      } else {
257  	krb5_set_error_message(context, HEIM_PFS_GROUP_INVALID,
258  			       "Group %d not accepted",
259  			       (int)peer->group);
260  	return HEIM_PFS_GROUP_INVALID;
261      }
262  
263      ret = _krb5_pk_kdf(context,
264  		       &pfs->ai,
265  		       shared_secret.data,
266  		       shared_secret.length,
267  		       pfs->client,
268  		       NULL,
269  		       enctype,
270  		       NULL,
271  		       NULL,
272  		       NULL,
273  		       &pfs->keyblock);
274      if (ret)
275  	goto out;
276  
277      _krb5_debug_keyblock(context, 20, "PFS shared keyblock", &pfs->keyblock);
278  
279      pfs->group = peer->group;
280  
281   out:
282      memset(shared_secret.data, 0, shared_secret.length);
283      krb5_data_free(&shared_secret);
284  
285      return ret;
286  }
287  
288  /*
289   *
290   */
291  
292  krb5_error_code
293  _krb5_pfs_update_key(krb5_context context,
294  		     krb5_auth_context auth_context,
295  		     const char *direction,
296  		     krb5_keyblock *subkey)
297  {
298      struct _krb5_pfs_data *pfs = auth_context->pfs;
299      krb5_keyblock newkey;
300      krb5_error_code ret;
301  
302      memset(&newkey, 0, sizeof(newkey));
303  
304      heim_assert(pfs, "no PFS requestd");
305      heim_assert(pfs->keyblock.keytype, "shared secret completed");
306      heim_assert(pfs->group != KRB5_PFS_INVALID, "no pfs group selected");
307  
308      _krb5_debugx(context, 10, "krb5_pfs: updating to PFS key for direction %s", direction);
309  
310      ret = _krb5_fast_cf2(context, subkey,
311  			 "AP PFS shared key",
312  			 &pfs->keyblock,
313  			 direction,
314  			 &newkey,
315  			 NULL);
316      /*
317       * If enctype doesn't support PRF, ignore the PFS variable, the
318       * peer should not have proposed it in the first place when the
319       * poor supported enctype was selected.
320       */
321      if (ret == HEIM_PFS_ENCTYPE_PRF_NOT_SUPPORTED)
322  	return 0;
323      else if (ret)
324  	return ret;
325  
326      krb5_free_keyblock_contents(context, subkey);
327      *subkey = newkey;
328  
329      _krb5_debug_keyblock(context, 20, direction, subkey);
330  
331      return 0;
332  }
333  
334  static krb5_error_code
335  fill_x25519_proposal(KRB5_PFS_SELECTION *sel, 
336  		     ccec25519pubkey pubkey)
337  {
338      sel->group = KRB5_PFS_X25519;
339      return krb5_data_copy(&sel->public_key, pubkey, sizeof(ccec25519pubkey));
340  }
341  
342  static krb5_error_code
343  add_x25519_proposal(krb5_context context,
344  		    KRB5_PFS_SELECTIONS *sels, 
345  		    ccec25519pubkey pubkey)
346  {
347      KRB5_PFS_SELECTION sel;
348      krb5_error_code ret;
349  
350      ret = fill_x25519_proposal(&sel, pubkey);
351      if (ret)
352  	goto out;
353  
354      ret = add_KRB5_PFS_SELECTIONS(sels, &sel);
355   out:
356      free_KRB5_PFS_SELECTION(&sel);
357      return ret;
358  }
359  
360  static krb5_error_code
361  fill_nist_proposal(krb5_context context,
362  		   KRB5_PFS_SELECTION *sel, 
363  		   KRB5_PFS_GROUP group,
364  		   ccec_full_ctx_t fullkey)
365  {
366      krb5_error_code ret;
367  
368      sel->group = group;
369      sel->public_key.length = ccec_export_pub_size(ccec_ctx_pub(fullkey));
370      sel->public_key.data = malloc(sel->public_key.length);
371      if (sel->public_key.data == NULL) {
372  	ret = krb5_enomem(context);
373  	goto out;
374      }
375  
376      ccec_export_pub(ccec_ctx_pub(fullkey), sel->public_key.data);
377      ret = 0;
378  
379   out:
380      return ret;
381  }
382  
383  static krb5_error_code
384  add_nist_proposal(krb5_context context,
385  		  KRB5_PFS_SELECTIONS *sels, 
386  		  KRB5_PFS_GROUP group,
387  		  ccec_full_ctx_t fullkey)
388  {
389      KRB5_PFS_SELECTION sel;
390      krb5_error_code ret;
391  
392      ret = fill_nist_proposal(context, &sel, group, fullkey);
393      if (ret)
394  	goto out;
395  
396      ret = add_KRB5_PFS_SELECTIONS(sels, &sel);
397   out:
398      free_KRB5_PFS_SELECTION(&sel);
399  
400      return ret;
401  }
402  
403  static krb5_boolean
404  enctype_pfs_supported(krb5_context context,
405  		      krb5_auth_context auth_context)
406  {
407      krb5_error_code ret;
408      size_t size;
409  
410      ret = krb5_crypto_prf_length(context,
411  				 auth_context->keyblock->keytype,
412  				 &size);
413      if (ret == HEIM_PFS_ENCTYPE_PRF_NOT_SUPPORTED) {
414  	_krb5_debugx(context, 10, "Enctype %d doesn't support PFS", 
415  		     auth_context->keyblock->keytype);
416  	return FALSE;
417      }
418      if (ret || size == 0)
419  	return FALSE;
420  
421      return TRUE;
422  }
423  
424  
425  /*
426   *
427   *
428   */
429  
430  krb5_error_code
431  _krb5_pfs_ap_req(krb5_context context,
432  		 krb5_auth_context auth_context,
433  		 krb5_const_principal client,
434  		 AuthorizationData *output)
435  {
436      struct _krb5_pfs_data *pfs = auth_context->pfs;
437      AuthorizationDataElement ade;
438      krb5_crypto crypto = NULL;
439      KRB5_PFS_PROPOSE pp;
440      krb5_error_code ret;
441      size_t size = 0;
442      krb5_data data;
443      
444      memset(&ade, 0, sizeof(ade));
445      memset(&pp, 0, sizeof(pp));
446      krb5_data_zero(&data);
447  
448      if (!enctype_pfs_supported(context, auth_context))
449  	return 0;
450  
451      if (pfs->config & PFS_X25519) {
452  	ret = add_x25519_proposal(context, &pp.selections, pfs->x25519pub);
453  	if (ret)
454  	    goto out;
455      }
456  
457      if (pfs->config & PFS_P256) {
458  	ret = add_nist_proposal(context, &pp.selections, KRB5_PFS_NIST_P256, pfs->p256);
459  	if (ret)
460  	    goto out;
461      }
462  
463      ret = krb5_copy_principal(context, client, &pfs->client);
464      if (ret) {
465  	_krb5_auth_con_free_pfs(context, auth_context);
466  	free_KRB5_PFS_PROPOSE(&pp);
467  	return ret;
468      }
469  
470      /*
471       * Create signture
472       */
473  
474      ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto);
475      if (ret) 
476  	goto out;
477  
478      ASN1_MALLOC_ENCODE(KRB5_PFS_PROPOSE, data.data, data.length, &pp, &size, ret);
479      if (ret)
480  	goto out;
481      heim_assert(size == data.length, "internal asn1 error");
482  
483      pp.checksum = calloc(1, sizeof(*pp.checksum));
484      if (pp.checksum == NULL) {
485  	ret = krb5_enomem(context);
486  	goto out;
487      }
488  
489      ret = krb5_create_checksum(context, crypto, KRB5_KU_H5L_PFS_AP_CHECKSUM_CLIENT, 0,
490  			       data.data, data.length, pp.checksum);
491      if (ret)
492  	goto out;
493  
494      /*
495       * Encode and add
496       */
497  
498      ade.ad_type = KRB5_AUTHDATA_PFS;
499  
500      ASN1_MALLOC_ENCODE(KRB5_PFS_PROPOSE, ade.ad_data.data, ade.ad_data.length, &pp, &size, ret);
501      if (ret)
502  	goto out;
503      if (ade.ad_data.length != size)
504  	krb5_abortx(context, "internal error in ASN.1 encoder");
505  
506      ret = add_AuthorizationData(output, &ade);
507      if (ret)
508  	goto out;
509  
510   out:
511      if (crypto)
512  	krb5_crypto_destroy(context, crypto);
513      free_KRB5_PFS_PROPOSE(&pp);
514      free_AuthorizationDataElement(&ade);
515      free(data.data);
516  
517      return ret;
518  }
519  
520  /*
521   *
522   */
523  
524  static krb5_error_code
525  _validate_rd_req_checksum(krb5_context context, krb5_keyblock *keyblock, KRB5_PFS_PROPOSE *pp)
526  {
527      krb5_error_code ret;
528      krb5_crypto crypto;
529      Checksum *checksum;
530      krb5_data data;
531      size_t size = 0;
532  
533      checksum = pp->checksum;
534      if (checksum == NULL) {
535  	krb5_set_error_message(context, HEIM_PFS_CHECKSUM_WRONG, "peer sent no checksum");
536  	return HEIM_PFS_CHECKSUM_WRONG;
537      }
538  
539      pp->checksum = NULL;
540  
541      ASN1_MALLOC_ENCODE(KRB5_PFS_PROPOSE, data.data, data.length, pp, &size, ret);
542      pp->checksum = checksum;
543      if (ret) {
544  	return ret;
545      }
546      heim_assert(data.length == size, "internal asn1 encode error");
547  
548      ret = krb5_crypto_init(context, keyblock, 0, &crypto);
549      if (ret) {
550  	free(data.data);
551  	return ret;
552      }
553  
554      if (!krb5_checksum_is_keyed(context, checksum->cksumtype)) {
555  	free(data.data);
556  	ret = HEIM_PFS_CHECKSUM_WRONG;
557  	krb5_set_error_message(context, ret, "checksum not keyed");
558  	return ret;
559      }
560  
561      ret = krb5_verify_checksum(context, crypto, KRB5_KU_H5L_PFS_AP_CHECKSUM_CLIENT,
562  			       data.data, data.length, checksum);
563      krb5_crypto_destroy(context, crypto);
564      free(data.data);
565  
566      return ret;
567  }
568  
569  
570  /*
571   *
572   */
573  
574  krb5_error_code
575  _krb5_pfs_rd_req(krb5_context context,
576  		 krb5_auth_context auth_context)
577  {
578      KRB5_PFS_SELECTION *selected = NULL;
579      AuthorizationData *ad = NULL;
580      KRB5_PFS_PROPOSE pp;
581      krb5_error_code ret;
582      krb5_data data;
583      size_t size = 0;
584      unsigned n;
585      
586      memset(&pp, 0, sizeof(pp));
587      krb5_data_zero(&data);
588  
589      if (auth_context->authenticator == NULL)
590  	return 0;
591  
592      ad = auth_context->authenticator->authorization_data;
593      if (ad == NULL)
594  	return 0;
595  
596      heim_assert(auth_context->keyblock, "pfs: don't have keyblock");
597  
598      if (!enctype_pfs_supported(context, auth_context))
599  	return 0;
600  
601      ret = _krb5_get_ad(context, ad, NULL, KRB5_AUTHDATA_PFS, &data);
602      if (ret)
603  	return 0;
604  
605      ret = decode_KRB5_PFS_PROPOSE(data.data, data.length, &pp, &size);
606      krb5_data_free(&data);
607      if (ret) 
608  	goto fail;
609  
610      ret = _validate_rd_req_checksum(context, auth_context->keyblock, &pp);
611      if (ret)
612  	goto fail;
613      
614      ret = _krb5_auth_con_setup_pfs(context, auth_context, auth_context->keyblock->keytype);
615      if (ret)
616  	goto fail;
617  
618      for (n = 0; n < pp.selections.len && selected == NULL; n++) {
619  	if (pp.selections.val[n].group == KRB5_PFS_X25519 && (auth_context->pfs->config & PFS_X25519))
620  	    selected = &pp.selections.val[n];
621  	else if (pp.selections.val[n].group == KRB5_PFS_NIST_P256  && (auth_context->pfs->config & PFS_P256))
622  	    selected = &pp.selections.val[n];
623      }
624  
625      if (selected == NULL) {
626  	krb5_set_error_message(context, HEIM_PFS_GROUP_INVALID, "No acceptable PFS group sent");
627  	ret = HEIM_PFS_GROUP_INVALID;
628  	goto fail;
629      }
630  
631      ret = _krb5_principalname2krb5_principal(context, &auth_context->pfs->client,
632  					     auth_context->authenticator->cname,
633  					     auth_context->authenticator->crealm);
634      if (ret)
635  	goto fail;
636      
637      ret = pfs_make_share_secret(context, auth_context,
638  				auth_context->keyblock->keytype,
639  				selected);
640      if (ret)
641  	goto fail;
642  
643      free_KRB5_PFS_PROPOSE(&pp);
644  
645      _krb5_debugx(context, 10, "PFS server made selected");
646  
647      return 0;
648  
649   fail:
650      free_KRB5_PFS_PROPOSE(&pp);
651      _krb5_auth_con_free_pfs(context, auth_context);
652      return ret;
653  }
654  
655  /*
656   *
657   */
658  
659  krb5_error_code
660  _krb5_pfs_mk_rep(krb5_context context,
661  		 krb5_auth_context auth_context,
662  		 AP_REP *rep)
663  {
664      struct _krb5_pfs_data *pfs = auth_context->pfs;
665      krb5_error_code ret;
666      krb5_crypto crypto = NULL;
667      krb5_data data;
668      size_t size = 0;
669  
670      heim_assert(pfs, "no pfs");
671      heim_assert(pfs->group != KRB5_PFS_INVALID, "no pfs group selected");
672  
673      krb5_data_zero(&data);
674  
675      rep->pfs = calloc(1, sizeof(*rep->pfs));
676      if (rep->pfs == NULL)
677  	return krb5_enomem(context);
678  
679      if (pfs->group == KRB5_PFS_X25519) {
680  	ret = fill_x25519_proposal(&rep->pfs->selection, pfs->x25519pub);
681  	if (ret)
682  	    goto out;
683      } else if (pfs->group == KRB5_PFS_NIST_P256) {
684  	ret = fill_nist_proposal(context, &rep->pfs->selection, KRB5_PFS_NIST_P256, pfs->p256);
685  	if (ret)
686  	    goto out;
687      } else {
688  	heim_assert(0, "Invalid PFS group");
689      }
690  
691      /*
692       *
693       */
694  
695      ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto);
696      if (ret) 
697  	goto out;
698  
699      ASN1_MALLOC_ENCODE(KRB5_PFS_ACCEPT, data.data, data.length, rep->pfs, &size, ret);
700      if (ret)
701  	goto out;
702      heim_assert(size == data.length, "internal asn1 error");
703  
704      rep->pfs->checksum = calloc(1, sizeof(*rep->pfs->checksum));
705      if (rep->pfs->checksum == NULL) {
706  	ret = krb5_enomem(context);
707  	goto out;
708      }
709  
710      ret = krb5_create_checksum(context, crypto, KRB5_KU_H5L_PFS_AP_CHECKSUM_SERVER, 0,
711  			       data.data, data.length, rep->pfs->checksum);
712      if (ret)
713  	goto out;
714  
715      /*
716       *
717       */
718  
719      _krb5_debugx(context, 20, "PFS deriving new keys on server");
720  
721      ret = _krb5_pfs_update_key(context, auth_context, "session key", auth_context->keyblock);
722      if (ret)
723  	goto out;
724  
725      if (auth_context->local_subkey) {
726  	ret = _krb5_pfs_update_key(context, auth_context, "server key", auth_context->local_subkey);
727  	if (ret)
728  	    goto out;
729      }
730  
731      if (auth_context->remote_subkey) {
732  	ret = _krb5_pfs_update_key(context, auth_context, "client key", auth_context->remote_subkey);
733  	if (ret)
734  	    goto out;
735      }
736  
737      auth_context->flags |= KRB5_AUTH_CONTEXT_USED_PFS;
738  
739   out:
740      _krb5_auth_con_free_pfs(context, auth_context);
741      krb5_crypto_destroy(context, crypto);
742      if (data.data)
743  	free(data.data);
744      if (ret) {
745  	if (rep->pfs) {
746  	    free_KRB5_PFS_ACCEPT(rep->pfs);
747  	    free(rep->pfs);
748  	    rep->pfs = NULL;
749  	}
750      }
751  
752      return ret;
753  }
754  
755  /*
756   *
757   */
758  
759  static krb5_error_code
760  _validate_rd_rep_checksum(krb5_context context, krb5_keyblock *keyblock, KRB5_PFS_ACCEPT *aa)
761  {
762      krb5_error_code ret;
763      krb5_crypto crypto;
764      Checksum *checksum;
765      krb5_data data;
766      size_t size = 0;
767  
768      checksum = aa->checksum;
769      if (checksum == NULL) {
770  	krb5_set_error_message(context, HEIM_PFS_CHECKSUM_WRONG, "peer sent no checksum");
771  	return HEIM_PFS_CHECKSUM_WRONG;
772      }
773      
774      aa->checksum = NULL;
775  
776      ASN1_MALLOC_ENCODE(KRB5_PFS_ACCEPT, data.data, data.length, aa, &size, ret);
777      aa->checksum = checksum;
778      if (ret) {
779  	return ret;
780      }
781      heim_assert(data.length == size, "internal asn1 encode error");
782  
783      ret = krb5_crypto_init(context, keyblock, 0, &crypto);
784      if (ret) {
785  	free(data.data);
786  	return ret;
787      }
788  
789      if (!krb5_checksum_is_keyed(context, checksum->cksumtype)) {
790  	free(data.data);
791  	ret = HEIM_PFS_CHECKSUM_WRONG;
792  	krb5_set_error_message(context, ret, "checksum not keyed");
793  	return ret;
794      }
795  
796      ret = krb5_verify_checksum(context, crypto, KRB5_KU_H5L_PFS_AP_CHECKSUM_SERVER,
797  			       data.data, data.length, checksum);
798      krb5_crypto_destroy(context, crypto);
799      free(data.data);
800  
801      return ret;
802  }
803  
804  /*
805   *
806   */
807  
808  krb5_error_code
809  _krb5_pfs_rd_rep(krb5_context context, krb5_auth_context auth_context, AP_REP *ap_rep)
810  {
811      krb5_error_code ret;
812  
813      heim_assert(auth_context->pfs, "no pfs");
814      heim_assert(ap_rep->pfs, "no pfs from server");
815  
816      ret = _validate_rd_rep_checksum(context, auth_context->keyblock, ap_rep->pfs);
817      if (ret)
818  	goto out;
819  
820      ret = pfs_make_share_secret(context, auth_context,
821  				auth_context->keyblock->keytype,
822  				&ap_rep->pfs->selection);
823      if (ret)
824  	goto out;
825  
826      _krb5_debugx(context, 10, "PFS client made secret");
827      _krb5_debugx(context, 20, "PFS deriving new keys on client");
828  
829      ret = _krb5_pfs_update_key(context, auth_context, "session key", auth_context->keyblock);
830      if (ret)
831  	goto out;
832  
833      if (auth_context->local_subkey) {
834  	ret = _krb5_pfs_update_key(context, auth_context, "client key", auth_context->local_subkey);
835  	if (ret)
836  	    goto out;
837      }
838  
839      heim_assert(auth_context->pfs->group != KRB5_PFS_INVALID, "no pfs group selected");
840      auth_context->flags |= KRB5_AUTH_CONTEXT_USED_PFS;
841   out:
842      if (ret) {
843  	_krb5_auth_con_free_pfs(context, auth_context);
844      }
845  
846      return ret;
847  }