/ lib / krb5 / pkinit.c
pkinit.c
   1  /*
   2   * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
   3   * (Royal Institute of Technology, Stockholm, Sweden).
   4   * All rights reserved.
   5   *
   6   * Portions Copyright (c) 2009 - 2010 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  #include <syslog.h>
  38  
  39  struct krb5_dh_moduli {
  40      char *name;
  41      unsigned long bits;
  42      heim_integer p;
  43      heim_integer g;
  44      heim_integer q;
  45  };
  46  
  47  #ifdef PKINIT
  48  
  49  #include <cms_asn1.h>
  50  #include <pkcs8_asn1.h>
  51  #include <pkcs9_asn1.h>
  52  #include <pkcs12_asn1.h>
  53  #include <pkinit_asn1.h>
  54  #include <asn1_err.h>
  55  
  56  #include <der.h>
  57  
  58  #undef HEIMDAL_PRINTF_ATTRIBUTE
  59  #define HEIMDAL_PRINTF_ATTRIBUTE(x)
  60  #undef HEIMDAL_NORETURN_ATTRIBUTE
  61  #define HEIMDAL_NORETURN_ATTRIBUTE
  62  
  63  struct krb5_pk_init_ctx_data {
  64      struct krb5_pk_identity *id;
  65      enum { USE_RSA, USE_DH, USE_ECDH } keyex;
  66      union {
  67  	DH *dh;
  68  #ifdef HAVE_OPENSSL
  69  	EC_KEY *eckey;
  70  #endif
  71      } u;
  72      krb5_data *clientDHNonce;
  73      struct krb5_dh_moduli **m;
  74      hx509_peer_info peer;
  75      enum krb5_pk_type type;
  76      unsigned int require_binding:1;
  77      unsigned int require_eku:1;
  78      unsigned int require_krbtgt_otherName:1;
  79      unsigned int require_hostname_match:1;
  80      unsigned int trustedCertifiers:1;
  81      unsigned int anonymous:1;
  82  };
  83  
  84  /*
  85   *
  86   */
  87  
  88  static krb5_error_code
  89  BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
  90  {
  91      integer->length = BN_num_bytes(bn);
  92      integer->data = malloc(integer->length);
  93      if (integer->data == NULL) {
  94  	krb5_clear_error_message(context);
  95  	return ENOMEM;
  96      }
  97      BN_bn2bin(bn, integer->data);
  98      integer->negative = BN_is_negative(bn);
  99      return 0;
 100  }
 101  
 102  static BIGNUM *
 103  integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
 104  {
 105      BIGNUM *bn;
 106  
 107      bn = BN_bin2bn((const unsigned char *)f->data, (int)f->length, NULL);
 108      if (bn == NULL) {
 109  	krb5_set_error_message(context, ENOMEM,
 110  			       N_("PKINIT: parsing BN failed %s", ""), field);
 111  	return NULL;
 112      }
 113      BN_set_negative(bn, f->negative);
 114      return bn;
 115  }
 116  
 117  static krb5_error_code
 118  select_dh_group(krb5_context context, DH *dh, unsigned long bits,
 119  		struct krb5_dh_moduli **moduli)
 120  {
 121      const struct krb5_dh_moduli *m;
 122  
 123      if (bits == 0) {
 124  	m = moduli[0];
 125      } else {
 126  	int i;
 127  	for (i = 0; moduli[i] != NULL; i++) {
 128  	    if (bits < moduli[i]->bits)
 129  		break;
 130  	}
 131  	if (moduli[i] == NULL) {
 132  	    krb5_set_error_message(context, EINVAL,
 133  				   N_("Did not find a DH group parameter "
 134  				      "matching requirement of %lu bits", ""),
 135  				   bits);
 136  	    return EINVAL;
 137  	}
 138  	m = moduli[i];
 139      }
 140  
 141      dh->p = integer_to_BN(context, "p", &m->p);
 142      if (dh->p == NULL)
 143  	return ENOMEM;
 144      dh->g = integer_to_BN(context, "g", &m->g);
 145      if (dh->g == NULL)
 146  	return ENOMEM;
 147      dh->q = integer_to_BN(context, "q", &m->q);
 148      if (dh->q == NULL)
 149  	return ENOMEM;
 150  
 151      return 0;
 152  }
 153  
 154  struct certfind {
 155      const char *type;
 156      const heim_oid *oid;
 157  };
 158  
 159  /*
 160   * Try searchin the key by to use by first looking for for PK-INIT
 161   * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
 162   */
 163  
 164  krb5_error_code
 165  _krb5_pk_find_cert(krb5_context context, int mme, struct hx509_certs_data *certs,
 166  		   struct hx509_query_data *q, struct hx509_cert_data **cert)
 167  {
 168      struct certfind cf[4] = {
 169  	{ "MobileMe EKU" },
 170  	{ "PKINIT EKU" },
 171  	{ "MS EKU" },
 172  	{ "any (or no)" }
 173      };
 174      int ret = HX509_CERT_NOT_FOUND;
 175      size_t i, start = 1;
 176      unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
 177      const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
 178  
 179      if (mme)
 180  	start = 0;
 181  
 182      cf[0].oid = &mobileMe;
 183      cf[1].oid = &asn1_oid_id_pkekuoid;
 184      cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
 185      cf[3].oid = NULL;
 186  
 187      for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
 188  	ret = hx509_query_match_eku(q, cf[i].oid);
 189  	if (ret) {
 190  	    _krb5_pk_copy_error(context, ret,
 191  				"Failed setting %s OID", cf[i].type);
 192  	    return ret;
 193  	}
 194  
 195  	ret = hx509_certs_find(context->hx509ctx, certs, q, cert);
 196  	if (ret == 0)
 197  	    break;
 198  	_krb5_pk_copy_error(context, ret,
 199  			    "Failed finding certificate with %s OID", cf[i].type);
 200      }
 201      return ret;
 202  }
 203  
 204  
 205  static krb5_error_code
 206  create_signature(krb5_context context,
 207  		 const heim_oid *eContentType,
 208  		 krb5_data *eContent,
 209  		 struct krb5_pk_identity *id,
 210  		 hx509_peer_info peer,
 211  		 krb5_data *sd_data)
 212  {
 213      int ret, flags = 0;
 214  
 215      if (id->cert == NULL)
 216  	flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
 217  
 218      ret = hx509_cms_create_signed_1(context->hx509ctx,
 219  				    flags,
 220  				    eContentType,
 221  				    eContent->data,
 222  				    eContent->length,
 223  				    NULL,
 224  				    id->cert,
 225  				    peer,
 226  				    NULL,
 227  				    id->certpool,
 228  				    sd_data);
 229      if (ret) {
 230  	_krb5_pk_copy_error(context, ret,
 231  			    "Create CMS signedData");
 232  	return ret;
 233      }
 234  
 235      return 0;
 236  }
 237  
 238  static int
 239  cert2epi(hx509_context context, void *ctx, hx509_cert c)
 240  {
 241      ExternalPrincipalIdentifiers *ids = ctx;
 242      ExternalPrincipalIdentifier id;
 243      hx509_name subject = NULL;
 244      void *p;
 245      int ret;
 246  
 247      if (ids->len > 10)
 248  	return 0;
 249  
 250      memset(&id, 0, sizeof(id));
 251  
 252      ret = hx509_cert_get_subject(c, &subject);
 253      if (ret)
 254  	return ret;
 255  
 256      if (hx509_name_is_null_p(subject) != 0) {
 257  
 258  	id.subjectName = calloc(1, sizeof(*id.subjectName));
 259  	if (id.subjectName == NULL) {
 260  	    hx509_name_free(&subject);
 261  	    free_ExternalPrincipalIdentifier(&id);
 262  	    return ENOMEM;
 263  	}
 264  
 265  	ret = hx509_name_binary(subject, id.subjectName);
 266  	if (ret) {
 267  	    hx509_name_free(&subject);
 268  	    free_ExternalPrincipalIdentifier(&id);
 269  	    return ret;
 270  	}
 271      }
 272      hx509_name_free(&subject);
 273  
 274  
 275      id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
 276      if (id.issuerAndSerialNumber == NULL) {
 277  	free_ExternalPrincipalIdentifier(&id);
 278  	return ENOMEM;
 279      }
 280  
 281      {
 282  	IssuerAndSerialNumber iasn;
 283  	hx509_name issuer;
 284  	size_t size = 0;
 285  
 286  	memset(&iasn, 0, sizeof(iasn));
 287  
 288  	ret = hx509_cert_get_issuer(c, &issuer);
 289  	if (ret) {
 290  	    free_ExternalPrincipalIdentifier(&id);
 291  	    return ret;
 292  	}
 293  
 294  	ret = hx509_name_to_Name(issuer, &iasn.issuer);
 295  	hx509_name_free(&issuer);
 296  	if (ret) {
 297  	    free_ExternalPrincipalIdentifier(&id);
 298  	    return ret;
 299  	}
 300  
 301  	ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
 302  	if (ret) {
 303  	    free_IssuerAndSerialNumber(&iasn);
 304  	    free_ExternalPrincipalIdentifier(&id);
 305  	    return ret;
 306  	}
 307  
 308  	ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
 309  			   id.issuerAndSerialNumber->data,
 310  			   id.issuerAndSerialNumber->length,
 311  			   &iasn, &size, ret);
 312  	free_IssuerAndSerialNumber(&iasn);
 313  	if (ret) {
 314  	    free_ExternalPrincipalIdentifier(&id);
 315  	    return ret;
 316  	}
 317  	if (id.issuerAndSerialNumber->length != size)
 318  	    abort();
 319      }
 320  
 321      id.subjectKeyIdentifier = NULL;
 322  
 323      p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
 324      if (p == NULL) {
 325  	free_ExternalPrincipalIdentifier(&id);
 326  	return ENOMEM;
 327      }
 328  
 329      ids->val = p;
 330      ids->val[ids->len] = id;
 331      ids->len++;
 332  
 333      return 0;
 334  }
 335  
 336  static krb5_error_code
 337  build_edi(krb5_context context,
 338  	  hx509_context hx509ctx,
 339  	  hx509_certs certs,
 340  	  ExternalPrincipalIdentifiers *ids)
 341  {
 342      return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);
 343  }
 344  
 345  static krb5_error_code
 346  build_auth_pack(krb5_context context,
 347  		unsigned nonce,
 348  		krb5_pk_init_ctx ctx,
 349  		const KDC_REQ_BODY *body,
 350  		AuthPack *a)
 351  {
 352      size_t buf_size, len = 0;
 353      krb5_error_code ret;
 354      void *buf;
 355      krb5_timestamp sec;
 356      int32_t usec;
 357      Checksum checksum;
 358  
 359      krb5_clear_error_message(context);
 360  
 361      memset(&checksum, 0, sizeof(checksum));
 362  
 363      krb5_us_timeofday(context, &sec, &usec);
 364      a->pkAuthenticator.ctime = sec;
 365      a->pkAuthenticator.nonce = nonce;
 366  
 367      ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
 368      if (ret)
 369  	return ret;
 370      if (buf_size != len)
 371  	krb5_abortx(context, "internal error in ASN.1 encoder");
 372  
 373      ret = krb5_create_checksum(context,
 374  			       NULL,
 375  			       0,
 376  			       CKSUMTYPE_SHA1,
 377  			       buf,
 378  			       len,
 379  			       &checksum);
 380      free(buf);
 381      if (ret)
 382  	return ret;
 383  
 384      ALLOC(a->pkAuthenticator.paChecksum, 1);
 385      if (a->pkAuthenticator.paChecksum == NULL) {
 386  	krb5_set_error_message(context, ENOMEM,
 387  			       N_("malloc: out of memory", ""));
 388  	return ENOMEM;
 389      }
 390  
 391      ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
 392  			 checksum.checksum.data, checksum.checksum.length);
 393      free_Checksum(&checksum);
 394      if (ret)
 395  	return ret;
 396  
 397      if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
 398  	const char *moduli_file;
 399  	unsigned long dh_min_bits;
 400  	krb5_data dhbuf;
 401  	size_t size = 0;
 402  
 403  	krb5_data_zero(&dhbuf);
 404  
 405  
 406  
 407  	moduli_file = krb5_config_get_string(context, NULL,
 408  					     "libdefaults",
 409  					     "moduli",
 410  					     NULL);
 411  
 412  	dh_min_bits =
 413  	    krb5_config_get_int_default(context, NULL, 0,
 414  					"libdefaults",
 415  					"pkinit_dh_min_bits",
 416  					NULL);
 417  
 418  	ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
 419  	if (ret)
 420  	    return ret;
 421  
 422  	ctx->u.dh = DH_new();
 423  	if (ctx->u.dh == NULL) {
 424  	    krb5_set_error_message(context, ENOMEM,
 425  				   N_("malloc: out of memory", ""));
 426  	    return ENOMEM;
 427  	}
 428  
 429  	ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
 430  	if (ret)
 431  	    return ret;
 432  
 433  	if (DH_generate_key(ctx->u.dh) != 1) {
 434  	    krb5_set_error_message(context, ENOMEM,
 435  				   N_("pkinit: failed to generate DH key", ""));
 436  	    return ENOMEM;
 437  	}
 438  
 439  
 440  	if (1 /* support_cached_dh */) {
 441  	    ALLOC(a->clientDHNonce, 1);
 442  	    if (a->clientDHNonce == NULL) {
 443  		krb5_clear_error_message(context);
 444  		return ENOMEM;
 445  	    }
 446  	    ret = krb5_data_alloc(a->clientDHNonce, 40);
 447  	    if (a->clientDHNonce == NULL) {
 448  		krb5_clear_error_message(context);
 449  		return ret;
 450  	    }
 451  	    krb5_generate_random_block(a->clientDHNonce->data,
 452  				       a->clientDHNonce->length);
 453  	    ret = krb5_copy_data(context, a->clientDHNonce,
 454  				 &ctx->clientDHNonce);
 455  	    if (ret)
 456  		return ret;
 457  	}
 458  
 459  	ALLOC(a->clientPublicValue, 1);
 460  	if (a->clientPublicValue == NULL)
 461  	    return ENOMEM;
 462  
 463  	if (ctx->keyex == USE_DH) {
 464  	    DH *dh = ctx->u.dh;
 465  	    DomainParameters dp;
 466  	    heim_integer dh_pub_key;
 467  
 468  	    ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
 469  			       &a->clientPublicValue->algorithm.algorithm);
 470  	    if (ret)
 471  		return ret;
 472  
 473  	    memset(&dp, 0, sizeof(dp));
 474  
 475  	    ret = BN_to_integer(context, dh->p, &dp.p);
 476  	    if (ret) {
 477  		free_DomainParameters(&dp);
 478  		return ret;
 479  	    }
 480  	    ret = BN_to_integer(context, dh->g, &dp.g);
 481  	    if (ret) {
 482  		free_DomainParameters(&dp);
 483  		return ret;
 484  	    }
 485  	    ret = BN_to_integer(context, dh->q, &dp.q);
 486  	    if (ret) {
 487  		free_DomainParameters(&dp);
 488  		return ret;
 489  	    }
 490  	    dp.j = NULL;
 491  	    dp.validationParms = NULL;
 492  
 493  	    a->clientPublicValue->algorithm.parameters =
 494  		malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
 495  	    if (a->clientPublicValue->algorithm.parameters == NULL) {
 496  		free_DomainParameters(&dp);
 497  		return ret;
 498  	    }
 499  
 500  	    ASN1_MALLOC_ENCODE(DomainParameters,
 501  			       a->clientPublicValue->algorithm.parameters->data,
 502  			       a->clientPublicValue->algorithm.parameters->length,
 503  			       &dp, &size, ret);
 504  	    free_DomainParameters(&dp);
 505  	    if (ret)
 506  		return ret;
 507  	    if (size != a->clientPublicValue->algorithm.parameters->length)
 508  		krb5_abortx(context, "Internal ASN1 encoder error");
 509  
 510  	    ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
 511  	    if (ret)
 512  		return ret;
 513  
 514  	    ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
 515  			       &dh_pub_key, &size, ret);
 516  	    der_free_heim_integer(&dh_pub_key);
 517  	    if (ret)
 518  		return ret;
 519  	    if (size != dhbuf.length)
 520  		krb5_abortx(context, "asn1 internal error");
 521  	} else if (ctx->keyex == USE_ECDH) {
 522  #ifdef HAVE_OPENSSL
 523  	    ECParameters ecp;
 524  	    unsigned char *p;
 525  	    int xlen;
 526  
 527  	    /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
 528  
 529  	    ecp.element = choice_ECParameters_namedCurve;
 530  	    ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
 531  			       &ecp.u.namedCurve);
 532  	    if (ret)
 533  		return ret;
 534  
 535  	    ALLOC(a->clientPublicValue->algorithm.parameters, 1);
 536  	    if (a->clientPublicValue->algorithm.parameters == NULL) {
 537  		free_ECParameters(&ecp);
 538  		return ENOMEM;
 539  	    }
 540  	    ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret);
 541  	    free_ECParameters(&ecp);
 542  	    if (ret)
 543  		return ret;
 544  	    if ((int)size != xlen)
 545  		krb5_abortx(context, "asn1 internal error");
 546  
 547  	    a->clientPublicValue->algorithm.parameters->data = p;
 548  	    a->clientPublicValue->algorithm.parameters->length = size;
 549  
 550  	    /* copy in public key */
 551  
 552  	    ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
 553  			       &a->clientPublicValue->algorithm.algorithm);
 554  	    if (ret)
 555  		return ret;
 556  
 557  	    ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
 558  	    if (ctx->u.eckey == NULL)
 559  		return ENOMEM;
 560  
 561  	    ret = EC_KEY_generate_key(ctx->u.eckey);
 562  	    if (ret != 1)
 563  		return EINVAL;
 564  
 565  	    /* encode onto dhkey */
 566  
 567  	    xlen = i2o_ECPublicKey(ctx->u.eckey, NULL);
 568  	    if (xlen <= 0)
 569  		abort();
 570  
 571  	    dhbuf.data = malloc(xlen);
 572  	    if (dhbuf.data == NULL)
 573  		abort();
 574  	    dhbuf.length = xlen;
 575  	    p = dhbuf.data;
 576  
 577  	    xlen = i2o_ECPublicKey(ctx->u.eckey, &p);
 578  	    if (xlen <= 0)
 579  		abort();
 580  
 581  	    /* XXX verify that this is right with RFC3279 */
 582  #else
 583  	    return EINVAL;
 584  #endif
 585  	} else
 586  	    krb5_abortx(context, "internal error");
 587  	a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
 588  	a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
 589      }
 590  
 591      {
 592  	a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
 593  	if (a->supportedCMSTypes == NULL)
 594  	    return ENOMEM;
 595  
 596  	ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
 597  				     ctx->id->cert,
 598  				     &a->supportedCMSTypes->val,
 599  				     &a->supportedCMSTypes->len);
 600  	if (ret)
 601  	    return ret;
 602      }
 603  
 604      return ret;
 605  }
 606  
 607  KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 608  _krb5_pk_mk_ContentInfo(krb5_context context,
 609  			const krb5_data *buf,
 610  			const heim_oid *oid,
 611  			struct ContentInfo *content_info)
 612  {
 613      krb5_error_code ret;
 614  
 615      ret = der_copy_oid(oid, &content_info->contentType);
 616      if (ret)
 617  	return ret;
 618      ALLOC(content_info->content, 1);
 619      if (content_info->content == NULL)
 620  	return ENOMEM;
 621      content_info->content->data = malloc(buf->length);
 622      if (content_info->content->data == NULL)
 623  	return ENOMEM;
 624      memcpy(content_info->content->data, buf->data, buf->length);
 625      content_info->content->length = buf->length;
 626      return 0;
 627  }
 628  
 629  static krb5_error_code
 630  pk_mk_padata(krb5_context context,
 631  	     krb5_pk_init_ctx ctx,
 632  	     const KDC_REQ_BODY *req_body,
 633  	     unsigned nonce,
 634  	     METHOD_DATA *md)
 635  {
 636      struct ContentInfo content_info;
 637      krb5_error_code ret;
 638      const heim_oid *oid = NULL;
 639      size_t size = 0;
 640      krb5_data buf, sd_buf;
 641      int pa_type = -1;
 642  
 643      krb5_data_zero(&buf);
 644      krb5_data_zero(&sd_buf);
 645      memset(&content_info, 0, sizeof(content_info));
 646  
 647      if (ctx->type == PKINIT_WIN2K) {
 648  	AuthPack_Win2k ap;
 649  	krb5_timestamp sec;
 650  	int32_t usec;
 651  
 652  	memset(&ap, 0, sizeof(ap));
 653  
 654  	/* fill in PKAuthenticator */
 655  	ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
 656  	if (ret) {
 657  	    free_AuthPack_Win2k(&ap);
 658  	    krb5_clear_error_message(context);
 659  	    goto out;
 660  	}
 661  	ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
 662  	if (ret) {
 663  	    free_AuthPack_Win2k(&ap);
 664  	    krb5_clear_error_message(context);
 665  	    goto out;
 666  	}
 667  
 668  	krb5_us_timeofday(context, &sec, &usec);
 669  	ap.pkAuthenticator.ctime = sec;
 670  	ap.pkAuthenticator.cusec = usec;
 671  	ap.pkAuthenticator.nonce = nonce;
 672  
 673  	ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
 674  			   &ap, &size, ret);
 675  	free_AuthPack_Win2k(&ap);
 676  	if (ret) {
 677  	    krb5_set_error_message(context, ret,
 678  				   N_("Failed encoding AuthPackWin: %d", ""),
 679  				   (int)ret);
 680  	    goto out;
 681  	}
 682  	if (buf.length != size)
 683  	    krb5_abortx(context, "internal ASN1 encoder error");
 684  
 685  	oid = &asn1_oid_id_pkcs7_data;
 686      } else if (ctx->type == PKINIT_27) {
 687  	AuthPack ap;
 688  
 689  	memset(&ap, 0, sizeof(ap));
 690  
 691  	ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
 692  	if (ret) {
 693  	    free_AuthPack(&ap);
 694  	    goto out;
 695  	}
 696  
 697  	ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
 698  	free_AuthPack(&ap);
 699  	if (ret) {
 700  	    krb5_set_error_message(context, ret,
 701  				   N_("Failed encoding AuthPack: %d", ""),
 702  				   (int)ret);
 703  	    goto out;
 704  	}
 705  	if (buf.length != size)
 706  	    krb5_abortx(context, "internal ASN1 encoder error");
 707  
 708  	oid = &asn1_oid_id_pkauthdata;
 709      } else
 710  	krb5_abortx(context, "internal pkinit error");
 711  
 712      ret = create_signature(context, oid, &buf, ctx->id,
 713  			   ctx->peer, &sd_buf);
 714      krb5_data_free(&buf);
 715      if (ret)
 716  	goto out;
 717  
 718      ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
 719      krb5_data_free(&sd_buf);
 720      if (ret) {
 721  	krb5_set_error_message(context, ret,
 722  			       N_("ContentInfo wrapping of signedData failed",""));
 723  	goto out;
 724      }
 725  
 726      if (ctx->type == PKINIT_WIN2K) {
 727  	PA_PK_AS_REQ_Win2k winreq;
 728  
 729  	pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
 730  
 731  	memset(&winreq, 0, sizeof(winreq));
 732  
 733  	winreq.signed_auth_pack = buf;
 734  
 735  	ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
 736  			   &winreq, &size, ret);
 737  	free_PA_PK_AS_REQ_Win2k(&winreq);
 738  
 739      } else if (ctx->type == PKINIT_27) {
 740  	PA_PK_AS_REQ req;
 741  
 742  	pa_type = KRB5_PADATA_PK_AS_REQ;
 743  
 744  	memset(&req, 0, sizeof(req));
 745  	req.signedAuthPack = buf;
 746  
 747  	if (ctx->trustedCertifiers) {
 748  
 749  	    req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
 750  	    if (req.trustedCertifiers == NULL) {
 751  		ret = ENOMEM;
 752  		krb5_set_error_message(context, ret,
 753  				       N_("malloc: out of memory", ""));
 754  		free_PA_PK_AS_REQ(&req);
 755  		goto out;
 756  	    }
 757  	    ret = build_edi(context, context->hx509ctx,
 758  			    ctx->id->anchors, req.trustedCertifiers);
 759  	    if (ret) {
 760  		krb5_set_error_message(context, ret,
 761  				       N_("pk-init: failed to build "
 762  					  "trustedCertifiers", ""));
 763  		free_PA_PK_AS_REQ(&req);
 764  		goto out;
 765  	    }
 766  	}
 767  	req.kdcPkId = NULL;
 768  
 769  	ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
 770  			   &req, &size, ret);
 771  
 772  	free_PA_PK_AS_REQ(&req);
 773  
 774      } else
 775  	krb5_abortx(context, "internal pkinit error");
 776      if (ret) {
 777  	krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
 778  	goto out;
 779      }
 780      if (buf.length != size)
 781  	krb5_abortx(context, "Internal ASN1 encoder error");
 782  
 783      ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
 784      if (ret)
 785  	free(buf.data);
 786  
 787      if (ret == 0)
 788      	krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
 789  
 790   out:
 791      free_ContentInfo(&content_info);
 792  
 793      return ret;
 794  }
 795  
 796  
 797  KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 798  _krb5_pk_mk_padata(krb5_context context,
 799  		   void *c,
 800  		   int ic_flags,
 801  		   int win2k,
 802  		   const KDC_REQ_BODY *req_body,
 803  		   unsigned nonce,
 804  		   METHOD_DATA *md)
 805  {
 806      krb5_pk_init_ctx ctx = c;
 807      int win2k_compat;
 808  
 809      if (ctx->id->cert == NULL && ctx->anonymous == 0) {
 810  	krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
 811  			       N_("PKINIT: No user certificate given", ""));
 812  	return HEIM_PKINIT_NO_PRIVATE_KEY;
 813      }
 814  
 815      win2k_compat = krb5_config_get_bool_default(context, NULL,
 816  						win2k,
 817  						"realms",
 818  						req_body->realm,
 819  						"pkinit_win2k",
 820  						NULL);
 821  
 822      if (win2k_compat) {
 823  	ctx->require_binding =
 824  	    krb5_config_get_bool_default(context, NULL,
 825  					 TRUE,
 826  					 "realms",
 827  					 req_body->realm,
 828  					 "pkinit_win2k_require_binding",
 829  					 NULL);
 830  	ctx->type = PKINIT_WIN2K;
 831      } else
 832  	ctx->type = PKINIT_27;
 833  
 834      ctx->require_eku =
 835  	krb5_config_get_bool_default(context, NULL,
 836  				     TRUE,
 837  				     "realms",
 838  				     req_body->realm,
 839  				     "pkinit_require_eku",
 840  				     NULL);
 841  
 842      ctx->require_krbtgt_otherName =
 843  	krb5_config_get_bool_default(context, NULL,
 844  				     TRUE,
 845  				     "realms",
 846  				     req_body->realm,
 847  				     "pkinit_require_krbtgt_otherName",
 848  				     NULL);
 849  
 850      ctx->require_hostname_match =
 851  	krb5_config_get_bool_default(context, NULL,
 852  				     FALSE,
 853  				     "realms",
 854  				     req_body->realm,
 855  				     "pkinit_require_hostname_match",
 856  				     NULL);
 857  
 858      ctx->trustedCertifiers =
 859  	krb5_config_get_bool_default(context, NULL,
 860  				     TRUE,
 861  				     "realms",
 862  				     req_body->realm,
 863  				     "pkinit_trustedCertifiers",
 864  				     NULL);
 865  
 866      if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
 867  	ctx->require_eku = 0;
 868      if (ic_flags & KRB5_INIT_CREDS_PKINIT_NO_KRBTGT_OTHERNAME_CHECK)
 869  	ctx->require_krbtgt_otherName = FALSE;
 870      if (ctx->id->flags & PKINIT_BTMM) {
 871  	ctx->require_eku = 0;
 872  	ctx->require_krbtgt_otherName = FALSE;
 873  	ctx->trustedCertifiers = FALSE;
 874      }
 875  
 876      return pk_mk_padata(context, ctx, req_body, nonce, md);
 877  }
 878  
 879  static krb5_error_code
 880  pk_verify_sign(krb5_context context,
 881  	       const void *data,
 882  	       size_t length,
 883  	       struct krb5_pk_identity *id,
 884  	       heim_oid *contentType,
 885  	       krb5_data *content,
 886  	       struct hx509_cert_data **signer)
 887  {
 888      heim_array_t signer_evaluate = NULL;
 889      hx509_evaluate eval = NULL;
 890      int ret, flags = 0;
 891      size_t len;
 892  
 893      /* BTMM is broken in Leo and SnowLeo */
 894      if (id->flags & PKINIT_BTMM) {
 895  	flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
 896  	flags |= HX509_CMS_VS_NO_KU_CHECK;
 897  	flags |= HX509_CMS_VS_NO_VALIDATE;
 898      }
 899  
 900      *signer = NULL;
 901  
 902      ret = hx509_cms_verify_signed(context->hx509ctx,
 903  				  id->verify_ctx,
 904  				  flags,
 905  				  data,
 906  				  length,
 907  				  NULL,
 908  				  id->certpool,
 909  				  contentType,
 910  				  content,
 911  				  &signer_evaluate);
 912      if (ret) {
 913  	_krb5_pk_copy_error(context, ret,
 914  			    "CMS verify signed failed");
 915  	return ret;
 916      }
 917  
 918      if (signer_evaluate) {
 919  	ret = KRB5_KDC_ERR_CANT_VERIFY_CERTIFICATE;
 920  
 921  	len = heim_array_get_length(signer_evaluate);
 922  	if (len == 0)
 923  	    goto out;
 924  
 925  	eval = heim_array_copy_value(signer_evaluate, 0);
 926  	if (eval == NULL)
 927  	    goto out;
 928  
 929  	len = hx509_evaluate_get_length(eval);
 930  	if (len == 0)
 931  	    goto out;
 932  
 933  	*signer = hx509_evaluate_get_cert(eval, 0);
 934  	if (*signer == NULL)
 935  	    goto out;
 936      }
 937      ret = 0;
 938  
 939   out:
 940      heim_release(signer_evaluate);
 941      heim_release(eval);
 942  
 943      return ret;
 944  }
 945  
 946  static krb5_error_code
 947  get_reply_key_win(krb5_context context,
 948  		  const krb5_data *content,
 949  		  unsigned nonce,
 950  		  krb5_keyblock **key)
 951  {
 952      ReplyKeyPack_Win2k key_pack;
 953      krb5_error_code ret;
 954      size_t size;
 955  
 956      ret = decode_ReplyKeyPack_Win2k(content->data,
 957  				    content->length,
 958  				    &key_pack,
 959  				    &size);
 960      if (ret) {
 961  	krb5_set_error_message(context, ret,
 962  			       N_("PKINIT decoding reply key failed", ""));
 963  	free_ReplyKeyPack_Win2k(&key_pack);
 964  	return ret;
 965      }
 966  
 967      if ((unsigned)key_pack.nonce != nonce) {
 968  	krb5_set_error_message(context, ret,
 969  			       N_("PKINIT enckey nonce is wrong", ""));
 970  	free_ReplyKeyPack_Win2k(&key_pack);
 971  	return KRB5KRB_AP_ERR_MODIFIED;
 972      }
 973  
 974      *key = malloc (sizeof (**key));
 975      if (*key == NULL) {
 976  	free_ReplyKeyPack_Win2k(&key_pack);
 977  	krb5_set_error_message(context, ENOMEM,
 978  			       N_("malloc: out of memory", ""));
 979  	return ENOMEM;
 980      }
 981  
 982      ret = copy_EncryptionKey(&key_pack.replyKey, *key);
 983      free_ReplyKeyPack_Win2k(&key_pack);
 984      if (ret) {
 985  	krb5_set_error_message(context, ret,
 986  			       N_("PKINIT failed copying reply key", ""));
 987  	free(*key);
 988  	*key = NULL;
 989      }
 990  
 991      return ret;
 992  }
 993  
 994  static krb5_error_code
 995  verify_req_checksum(krb5_context context,
 996  		    krb5_keyblock *key,
 997  		    Checksum *checksum,
 998  		    const krb5_data *req_buffer)
 999  {
1000      krb5_error_code ret;
1001      krb5_crypto crypto;
1002  
1003      ret = krb5_crypto_init(context, key, 0, &crypto);
1004      if (ret)
1005  	return ret;
1006  
1007      ret = krb5_verify_checksum(context, crypto, KRB5_KU_TGS_REQ_AUTH_CKSUM,
1008  			       req_buffer->data, req_buffer->length,
1009  			       checksum);
1010      krb5_crypto_destroy(context, crypto);
1011      return ret;
1012  }
1013  
1014  static krb5_error_code
1015  get_reply_key(krb5_context context,
1016  	      const krb5_data *content,
1017  	      const krb5_data *req_buffer,
1018  	      krb5_keyblock **key)
1019  {
1020      ReplyKeyPack key_pack;
1021      krb5_error_code ret;
1022      size_t size;
1023  
1024      ret = decode_ReplyKeyPack(content->data,
1025  			      content->length,
1026  			      &key_pack,
1027  			      &size);
1028      if (ret) {
1029  	krb5_set_error_message(context, ret,
1030  			       N_("PKINIT decoding reply key failed", ""));
1031  	free_ReplyKeyPack(&key_pack);
1032  	return ret;
1033      }
1034  
1035      {
1036  	ret = krb5_enctype_valid(context, key_pack.replyKey.keytype);
1037  	if (ret) {
1038  	    free_ReplyKeyPack(&key_pack);
1039  	    return ret;
1040  	}
1041  
1042  	ret = verify_req_checksum(context, &key_pack.replyKey,
1043  				  &key_pack.asChecksum, req_buffer);
1044  	if (ret &&
1045  	    key_pack.asChecksum.cksumtype == CKSUMTYPE_HMAC_SHA1_DES3 &&
1046  	    key_pack.replyKey.keytype == ETYPE_AES256_CTS_HMAC_SHA1_96 &&
1047  	    key_pack.asChecksum.checksum.length == 20)
1048  	{
1049  	    /*
1050  	     * So Mac OS X 10.6.0 to 10.6.x treats the checksum as a
1051  	     * DES3-CBC-SHA1 checksum because I misunderstood the MIT
1052  	     * Kerberos API, so lets try using the key as a DES3 key
1053  	     * too.
1054  	     */
1055  	    key_pack.asChecksum.cksumtype = CKSUMTYPE_HMAC_SHA1_96_AES_256;
1056  	    key_pack.asChecksum.checksum.length = 12;
1057  
1058  	    ret = verify_req_checksum(context, &key_pack.replyKey,
1059  				      &key_pack.asChecksum, req_buffer);
1060  	}
1061  	if (ret) {
1062  	    free_ReplyKeyPack(&key_pack);
1063  	    return ret;
1064  	}
1065      }
1066  
1067      *key = malloc (sizeof (**key));
1068      if (*key == NULL) {
1069  	free_ReplyKeyPack(&key_pack);
1070  	krb5_set_error_message(context, ENOMEM,
1071  			       N_("malloc: out of memory", ""));
1072  	return ENOMEM;
1073      }
1074  
1075      ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1076      free_ReplyKeyPack(&key_pack);
1077      if (ret) {
1078  	krb5_set_error_message(context, ret,
1079  			       N_("PKINIT failed copying reply key", ""));
1080  	free(*key);
1081  	*key = NULL;
1082      }
1083  
1084      return ret;
1085  }
1086  
1087  
1088  static krb5_error_code
1089  pk_verify_host(krb5_context context,
1090  	       const char *realm,
1091  	       const krb5_krbhst_info *hi,
1092  	       struct krb5_pk_init_ctx_data *ctx,
1093  	       struct hx509_cert_data *host)
1094  {
1095      krb5_error_code ret = 0;
1096  
1097      if (ctx->require_eku) {
1098  	ret = hx509_cert_check_eku(context->hx509ctx, host,
1099  				   &asn1_oid_id_pkkdcekuoid, 0);
1100  	if (ret) {
1101  	    krb5_set_error_message(context, ret,
1102  				   N_("No PK-INIT KDC EKU in kdc certificate", ""));
1103  	    return ret;
1104  	}
1105      }
1106      if (ctx->require_krbtgt_otherName) {
1107  	krb5_principal principal;
1108  
1109  	ret = krb5_make_principal(context, &principal, realm,
1110  				  KRB5_TGS_NAME, realm, NULL);
1111  
1112  	if (ret)
1113  	    return ret;
1114  
1115  	ret = _krb5_pk_match_cert(context, principal, host, 1);
1116  	krb5_free_principal(context, principal);
1117  	if (ret) {
1118  	    ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1119  	    krb5_set_error_message(context, ret,
1120  				   N_("KDC have wrong realm name in "
1121  				      "the certificate", ""));
1122  	    return ret;
1123  	}
1124      }
1125  
1126      if (hi) {
1127  	ret = hx509_verify_hostname(context->hx509ctx, host,
1128  				    ctx->require_hostname_match,
1129  				    HX509_HN_HOSTNAME,
1130  				    hi->hostname,
1131  				    hi->ai->ai_addr, hi->ai->ai_addrlen);
1132  
1133  	if (ret)
1134  	    krb5_set_error_message(context, ret,
1135  				   N_("Address mismatch in "
1136  				      "the KDC certificate", ""));
1137      }
1138      return ret;
1139  }
1140  
1141  static krb5_error_code
1142  pk_rd_pa_reply_enckey(krb5_context context,
1143  		      int type,
1144  		      const heim_octet_string *indata,
1145  		      const heim_oid *dataType,
1146  		      const char *realm,
1147  		      krb5_pk_init_ctx ctx,
1148  		      krb5_enctype etype,
1149  		      const krb5_krbhst_info *hi,
1150  	       	      unsigned nonce,
1151  		      const krb5_data *req_buffer,
1152  	       	      PA_DATA *pa,
1153  	       	      krb5_keyblock **key)
1154  {
1155      krb5_error_code ret;
1156      struct hx509_cert_data *host = NULL;
1157      krb5_data content;
1158      heim_oid contentType = { 0, NULL };
1159      int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1160  
1161      if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1162  	krb5_set_error_message(context, EINVAL,
1163  			       N_("PKINIT: Invalid content type", ""));
1164  	return EINVAL;
1165      }
1166  
1167      if (ctx->type == PKINIT_WIN2K)
1168  	flags |= HX509_CMS_UE_ALLOW_WEAK;
1169  
1170      ret = hx509_cms_unenvelope(context->hx509ctx,
1171  			       ctx->id->certs,
1172  			       flags,
1173  			       indata->data,
1174  			       indata->length,
1175  			       NULL,
1176  			       0,
1177  			       &contentType,
1178  			       &content);
1179      if (ret) {
1180  	_krb5_pk_copy_error(context, ret,
1181  			    "Failed to unenvelope CMS data in PK-INIT reply");
1182  	return ret;
1183      }
1184      der_free_oid(&contentType);
1185  
1186      /* win2k uses ContentInfo */
1187      if (type == PKINIT_WIN2K) {
1188  	heim_oid type2;
1189  	heim_octet_string out;
1190  
1191  	ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1192  	if (ret) {
1193  	    /* windows LH with interesting CMS packets */
1194  	    size_t ph = 1 + der_length_len(content.length);
1195  	    unsigned char *ptr = malloc(content.length + ph);
1196  	    size_t l;
1197  
1198  	    memcpy(ptr + ph, content.data, content.length);
1199  
1200  	    ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1201  					  ASN1_C_UNIV, CONS, UT_Sequence, &l);
1202  	    if (ret)
1203  		return ret;
1204  	    free(content.data);
1205  	    content.data = ptr;
1206  	    content.length += ph;
1207  
1208  	    ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1209  	    if (ret)
1210  		goto out;
1211  	}
1212  	if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1213  	    ret = EINVAL; /* XXX */
1214  	    krb5_set_error_message(context, ret,
1215  				   N_("PKINIT: Invalid content type", ""));
1216  	    der_free_oid(&type2);
1217  	    der_free_octet_string(&out);
1218  	    goto out;
1219  	}
1220  	der_free_oid(&type2);
1221  	krb5_data_free(&content);
1222  	ret = krb5_data_copy(&content, out.data, out.length);
1223  	der_free_octet_string(&out);
1224  	if (ret) {
1225  	    krb5_set_error_message(context, ret,
1226  				   N_("malloc: out of memory", ""));
1227  	    goto out;
1228  	}
1229      }
1230  
1231      ret = pk_verify_sign(context,
1232  			 content.data,
1233  			 content.length,
1234  			 ctx->id,
1235  			 &contentType,
1236  			 &content,
1237  			 &host);
1238      if (ret)
1239  	goto out;
1240  
1241      /* make sure that it is the kdc's certificate */
1242      ret = pk_verify_host(context, realm, hi, ctx, host);
1243      if (ret) {
1244  	goto out;
1245      }
1246  
1247  #if 0
1248      if (type == PKINIT_WIN2K) {
1249  	if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1250  	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1251  	    krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1252  	    goto out;
1253  	}
1254      } else {
1255  	if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1256  	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1257  	    krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1258  	    goto out;
1259  	}
1260      }
1261  #endif
1262  
1263      switch(type) {
1264      case PKINIT_WIN2K:
1265  	ret = get_reply_key(context, &content, req_buffer, key);
1266  	if (ret != 0 && ctx->require_binding == 0)
1267  	    ret = get_reply_key_win(context, &content, nonce, key);
1268  	break;
1269      case PKINIT_27:
1270  	ret = get_reply_key(context, &content, req_buffer, key);
1271  	break;
1272      }
1273      if (ret)
1274  	goto out;
1275  
1276      /* XXX compare given etype with key->etype */
1277  
1278   out:
1279      hx509_cert_free(host);
1280      der_free_oid(&contentType);
1281      krb5_data_free(&content);
1282  
1283      return ret;
1284  }
1285  
1286  static krb5_error_code
1287  pk_rd_pa_reply_dh(krb5_context context,
1288  		  const heim_octet_string *indata,
1289  		  const heim_oid *dataType,
1290  		  const char *realm,
1291  		  krb5_pk_init_ctx ctx,
1292  		  krb5_enctype etype,
1293  		  const krb5_krbhst_info *hi,
1294  		  const DHNonce *c_n,
1295  		  const DHNonce *k_n,
1296                    unsigned nonce,
1297                    PA_DATA *pa,
1298                    krb5_keyblock **key)
1299  {
1300      const unsigned char *p;
1301      unsigned char *dh_gen_key = NULL;
1302      struct hx509_cert_data *host = NULL;
1303      BIGNUM *kdc_dh_pubkey = NULL;
1304      KDCDHKeyInfo kdc_dh_info;
1305      heim_oid contentType = { 0, NULL };
1306      krb5_data content;
1307      krb5_error_code ret;
1308      int dh_gen_keylen = 0;
1309      size_t size;
1310  
1311      krb5_data_zero(&content);
1312      memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1313  
1314      if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1315  	krb5_set_error_message(context, EINVAL,
1316  			       N_("PKINIT: Invalid content type", ""));
1317  	return EINVAL;
1318      }
1319  
1320      ret = pk_verify_sign(context,
1321  			 indata->data,
1322  			 indata->length,
1323  			 ctx->id,
1324  			 &contentType,
1325  			 &content,
1326  			 &host);
1327      if (ret)
1328  	goto out;
1329  
1330      /* make sure that it is the kdc's certificate */
1331      ret = pk_verify_host(context, realm, hi, ctx, host);
1332      if (ret)
1333  	goto out;
1334  
1335      if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1336  	ret = KRB5KRB_AP_ERR_MSG_TYPE;
1337  	krb5_set_error_message(context, ret,
1338  			       N_("pkinit - dh reply contains wrong oid", ""));
1339  	goto out;
1340      }
1341  
1342      ret = decode_KDCDHKeyInfo(content.data,
1343  			      content.length,
1344  			      &kdc_dh_info,
1345  			      &size);
1346  
1347      if (ret) {
1348  	krb5_set_error_message(context, ret,
1349  			       N_("pkinit - failed to decode "
1350  				  "KDC DH Key Info", ""));
1351  	goto out;
1352      }
1353  
1354      if (kdc_dh_info.nonce != nonce) {
1355  	ret = KRB5KRB_AP_ERR_MODIFIED;
1356  	krb5_set_error_message(context, ret,
1357  			       N_("PKINIT: DH nonce is wrong", ""));
1358  	goto out;
1359      }
1360  
1361      if (kdc_dh_info.dhKeyExpiration) {
1362  	if (k_n == NULL) {
1363  	    ret = KRB5KRB_ERR_GENERIC;
1364  	    krb5_set_error_message(context, ret,
1365  				   N_("pkinit; got key expiration "
1366  				      "without server nonce", ""));
1367  	    goto out;
1368  	}
1369  	if (c_n == NULL) {
1370  	    ret = KRB5KRB_ERR_GENERIC;
1371  	    krb5_set_error_message(context, ret,
1372  				   N_("pkinit; got DH reuse but no "
1373  				      "client nonce", ""));
1374  	    goto out;
1375  	}
1376      } else {
1377  	if (k_n) {
1378  	    ret = KRB5KRB_ERR_GENERIC;
1379  	    krb5_set_error_message(context, ret,
1380  				   N_("pkinit: got server nonce "
1381  				      "without key expiration", ""));
1382  	    goto out;
1383  	}
1384  	c_n = NULL;
1385      }
1386  
1387  
1388      p = kdc_dh_info.subjectPublicKey.data;
1389      size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1390  
1391      if (ctx->keyex == USE_DH) {
1392  	DHPublicKey k;
1393  	ret = decode_DHPublicKey(p, size, &k, NULL);
1394  	if (ret) {
1395  	    krb5_set_error_message(context, ret,
1396  				   N_("pkinit: can't decode "
1397  				      "without key expiration", ""));
1398  	    goto out;
1399  	}
1400  
1401  	kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1402  	free_DHPublicKey(&k);
1403  	if (kdc_dh_pubkey == NULL) {
1404  	    ret = ENOMEM;
1405  	    goto out;
1406  	}
1407  
1408  
1409  	size = DH_size(ctx->u.dh);
1410  
1411  	dh_gen_key = malloc(size);
1412  	if (dh_gen_key == NULL) {
1413  	    ret = ENOMEM;
1414  	    krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1415  	    goto out;
1416  	}
1417  
1418  	dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1419  	if (dh_gen_keylen <= 0 || (size_t)dh_gen_keylen < size / 2) {
1420  	    ret = KRB5KRB_ERR_GENERIC;
1421  	    dh_gen_keylen = 0;
1422  	    krb5_set_error_message(context, ret,
1423  				   N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1424  	    goto out;
1425  	}
1426  	if (dh_gen_keylen < (int)size) {
1427  	    size_t diff = size - dh_gen_keylen;
1428  	    memmove(dh_gen_key + diff, dh_gen_key, dh_gen_keylen);
1429  	    memset(dh_gen_key, 0, diff);
1430  	    dh_gen_keylen = (int)size;
1431  	}
1432  
1433      } else {
1434  #ifdef HAVE_OPENSSL
1435  	const EC_GROUP *group;
1436  	EC_KEY *public = NULL;
1437  
1438  	group = EC_KEY_get0_group(ctx->u.eckey);
1439  
1440  	public = EC_KEY_new();
1441  	if (public == NULL) {
1442  	    ret = ENOMEM;
1443  	    goto out;
1444  	}
1445  	if (EC_KEY_set_group(public, group) != 1) {
1446  	    EC_KEY_free(public);
1447  	    ret = ENOMEM;
1448  	    goto out;
1449  	}
1450  
1451  	if (o2i_ECPublicKey(&public, &p, size) == NULL) {
1452  	    EC_KEY_free(public);
1453  	    ret = KRB5KRB_ERR_GENERIC;
1454  	    krb5_set_error_message(context, ret,
1455  				   N_("PKINIT: Can't parse ECDH public key", ""));
1456  	    goto out;
1457  	}
1458  
1459  	size = (EC_GROUP_get_degree(group) + 7) / 8;
1460  	dh_gen_key = malloc(size);
1461  	if (dh_gen_key == NULL) {
1462  	    EC_KEY_free(public);
1463  	    ret = ENOMEM;
1464  	    krb5_set_error_message(context, ret,
1465  				   N_("malloc: out of memory", ""));
1466  	    goto out;
1467  	}
1468  	dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
1469  					 EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
1470  	EC_KEY_free(public);
1471  	if (dh_gen_keylen == -1) {
1472  	    ret = KRB5KRB_ERR_GENERIC;
1473  	    dh_gen_keylen = 0;
1474  	    krb5_set_error_message(context, ret,
1475  				   N_("PKINIT: Can't compute ECDH public key", ""));
1476  	    goto out;
1477  	}
1478  #else
1479  	ret = EINVAL;
1480  	goto out;
1481  #endif
1482      }
1483  
1484      if (dh_gen_keylen <= 0) {
1485  	ret = EINVAL;
1486  	krb5_set_error_message(context, ret,
1487  			       N_("PKINIT: resulting DH key <= 0", ""));
1488  	dh_gen_keylen = 0;
1489  	goto out;
1490      }
1491  
1492      *key = malloc (sizeof (**key));
1493      if (*key == NULL) {
1494  	ret = ENOMEM;
1495  	krb5_set_error_message(context, ret,
1496  			       N_("malloc: out of memory", ""));
1497  	goto out;
1498      }
1499  
1500      ret = _krb5_pk_octetstring2key(context,
1501  				   etype,
1502  				   dh_gen_key, dh_gen_keylen,
1503  				   c_n, k_n,
1504  				   *key);
1505      if (ret) {
1506  	krb5_set_error_message(context, ret,
1507  			       N_("PKINIT: can't create key from DH key", ""));
1508  	free(*key);
1509  	*key = NULL;
1510  	goto out;
1511      }
1512  
1513   out:
1514      if (kdc_dh_pubkey)
1515  	BN_free(kdc_dh_pubkey);
1516      if (dh_gen_key) {
1517  	memset(dh_gen_key, 0, dh_gen_keylen);
1518  	free(dh_gen_key);
1519      }
1520      hx509_cert_free(host);
1521      if (content.data)
1522  	krb5_data_free(&content);
1523      der_free_oid(&contentType);
1524      free_KDCDHKeyInfo(&kdc_dh_info);
1525  
1526      return ret;
1527  }
1528  
1529  KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1530  _krb5_pk_rd_pa_reply(krb5_context context,
1531  		     const char *realm,
1532  		     struct krb5_pk_init_ctx_data *ctx,
1533  		     krb5_enctype etype,
1534  		     const krb5_krbhst_info *hi,
1535  		     unsigned nonce,
1536  		     const krb5_data *req_buffer,
1537  		     PA_DATA *pa,
1538  		     krb5_keyblock **key)
1539  {
1540      krb5_error_code ret;
1541      size_t size;
1542  
1543      /* Check for IETF PK-INIT first */
1544      if (ctx->type == PKINIT_27) {
1545  	PA_PK_AS_REP rep;
1546  	heim_octet_string os, data;
1547  	heim_oid oid;
1548  
1549  	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1550  	    krb5_set_error_message(context, EINVAL,
1551  				   N_("PKINIT: wrong padata recv", ""));
1552  	    return EINVAL;
1553  	}
1554  
1555  	ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1556  				  pa->padata_value.length,
1557  				  &rep,
1558  				  &size);
1559  	if (ret) {
1560  	    krb5_set_error_message(context, ret,
1561  				   N_("Failed to decode pkinit AS rep", ""));
1562  	    return ret;
1563  	}
1564  
1565  	switch (rep.element) {
1566  	case choice_PA_PK_AS_REP_dhInfo:
1567  	    _krb5_debugx(context, 5, "krb5_get_init_creds: using pkinit dh");
1568  	    os = rep.u.dhInfo.dhSignedData;
1569  	    break;
1570  	case choice_PA_PK_AS_REP_encKeyPack:
1571  	    _krb5_debugx(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1572  	    os = rep.u.encKeyPack;
1573  	    break;
1574  	default: {
1575  	    PA_PK_AS_REP_BTMM btmm;
1576  	    free_PA_PK_AS_REP(&rep);
1577  	    memset(&rep, 0, sizeof(rep));
1578  	    
1579  	    _krb5_debugx(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1580  
1581  	    ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1582  					   pa->padata_value.length,
1583  					   &btmm,
1584  					   &size);
1585  	    if (ret) {
1586  		krb5_set_error_message(context, EINVAL,
1587  				       N_("PKINIT: -27 reply "
1588  					  "invalid content type", ""));
1589  		return EINVAL;
1590  	    }
1591  
1592  	    if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1593  		free_PA_PK_AS_REP_BTMM(&btmm);
1594  		ret = EINVAL;
1595  		krb5_set_error_message(context, ret,
1596  				       N_("DH mode not supported for BTMM mode", ""));
1597  		return ret;
1598  	    }
1599  
1600  	    /*
1601  	     * Transform to IETF style PK-INIT reply so that free works below
1602  	     */
1603  
1604  	    rep.element = choice_PA_PK_AS_REP_encKeyPack;
1605  	    rep.u.encKeyPack.data = btmm.encKeyPack->data;
1606  	    rep.u.encKeyPack.length = btmm.encKeyPack->length;
1607  	    btmm.encKeyPack->data = NULL;
1608  	    btmm.encKeyPack->length = 0;
1609  	    free_PA_PK_AS_REP_BTMM(&btmm);
1610  	    os = rep.u.encKeyPack;
1611  	}
1612  	}
1613  
1614  	ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1615  	if (ret) {
1616  	    free_PA_PK_AS_REP(&rep);
1617  	    krb5_set_error_message(context, ret,
1618  				   N_("PKINIT: failed to unwrap CI", ""));
1619  	    return ret;
1620  	}
1621  
1622  	switch (rep.element) {
1623  	case choice_PA_PK_AS_REP_dhInfo:
1624  	    ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1625  				    ctx->clientDHNonce,
1626  				    rep.u.dhInfo.serverDHNonce,
1627  				    nonce, pa, key);
1628  	    break;
1629  	case choice_PA_PK_AS_REP_encKeyPack:
1630  	    ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1631  					ctx, etype, hi, nonce, req_buffer, pa, key);
1632  	    break;
1633  	default:
1634  	    krb5_abortx(context, "pk-init as-rep case not possible to happen");
1635  	}
1636  	der_free_octet_string(&data);
1637  	der_free_oid(&oid);
1638  	free_PA_PK_AS_REP(&rep);
1639  
1640      } else if (ctx->type == PKINIT_WIN2K) {
1641  	PA_PK_AS_REP_Win2k w2krep;
1642  
1643  	/* Check for Windows encoding of the AS-REP pa data */
1644  
1645  #if 0 /* should this be ? */
1646  	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1647  	    krb5_set_error_message(context, EINVAL,
1648  				   "PKINIT: wrong padata recv");
1649  	    return EINVAL;
1650  	}
1651  #endif
1652  
1653  	memset(&w2krep, 0, sizeof(w2krep));
1654  
1655  	ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1656  					pa->padata_value.length,
1657  					&w2krep,
1658  					&size);
1659  	if (ret) {
1660  	    krb5_set_error_message(context, ret,
1661  				   N_("PKINIT: Failed decoding windows "
1662  				      "pkinit reply %d", ""), (int)ret);
1663  	    return ret;
1664  	}
1665  
1666  	krb5_clear_error_message(context);
1667  
1668  	switch (w2krep.element) {
1669  	case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1670  	    heim_octet_string data;
1671  	    heim_oid oid;
1672  
1673  	    ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1674  					       &oid, &data, NULL);
1675  	    free_PA_PK_AS_REP_Win2k(&w2krep);
1676  	    if (ret) {
1677  		krb5_set_error_message(context, ret,
1678  				       N_("PKINIT: failed to unwrap CI", ""));
1679  		return ret;
1680  	    }
1681  
1682  	    ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1683  					ctx, etype, hi, nonce, req_buffer, pa, key);
1684  	    der_free_octet_string(&data);
1685  	    der_free_oid(&oid);
1686  
1687  	    break;
1688  	}
1689  	default:
1690  	    free_PA_PK_AS_REP_Win2k(&w2krep);
1691  	    ret = EINVAL;
1692  	    krb5_set_error_message(context, ret,
1693  				   N_("PKINIT: win2k reply invalid "
1694  				      "content type", ""));
1695  	    break;
1696  	}
1697  
1698      } else {
1699  	ret = EINVAL;
1700  	krb5_set_error_message(context, ret,
1701  			       N_("PKINIT: unknown reply type", ""));
1702      }
1703  
1704      return ret;
1705  }
1706  
1707  struct prompter {
1708      krb5_context context;
1709      krb5_prompter_fct prompter;
1710      void *prompter_data;
1711  };
1712  
1713  static int
1714  hx_pass_prompter(void *data, const hx509_prompt *prompter)
1715  {
1716      krb5_error_code ret;
1717      krb5_prompt prompt;
1718      krb5_data password_data;
1719      struct prompter *p = data;
1720  
1721      password_data.data   = prompter->reply.data;
1722      password_data.length = prompter->reply.length;
1723  
1724      prompt.prompt = prompter->prompt;
1725      prompt.hidden = hx509_prompt_hidden(prompter->type);
1726      prompt.reply  = &password_data;
1727  
1728      switch (prompter->type) {
1729      case HX509_PROMPT_TYPE_INFO:
1730  	prompt.type   = KRB5_PROMPT_TYPE_INFO;
1731  	break;
1732      case HX509_PROMPT_TYPE_PASSWORD:
1733      case HX509_PROMPT_TYPE_QUESTION:
1734  	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1735  	break;
1736      }
1737  
1738      ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1739      if (ret) {
1740  	memset (prompter->reply.data, 0, prompter->reply.length);
1741  	return 1;
1742      }
1743      return 0;
1744  }
1745  
1746  krb5_error_code
1747  _krb5_pk_set_user_id(krb5_context context,
1748  		     struct krb5_pk_init_ctx_data *ctx,
1749  		     struct hx509_cert_data *cert)
1750  {
1751      krb5_error_code ret;
1752  
1753      if (ctx->id->cert)
1754  	hx509_cert_free(ctx->id->cert);
1755  
1756      ctx->id->cert = hx509_cert_ref(cert);
1757  
1758      if (ctx->id->certs)
1759  	hx509_certs_free(&ctx->id->certs);
1760  
1761      /* add cert to certs since it might be needed when we do rsa mode PKINIT */
1762      if (ctx->id->cert) {
1763  	ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-set-user-id",
1764  			       0, NULL, &ctx->id->certs);
1765  	if (ret)
1766  	    return ret;
1767  	hx509_certs_add(context->hx509ctx, ctx->id->certs, cert);
1768      }
1769  
1770      if (ctx->id->cert && _krb5_have_debug(context, 2)) {
1771  	hx509_name name;
1772  	char *str, *sn;
1773  	heim_integer i;
1774  
1775  	ret = hx509_cert_get_subject(ctx->id->cert, &name);
1776  	if (ret)
1777  	    return ret;
1778  
1779  	ret = hx509_name_to_string(name, &str);
1780  	hx509_name_free(&name);
1781  	if (ret)
1782  	    return ret;
1783  
1784  	ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1785  	if (ret) {
1786  	    free(str);
1787  	    return ret;
1788  	}
1789  
1790  	ret = der_print_hex_heim_integer(&i, &sn);
1791  	der_free_heim_integer(&i);
1792  	if (ret) {
1793  	    free(name);
1794  	    return ret;
1795  	}
1796  
1797  	_krb5_debugx(context, 2, "using cert: subject: %s sn: %s", str, sn);
1798  	free(str);
1799  	free(sn);
1800      }
1801      return 0;
1802  }
1803  
1804  
1805  static krb5_error_code
1806  _krb5_pk_select_cert(krb5_context context,
1807  		     krb5_principal principal,
1808  		     krb5_pk_init_ctx ctx,
1809  		     struct hx509_certs_data *certs)
1810  {
1811      hx509_certs c = hx509_certs_ref(certs);
1812      hx509_query *q = NULL;
1813      hx509_cert cert = NULL;
1814      int ret;
1815  
1816      if (ctx->id->certs)
1817  	hx509_certs_free(&ctx->id->certs);
1818      if (ctx->id->cert) {
1819  	hx509_cert_free(ctx->id->cert);
1820  	ctx->id->cert = NULL;
1821      }
1822  
1823      /* Merge certs into cert pool so that cms call can use them */
1824      hx509_certs_merge(context->hx509ctx, ctx->id->certpool, c);
1825  
1826      ctx->id->certs = c;
1827      ctx->anonymous = 0;
1828  
1829      ret = hx509_query_alloc(context->hx509ctx, &q);
1830      if (ret) {
1831  	_krb5_pk_copy_error(context, ret,
1832  			    "Allocate query to find signing certificate");
1833  	return ret;
1834      }
1835  	
1836      hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1837      hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1838  	
1839      ret = _krb5_pk_find_cert(context, (ctx->id->flags & PKINIT_BTMM) ? 1 : 0,
1840  			     ctx->id->certs, q, &cert);
1841      hx509_query_free(context->hx509ctx, q);
1842  
1843      if (ret == 0) {
1844  	_krb5_pk_set_user_id(context, ctx, cert);
1845  	hx509_cert_free(cert);
1846      }
1847  
1848      return ret;
1849  }
1850  
1851  KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1852  _krb5_pk_load_id(krb5_context context,
1853  		 struct krb5_pk_identity **ret_id,
1854  		 const char *user_id,
1855  		 const char *anchor_id,
1856  		 char * const *chain_list,
1857  		 char * const *revoke_list,
1858  		 krb5_prompter_fct prompter,
1859  		 void *prompter_data,
1860  		 char *password)
1861  {
1862      struct krb5_pk_identity *id = NULL;
1863      struct prompter p;
1864      int ret;
1865  
1866      *ret_id = NULL;
1867  
1868      if (anchor_id == NULL) {
1869  	krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1870  			       N_("PKINIT: No anchor given", ""));
1871  	return HEIM_PKINIT_NO_VALID_CA;
1872      }
1873  
1874      /* load cert */
1875  
1876      id = calloc(1, sizeof(*id));
1877      if (id == NULL) {
1878  	krb5_set_error_message(context, ENOMEM,
1879  			       N_("malloc: out of memory", ""));
1880  	return ENOMEM;
1881      }
1882  
1883      if (user_id) {
1884  	hx509_lock lock;
1885  
1886  	ret = hx509_lock_init(context->hx509ctx, &lock);
1887  	if (ret) {
1888  	    _krb5_pk_copy_error(context, ret, "Failed init lock");
1889  	    goto out;
1890  	}
1891  
1892  	if (password && password[0])
1893  	    hx509_lock_add_password(lock, password);
1894  
1895  	if (prompter) {
1896  	    p.context = context;
1897  	    p.prompter = prompter;
1898  	    p.prompter_data = prompter_data;
1899  
1900  	    ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1901  	    if (ret) {
1902  		hx509_lock_free(lock);
1903  		goto out;
1904  	    }
1905  	}
1906  
1907  	ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1908          hx509_lock_free(lock);
1909  	if (ret) {
1910  	    _krb5_pk_copy_error(context, ret,
1911  			  "Failed to init cert certs");
1912  	    goto out;
1913  	}
1914      } else {
1915  	id->certs = NULL;
1916      }
1917  
1918      ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1919      if (ret) {
1920  	_krb5_pk_copy_error(context, ret,
1921  			    "Failed to init anchors");
1922  	goto out;
1923      }
1924  
1925      ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1926  			   0, NULL, &id->certpool);
1927      if (ret) {
1928  	_krb5_pk_copy_error(context, ret,
1929  			    "Failed to init chain");
1930  	goto out;
1931      }
1932  
1933      while (chain_list && *chain_list) {
1934  	ret = hx509_certs_append(context->hx509ctx, id->certpool,
1935  				 NULL, *chain_list);
1936  	if (ret)
1937  	    _krb5_debugx(context, 5,
1938  			"Failed to load cert pool: %s",
1939  			*chain_list);
1940  	chain_list++;
1941      }
1942  
1943      if (revoke_list) {
1944  	ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1945  	if (ret) {
1946  	    _krb5_pk_copy_error(context, ret,
1947  				"Failed init revoke list");
1948  	    goto out;
1949  	}
1950  
1951  	while (*revoke_list) {
1952  	    ret = hx509_revoke_add_crl(context->hx509ctx,
1953  				       id->revokectx,
1954  				       *revoke_list);
1955  	    if (ret) {
1956  		_krb5_pk_copy_error(context, ret,
1957  				    "Failed load revoke list");
1958  		goto out;
1959  	    }
1960  	    revoke_list++;
1961  	}
1962      } else
1963  	hx509_context_set_missing_revoke(context->hx509ctx, 1);
1964  
1965      ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1966      if (ret) {
1967  	_krb5_pk_copy_error(context, ret,
1968  			    "Failed init verify context");
1969  	goto out;
1970      }
1971  
1972      hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1973      hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1974  
1975   out:
1976      if (ret) {
1977  	hx509_verify_destroy_ctx(id->verify_ctx);
1978  	hx509_certs_free(&id->certs);
1979  	hx509_certs_free(&id->anchors);
1980  	hx509_certs_free(&id->certpool);
1981  	hx509_revoke_free(&id->revokectx);
1982  	free(id);
1983      } else
1984  	*ret_id = id;
1985  
1986      return ret;
1987  }
1988  
1989  /*
1990   *
1991   */
1992  
1993  static int
1994  parse_integer(krb5_context context, char **p, const char *file, int lineno,
1995  	      const char *name, heim_integer *integer)
1996  {
1997      int ret;
1998      char *p1;
1999      p1 = strsep(p, " \t");
2000      if (p1 == NULL) {
2001  	krb5_set_error_message(context, EINVAL,
2002  			       N_("moduli file %s missing %s on line %d", ""),
2003  			       file, name, lineno);
2004  	return EINVAL;
2005      }
2006      ret = der_parse_hex_heim_integer(p1, integer);
2007      if (ret) {
2008  	krb5_set_error_message(context, ret,
2009  			       N_("moduli file %s failed parsing %s "
2010  				  "on line %d", ""),
2011  			       file, name, lineno);
2012  	return ret;
2013      }
2014  
2015      return 0;
2016  }
2017  
2018  krb5_error_code
2019  _krb5_parse_moduli_line(krb5_context context,
2020  			const char *file,
2021  			int lineno,
2022  			char *p,
2023  			struct krb5_dh_moduli **m)
2024  {
2025      struct krb5_dh_moduli *m1;
2026      char *p1;
2027      int ret;
2028  
2029      *m = NULL;
2030  
2031      m1 = calloc(1, sizeof(*m1));
2032      if (m1 == NULL) {
2033  	krb5_set_error_message(context, ENOMEM,
2034  			       N_("malloc: out of memory", ""));
2035  	return ENOMEM;
2036      }
2037  
2038      while (isspace((unsigned char)*p))
2039  	p++;
2040      if (*p  == '#') {
2041          free(m1);
2042  	return 0;
2043      }
2044      ret = EINVAL;
2045  
2046      p1 = strsep(&p, " \t");
2047      if (p1 == NULL) {
2048  	krb5_set_error_message(context, ret,
2049  			       N_("moduli file %s missing name on line %d", ""),
2050  			       file, lineno);
2051  	goto out;
2052      }
2053      m1->name = strdup(p1);
2054      if (m1->name == NULL) {
2055  	ret = ENOMEM;
2056  	krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
2057  	goto out;
2058      }
2059  
2060      p1 = strsep(&p, " \t");
2061      if (p1 == NULL) {
2062  	krb5_set_error_message(context, ret,
2063  			       N_("moduli file %s missing bits on line %d", ""),
2064  			       file, lineno);
2065  	goto out;
2066      }
2067  
2068      m1->bits = atoi(p1);
2069      if (m1->bits == 0) {
2070  	krb5_set_error_message(context, ret,
2071  			       N_("moduli file %s have un-parsable "
2072  				  "bits on line %d", ""), file, lineno);
2073  	goto out;
2074      }
2075  
2076      ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2077      if (ret)
2078  	goto out;
2079      ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2080      if (ret)
2081  	goto out;
2082      ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2083      if (ret)
2084  	goto out;
2085  
2086      *m = m1;
2087  
2088      return 0;
2089   out:
2090      free(m1->name);
2091      der_free_heim_integer(&m1->p);
2092      der_free_heim_integer(&m1->g);
2093      der_free_heim_integer(&m1->q);
2094      free(m1);
2095      return ret;
2096  }
2097  
2098  void
2099  _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2100  {
2101      int i;
2102      for (i = 0; moduli[i] != NULL; i++) {
2103  	free(moduli[i]->name);
2104  	der_free_heim_integer(&moduli[i]->p);
2105  	der_free_heim_integer(&moduli[i]->g);
2106  	der_free_heim_integer(&moduli[i]->q);
2107  	free(moduli[i]);
2108      }
2109      free(moduli);
2110  }
2111  
2112  static const char *default_moduli_rfc3526_MODP_group16 =
2113      /* name */
2114      "rfc3526-MODP-group16 "
2115      /* bits */
2116      "4096 "
2117      /* p */
2118      "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2119      "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2120      "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2121      "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2122      "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2123      "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2124      "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2125      "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2126      "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2127      "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2128      "15728E5A" "8AAAC42D" "AD33170D" "04507A33" "A85521AB" "DF1CBA64"
2129      "ECFB8504" "58DBEF0A" "8AEA7157" "5D060C7D" "B3970F85" "A6E1E4C7"
2130      "ABF5AE8C" "DB0933D7" "1E8C94E0" "4A25619D" "CEE3D226" "1AD2EE6B"
2131      "F12FFA06" "D98A0864" "D8760273" "3EC86A64" "521F2B18" "177B200C"
2132      "BBE11757" "7A615D6C" "770988C0" "BAD946E2" "08E24FA0" "74E5AB31"
2133      "43DB5BFC" "E0FD108E" "4B82D120" "A9210801" "1A723C12" "A787E6D7"
2134      "88719A10" "BDBA5B26" "99C32718" "6AF4E23C" "1A946834" "B6150BDA"
2135      "2583E9CA" "2AD44CE8" "DBBBC2DB" "04DE8EF9" "2E8EFC14" "1FBECAA6"
2136      "287C5947" "4E6BC05D" "99B2964F" "A090C3A2" "233BA186" "515BE7ED"
2137      "1F612970" "CEE2D7AF" "B81BDD76" "2170481C" "D0069127" "D5B05AA9"
2138      "93B4EA98" "8D8FDDC1" "86FFB7DC" "90A6C08F" "4DF435C9" "34063199"
2139      "FFFFFFFF" "FFFFFFFF "
2140      /* g */
2141      "02 "
2142      /* q */
2143      "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2144      "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2145      "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2146      "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2147      "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2148      "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2149      "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2150      "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2151      "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2152      "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2153      "0AB9472D" "45556216" "D6998B86" "82283D19" "D42A90D5" "EF8E5D32"
2154      "767DC282" "2C6DF785" "457538AB" "AE83063E" "D9CB87C2" "D370F263"
2155      "D5FAD746" "6D8499EB" "8F464A70" "2512B0CE" "E771E913" "0D697735"
2156      "F897FD03" "6CC50432" "6C3B0139" "9F643532" "290F958C" "0BBD9006"
2157      "5DF08BAB" "BD30AEB6" "3B84C460" "5D6CA371" "047127D0" "3A72D598"
2158      "A1EDADFE" "707E8847" "25C16890" "54908400" "8D391E09" "53C3F36B"
2159      "C438CD08" "5EDD2D93" "4CE1938C" "357A711E" "0D4A341A" "5B0A85ED"
2160      "12C1F4E5" "156A2674" "6DDDE16D" "826F477C" "97477E0A" "0FDF6553"
2161      "143E2CA3" "A735E02E" "CCD94B27" "D04861D1" "119DD0C3" "28ADF3F6"
2162      "8FB094B8" "67716BD7" "DC0DEEBB" "10B8240E" "68034893" "EAD82D54"
2163      "C9DA754C" "46C7EEE0" "C37FDBEE" "48536047" "A6FA1AE4" "9A0318CC"
2164      "FFFFFFFF" "FFFFFFFF";
2165  
2166  static const char *default_moduli_rfc3526_MODP_group14 =
2167      /* name */
2168      "rfc3526-MODP-group14 "
2169      /* bits */
2170      "1760 "
2171      /* p */
2172      "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2173      "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2174      "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2175      "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2176      "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2177      "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2178      "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2179      "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2180      "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2181      "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2182      "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2183      /* g */
2184      "02 "
2185      /* q */
2186      "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2187      "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2188      "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2189      "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2190      "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2191      "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2192      "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2193      "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2194      "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2195      "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2196      "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2197  
2198  
2199  krb5_error_code
2200  _krb5_parse_moduli(krb5_context context, const char *file,
2201  		   struct krb5_dh_moduli ***moduli)
2202  {
2203      /* name bits P G Q */
2204      krb5_error_code ret;
2205      struct krb5_dh_moduli **m = NULL, **m2;
2206      char buf[4096];
2207      FILE *f;
2208      int lineno = 0, n = 0;
2209  
2210      *moduli = NULL;
2211  
2212      m = calloc(1, sizeof(m[0]) * 3);
2213      if (m == NULL) {
2214  	krb5_set_error_message(context, ENOMEM,
2215  			       N_("malloc: out of memory", ""));
2216  	return ENOMEM;
2217      }
2218  
2219      strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2220      ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[0]);
2221      if (ret) {
2222  	_krb5_free_moduli(m);
2223  	return ret;
2224      }
2225      n++;
2226  
2227      strlcpy(buf, default_moduli_rfc3526_MODP_group16, sizeof(buf));
2228      ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[1]);
2229      if (ret) {
2230  	_krb5_free_moduli(m);
2231  	return ret;
2232      }
2233      n++;
2234  
2235  
2236      if (file == NULL)
2237  	file = MODULI_FILE;
2238  
2239  #ifdef KRB5_USE_PATH_TOKENS
2240      {
2241          char * exp_file;
2242  
2243          if (_krb5_expand_path_tokens(context, file, &exp_file) == 0) {
2244              f = fopen(exp_file, "r");
2245              krb5_xfree(exp_file);
2246          } else {
2247              f = NULL;
2248          }
2249      }
2250  #else
2251      f = fopen(file, "r");
2252  #endif
2253  
2254      if (f == NULL) {
2255  	*moduli = m;
2256  	return 0;
2257      }
2258      rk_cloexec_file(f);
2259  
2260      while(fgets(buf, sizeof(buf), f) != NULL) {
2261  	struct krb5_dh_moduli *element;
2262  
2263  	buf[strcspn(buf, "\n")] = '\0';
2264  	lineno++;
2265  
2266  	m2 = realloc(m, (n + 2) * sizeof(m[0]));
2267  	if (m2 == NULL) {
2268  	    _krb5_free_moduli(m);
2269  	    krb5_set_error_message(context, ENOMEM,
2270  				   N_("malloc: out of memory", ""));
2271  	    return ENOMEM;
2272  	}
2273  	m = m2;
2274  
2275  	m[n] = NULL;
2276  
2277  	ret = _krb5_parse_moduli_line(context, file, lineno, buf,  &element);
2278  	if (ret) {
2279  	    _krb5_free_moduli(m);
2280  	    return ret;
2281  	}
2282  	if (element == NULL)
2283  	    continue;
2284  
2285  	m[n] = element;
2286  	m[n + 1] = NULL;
2287  	n++;
2288      }
2289      *moduli = m;
2290      return 0;
2291  }
2292  
2293  krb5_error_code
2294  _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2295  		  heim_integer *p, heim_integer *g, heim_integer *q,
2296  		  struct krb5_dh_moduli **moduli,
2297  		  char **name)
2298  {
2299      int i;
2300  
2301      if (name)
2302  	*name = NULL;
2303  
2304      for (i = 0; moduli[i] != NULL; i++) {
2305  	if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2306  	    der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2307  	    (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2308  	    {
2309  		if (bits && bits > moduli[i]->bits) {
2310  		    krb5_set_error_message(context,
2311  					   KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2312  					   N_("PKINIT: DH group parameter %s "
2313  					      "no accepted, not enough bits "
2314  					      "generated", ""),
2315  					   moduli[i]->name);
2316  		    return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2317  		}
2318  		if (name)
2319  		    *name = strdup(moduli[i]->name);
2320  		return 0;
2321  	    }
2322      }
2323      krb5_set_error_message(context,
2324  			   KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2325  			   N_("PKINIT: DH group parameter no ok", ""));
2326      return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2327  }
2328  #endif /* PKINIT */
2329  
2330  KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2331  _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2332  {
2333  #ifdef PKINIT
2334      krb5_pk_init_ctx ctx;
2335  
2336      if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2337  	return;
2338      ctx = opt->opt_private->pk_init_ctx;
2339      switch (ctx->keyex) {
2340      case USE_DH:
2341  	if (ctx->u.dh)
2342  	    DH_free(ctx->u.dh);
2343  	break;
2344      case USE_RSA:
2345  	break;
2346      case USE_ECDH:
2347  #ifdef HAVE_OPENSSL
2348  	if (ctx->u.eckey)
2349  	    EC_KEY_free(ctx->u.eckey);
2350  #endif
2351  	break;
2352      }
2353      if (ctx->id) {
2354  	hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2355  	hx509_certs_free(&ctx->id->certs);
2356  	hx509_cert_free(ctx->id->cert);
2357  	hx509_certs_free(&ctx->id->anchors);
2358  	hx509_certs_free(&ctx->id->certpool);
2359  
2360  	if (ctx->clientDHNonce) {
2361  	    krb5_free_data(NULL, ctx->clientDHNonce);
2362  	    ctx->clientDHNonce = NULL;
2363  	}
2364  	if (ctx->m)
2365  	    _krb5_free_moduli(ctx->m);
2366  	free(ctx->id);
2367  	ctx->id = NULL;
2368      }
2369      free(opt->opt_private->pk_init_ctx);
2370      opt->opt_private->pk_init_ctx = NULL;
2371  #endif
2372  }
2373  
2374  KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2375  _krb5_get_init_creds_opt_set_pkinit_user_cert(krb5_context context,
2376                                     krb5_get_init_creds_opt *opt,
2377                                     krb5_principal principal,
2378                                     const char *user_id,
2379                                     const char *x509_anchors,
2380                                     char * const * pool,
2381                                     char * const * pki_revoke,
2382                                     int flags,
2383                                     krb5_prompter_fct prompter,
2384                                     void *prompter_data,
2385                                     char *password)
2386  {
2387  	return krb5_get_init_creds_opt_set_pkinit(context, opt, principal, user_id, x509_anchors, pool, pki_revoke, flags, prompter, prompter_data, password);
2388  } 
2389  
2390  KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2391  krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2392  				   krb5_get_init_creds_opt *opt,
2393  				   krb5_principal principal,
2394  				   const char *user_id,
2395  				   const char *x509_anchors,
2396  				   char * const * pool,
2397  				   char * const * pki_revoke,
2398  				   int flags,
2399  				   krb5_prompter_fct prompter,
2400  				   void *prompter_data,
2401  				   char *password)
2402  {
2403  #ifdef PKINIT
2404      krb5_error_code ret;
2405      char *anchors = NULL;
2406  
2407      if (opt->opt_private == NULL) {
2408  	krb5_set_error_message(context, EINVAL,
2409  			       N_("PKINIT: on non extendable opt", ""));
2410  	return EINVAL;
2411      }
2412  
2413      opt->opt_private->pk_init_ctx =
2414  	calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2415      if (opt->opt_private->pk_init_ctx == NULL) {
2416  	krb5_set_error_message(context, ENOMEM,
2417  			       N_("malloc: out of memory", ""));
2418  	return ENOMEM;
2419      }
2420      opt->opt_private->pk_init_ctx->require_binding = 0;
2421      opt->opt_private->pk_init_ctx->require_eku = 1;
2422      opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2423      opt->opt_private->pk_init_ctx->peer = NULL;
2424  
2425      /* XXX implement krb5_appdefault_strings  */
2426      if (pool == NULL)
2427  	pool = krb5_config_get_strings(context, NULL,
2428  				       "appdefaults",
2429  				       "pkinit_pool",
2430  				       NULL);
2431      if (pool == NULL) {
2432  	static char *default_pool[] = { "KEYCHAIN:", NULL };
2433  	pool = default_pool;
2434      }
2435  
2436      if (pki_revoke == NULL)
2437  	pki_revoke = krb5_config_get_strings(context, NULL,
2438  					     "appdefaults",
2439  					     "pkinit_revoke",
2440  					     NULL);
2441  
2442      if (x509_anchors == NULL) {
2443  	krb5_appdefault_string(context, "kinit",
2444  			       krb5_principal_get_realm(context, principal),
2445  			       "pkinit_anchors", NULL, &anchors);
2446  	x509_anchors = anchors;
2447      }
2448  
2449      if (flags & 4)
2450  	opt->opt_private->pk_init_ctx->anonymous = 1;
2451  
2452      ret = _krb5_pk_load_id(context,
2453  			   &opt->opt_private->pk_init_ctx->id,
2454  			   user_id,
2455  			   x509_anchors,
2456  			   pool,
2457  			   pki_revoke,
2458  			   prompter,
2459  			   prompter_data,
2460  			   password);
2461      if (ret) {
2462  	free(opt->opt_private->pk_init_ctx);
2463  	opt->opt_private->pk_init_ctx = NULL;
2464  	return ret;
2465      }
2466      if (flags & 8)
2467  	opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
2468  
2469      if (principal && krb5_principal_is_lkdc(context, principal))
2470  	opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
2471  
2472      if (opt->opt_private->pk_init_ctx->id->certs) {
2473  	_krb5_pk_select_cert(context,
2474  			     principal,
2475  			     opt->opt_private->pk_init_ctx,
2476  			     opt->opt_private->pk_init_ctx->id->certs);
2477      } else
2478  	opt->opt_private->pk_init_ctx->id->cert = NULL;
2479  
2480      if ((flags & 2) == 0) {
2481  	hx509_context hx509ctx = context->hx509ctx;
2482  	hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2483  
2484  	opt->opt_private->pk_init_ctx->keyex = USE_DH;
2485  
2486  	/*
2487  	 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2488  	 */
2489  	if (cert) {
2490  	    AlgorithmIdentifier alg;
2491  
2492  	    ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2493  	    if (ret == 0) {
2494  		if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2495  		    opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2496  		free_AlgorithmIdentifier(&alg);
2497  	    }
2498  	}
2499  
2500      } else {
2501  	opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2502  
2503  	if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2504  	    krb5_set_error_message(context, EINVAL,
2505  				   N_("No anonymous pkinit support in RSA mode", ""));
2506  	    return EINVAL;
2507  	}
2508      }
2509  
2510      return 0;
2511  #else
2512      krb5_set_error_message(context, EINVAL,
2513  			   N_("no support for PKINIT compiled in", ""));
2514      return EINVAL;
2515  #endif
2516  }
2517  
2518  krb5_error_code KRB5_LIB_FUNCTION
2519  krb5_get_init_creds_opt_set_pkinit_user_cert(krb5_context context,
2520  					     krb5_get_init_creds_opt *opt,
2521  					     struct hx509_cert_data *cert)
2522  {
2523  #ifdef PKINIT
2524      if (opt->opt_private == NULL) {
2525  	krb5_set_error_message(context, EINVAL,
2526  			       N_("PKINIT: on non extendable opt", ""));
2527  	return EINVAL;
2528      }
2529      if (opt->opt_private->pk_init_ctx == NULL) {
2530  	krb5_set_error_message(context, EINVAL,
2531  			       N_("PKINIT: on pkinit context", ""));
2532  	return EINVAL;
2533      }
2534  
2535      _krb5_pk_set_user_id(context, opt->opt_private->pk_init_ctx, cert);
2536  
2537      return 0;
2538  #else
2539      krb5_set_error_message(context, EINVAL,
2540  			   N_("no support for PKINIT compiled in", ""));
2541      return EINVAL;
2542  #endif
2543  }
2544  
2545  #ifdef PKINIT
2546  
2547  static int
2548  get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2549  {
2550      hx509_octet_string_list list;
2551      int ret;
2552  
2553      *upn = NULL;
2554  
2555      ret = hx509_cert_find_subjectAltName_otherName(context,
2556  						   cert,
2557  						   &asn1_oid_id_pkinit_ms_san,
2558  						   &list);
2559      if (ret)
2560  	return 0;
2561  
2562      if (list.len > 0 && list.val[0].length > 0)
2563  	ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2564  				upn, NULL);
2565      else
2566  	ret = 1;
2567      hx509_free_octet_string_list(&list);
2568  
2569      return ret;
2570  }
2571  
2572  static int
2573  find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2574  {
2575      char *upn;
2576      int ret;
2577  
2578      ret = get_ms_san(context, cert, &upn);
2579      if (ret == 0)
2580  	free(upn);
2581      return ret;
2582  }
2583  
2584  
2585  
2586  #endif
2587  
2588  KRB5_LIB_FUNCTION krb5_error_code  KRB5_LIB_CALL
2589  _krb5_pk_enterprise_cert(krb5_context context,
2590  			 const char *user_id,
2591  			 krb5_const_realm realm,
2592  			 krb5_principal *principal,
2593  			 struct hx509_cert_data **res)
2594  {
2595      return krb5_pk_enterprise_cert(context, user_id, realm, principal, res);
2596  }
2597  
2598  
2599  /*
2600   * Private since it need to be redesigned using krb5_get_init_creds()
2601   */
2602  
2603  KRB5_LIB_FUNCTION krb5_error_code  KRB5_LIB_CALL
2604  krb5_pk_enterprise_cert(krb5_context context,
2605  			const char *user_id,
2606  			krb5_const_realm realm,
2607  			krb5_principal *principal,
2608  			struct hx509_cert_data **res)
2609  {
2610  #ifdef PKINIT
2611      krb5_error_code ret;
2612      hx509_certs certs, result;
2613      hx509_cert cert = NULL;
2614      hx509_query *q;
2615      char *name;
2616  
2617      *principal = NULL;
2618      if (res)
2619  	*res = NULL;
2620  
2621      if (user_id == NULL) {
2622  	krb5_set_error_message(context, ENOENT, "no user id");
2623  	return ENOENT;
2624      }
2625  
2626      ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2627      if (ret) {
2628  	_krb5_pk_copy_error(context, ret,
2629  			    "Failed to init cert certs");
2630  	goto out;
2631      }
2632  
2633      ret = hx509_query_alloc(context->hx509ctx, &q);
2634      if (ret) {
2635  	krb5_set_error_message(context, ret, "out of memory");
2636  	hx509_certs_free(&certs);
2637  	goto out;
2638      }
2639  
2640      hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2641      hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2642      hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2643      hx509_query_match_cmp_func(q, find_ms_san, NULL);
2644  
2645      ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2646      hx509_query_free(context->hx509ctx, q);
2647      hx509_certs_free(&certs);
2648      if (ret) {
2649  	_krb5_pk_copy_error(context, ret,
2650  			    "Failed to find PKINIT certificate");
2651  	return ret;
2652      }
2653  
2654      ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2655      hx509_certs_free(&result);
2656      if (ret) {
2657  	_krb5_pk_copy_error(context, ret,
2658  			    "Failed to get one cert");
2659  	goto out;
2660      }
2661  
2662      ret = get_ms_san(context->hx509ctx, cert, &name);
2663      if (ret) {
2664  	_krb5_pk_copy_error(context, ret,
2665  			    "Failed to get MS SAN");
2666  	goto out;
2667      }
2668  
2669      ret = krb5_make_principal(context, principal, realm, name, NULL);
2670      free(name);
2671      if (ret)
2672  	goto out;
2673  
2674      krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2675  
2676      if (res)
2677         *res = hx509_cert_ref(cert);
2678  
2679   out:
2680      hx509_cert_free(cert);
2681      if (ret) {
2682  	ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
2683  	krb5_set_error_message(context, ret,
2684  			       N_("PK-INIT cert didn't contain principal SAN", ""));
2685      }
2686  
2687      return ret;
2688  #else
2689      krb5_set_error_message(context, EINVAL,
2690  			   N_("no support for PKINIT compiled in", ""));
2691      return EINVAL;
2692  #endif
2693  }
2694  
2695  krb5_error_code  KRB5_LIB_FUNCTION
2696  _krb5_pk_match_cert(krb5_context context,
2697                      krb5_principal principal,
2698                      struct hx509_cert_data *cert,
2699                      int match_realm)
2700  {
2701  #ifdef PKINIT
2702      hx509_octet_string_list list;
2703      krb5_error_code ret;
2704      unsigned i;
2705      int found = 0;
2706  
2707      ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx, cert,
2708                                                     &asn1_oid_id_pkinit_san,
2709                                                     &list);
2710      if (ret) {
2711          krb5_set_error_message(context, ret,
2712                                 N_("Failed to find the PK-INIT "
2713                                    "subjectAltName in the certificate", ""));
2714          
2715          return ret;
2716      }
2717  
2718      for (i = 0; !found && i < list.len; i++) {
2719          KRB5PrincipalName r;
2720  
2721          ret = decode_KRB5PrincipalName(list.val[i].data,
2722                                         list.val[i].length,
2723                                         &r, NULL);
2724          if (ret) {
2725              krb5_set_error_message(context, ret,
2726                                     N_("Failed to decode the PK-INIT "
2727                                        "subjectAltName in the "
2728                                        "KDC certificate", ""));
2729              goto out;
2730          }
2731  
2732          if (_krb5_principal_compare_PrincipalName(context, principal, &r.principalName) &&
2733              (!match_realm || strcmp(r.realm, principal->realm) == 0))
2734              found = 1;
2735  
2736          free_KRB5PrincipalName(&r);
2737      }
2738   out:
2739      if (found)
2740          ret = 0;
2741      else if (ret == 0) {
2742          ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
2743          krb5_set_error_message(context, ret,
2744                                 N_("PK-INIT cert didn't contain principal SAN", ""));
2745      }
2746  
2747      hx509_free_octet_string_list(&list);
2748      return ret;
2749  #else
2750      krb5_set_error_message(context, EINVAL,
2751                             N_("no support for PKINIT compiled in", ""));
2752      return EINVAL;
2753  #endif
2754  }
2755  
2756  
2757  
2758  #ifdef PKINIT
2759  
2760  void
2761  _krb5_pk_copy_error(krb5_context context, int hxret, const char *fmt, ...)
2762      HEIMDAL_PRINTF_ATTRIBUTE((printf, 3, 4))
2763  {
2764      va_list va;
2765      char *s, *f;
2766      int ret;
2767  
2768      va_start(va, fmt);
2769      ret = vasprintf(&f, fmt, va);
2770      va_end(va);
2771      if (ret == -1 || f == NULL) {
2772  	krb5_clear_error_message(context);
2773  	return;
2774      }
2775  
2776      s = hx509_get_error_string(context->hx509ctx, hxret);
2777      if (s == NULL) {
2778  	krb5_clear_error_message(context);
2779  	free(f);
2780  	return;
2781      }
2782      krb5_set_error_message(context, hxret, "%s: %s", f, s);
2783      _krb5_debugx(context, 5, "%s: %s: %d", f, s, hxret);
2784      free(s);
2785      free(f);
2786  }
2787  
2788  #endif