diff options
Diffstat (limited to 'lib/krb5/pkinit.c')
| -rw-r--r-- | lib/krb5/pkinit.c | 1725 | 
1 files changed, 1148 insertions, 577 deletions
| diff --git a/lib/krb5/pkinit.c b/lib/krb5/pkinit.c index a0b6a4e07938..1103a17807be 100644 --- a/lib/krb5/pkinit.c +++ b/lib/krb5/pkinit.c @@ -1,40 +1,40 @@  /* - * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan - * (Royal Institute of Technology, Stockholm, Sweden).  - * All rights reserved.  + * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved.   * - * Redistribution and use in source and binary forms, with or without  - * modification, are permitted provided that the following conditions  - * are met:  + * Portions Copyright (c) 2009 Apple Inc. All rights reserved.   * - * 1. Redistributions of source code must retain the above copyright  - *    notice, this list of conditions and the following disclaimer.  + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met:   * - * 2. Redistributions in binary form must reproduce the above copyright  - *    notice, this list of conditions and the following disclaimer in the  - *    documentation and/or other materials provided with the distribution.  + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer.   * - * 3. Neither the name of the Institute nor the names of its contributors  - *    may be used to endorse or promote products derived from this software  - *    without specific prior written permission.  + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution.   * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND  - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  - * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE  - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS  - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)  - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT  - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY  - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  - * SUCH DAMAGE.  + * 3. Neither the name of the Institute nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE.   */  #include "krb5_locl.h" -RCSID("$Id: pkinit.c 22433 2008-01-13 14:11:46Z lha $"); -  struct krb5_dh_moduli {      char *name;      unsigned long bits; @@ -45,8 +45,6 @@ struct krb5_dh_moduli {  #ifdef PKINIT -#include <heim_asn1.h> -#include <rfc2459_asn1.h>  #include <cms_asn1.h>  #include <pkcs8_asn1.h>  #include <pkcs9_asn1.h> @@ -56,53 +54,44 @@ struct krb5_dh_moduli {  #include <der.h> -#include <hx509.h> - -enum { -    COMPAT_WIN2K = 1, -    COMPAT_IETF = 2 -}; - -struct krb5_pk_identity { -    hx509_context hx509ctx; -    hx509_verify_ctx verify_ctx; -    hx509_certs certs; -    hx509_certs anchors; -    hx509_certs certpool; -    hx509_revoke_ctx revokectx; -}; -  struct krb5_pk_cert {      hx509_cert cert;  };  struct krb5_pk_init_ctx_data {      struct krb5_pk_identity *id; -    DH *dh; +    enum { USE_RSA, USE_DH, USE_ECDH } keyex; +    union { +	DH *dh; +#ifdef HAVE_OPENSSL +	EC_KEY *eckey; +#endif +    } u;      krb5_data *clientDHNonce;      struct krb5_dh_moduli **m;      hx509_peer_info peer; -    int type; +    enum krb5_pk_type type;      unsigned int require_binding:1;      unsigned int require_eku:1;      unsigned int require_krbtgt_otherName:1;      unsigned int require_hostname_match:1;      unsigned int trustedCertifiers:1; +    unsigned int anonymous:1;  };  static void -_krb5_pk_copy_error(krb5_context context, -		    hx509_context hx509ctx, -		    int hxret, -		    const char *fmt, -		    ...) +pk_copy_error(krb5_context context, +	      hx509_context hx509ctx, +	      int hxret, +	      const char *fmt, +	      ...)      __attribute__ ((format (printf, 4, 5)));  /*   *   */ -void KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION void KRB5_LIB_CALL  _krb5_pk_cert_free(struct krb5_pk_cert *cert)  {      if (cert->cert) { @@ -117,7 +106,7 @@ BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)      integer->length = BN_num_bytes(bn);      integer->data = malloc(integer->length);      if (integer->data == NULL) { -	krb5_clear_error_string(context); +	krb5_clear_error_message(context);  	return ENOMEM;      }      BN_bn2bin(bn, integer->data); @@ -132,60 +121,136 @@ integer_to_BN(krb5_context context, const char *field, const heim_integer *f)      bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);      if (bn == NULL) { -	krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field); +	krb5_set_error_message(context, ENOMEM, +			       N_("PKINIT: parsing BN failed %s", ""), field);  	return NULL;      }      BN_set_negative(bn, f->negative);      return bn;  } -  static krb5_error_code -_krb5_pk_create_sign(krb5_context context, -		     const heim_oid *eContentType, -		     krb5_data *eContent, -		     struct krb5_pk_identity *id, -		     hx509_peer_info peer, -		     krb5_data *sd_data) +select_dh_group(krb5_context context, DH *dh, unsigned long bits, +		struct krb5_dh_moduli **moduli)  { -    hx509_cert cert; -    hx509_query *q; -    int ret; +    const struct krb5_dh_moduli *m; -    ret = hx509_query_alloc(id->hx509ctx, &q); -    if (ret) { -	_krb5_pk_copy_error(context, id->hx509ctx, ret,  -			    "Allocate query to find signing certificate"); -	return ret; +    if (bits == 0) { +	m = moduli[1]; /* XXX */ +	if (m == NULL) +	    m = moduli[0]; /* XXX */ +    } else { +	int i; +	for (i = 0; moduli[i] != NULL; i++) { +	    if (bits < moduli[i]->bits) +		break; +	} +	if (moduli[i] == NULL) { +	    krb5_set_error_message(context, EINVAL, +				   N_("Did not find a DH group parameter " +				      "matching requirement of %lu bits", ""), +				   bits); +	    return EINVAL; +	} +	m = moduli[i];      } -    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); -    hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); +    dh->p = integer_to_BN(context, "p", &m->p); +    if (dh->p == NULL) +	return ENOMEM; +    dh->g = integer_to_BN(context, "g", &m->g); +    if (dh->g == NULL) +	return ENOMEM; +    dh->q = integer_to_BN(context, "q", &m->q); +    if (dh->q == NULL) +	return ENOMEM; -    ret = hx509_certs_find(id->hx509ctx, id->certs, q, &cert); -    hx509_query_free(id->hx509ctx, q); -    if (ret) { -	_krb5_pk_copy_error(context, id->hx509ctx, ret,  -			    "Find certificate to signed CMS data"); -	return ret; +    return 0; +} + +struct certfind { +    const char *type; +    const heim_oid *oid; +}; + +/* + * Try searchin the key by to use by first looking for for PK-INIT + * EKU, then the Microsoft smart card EKU and last, no special EKU at all. + */ + +static krb5_error_code +find_cert(krb5_context context, struct krb5_pk_identity *id, +	  hx509_query *q, hx509_cert *cert) +{ +    struct certfind cf[4] = { +	{ "MobileMe EKU" }, +	{ "PKINIT EKU" }, +	{ "MS EKU" }, +	{ "any (or no)" } +    }; +    int ret = HX509_CERT_NOT_FOUND; +    size_t i, start = 1; +    unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 }; +    const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids }; + + +    if (id->flags & PKINIT_BTMM) +	start = 0; + +    cf[0].oid = &mobileMe; +    cf[1].oid = &asn1_oid_id_pkekuoid; +    cf[2].oid = &asn1_oid_id_pkinit_ms_eku; +    cf[3].oid = NULL; + +    for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) { +	ret = hx509_query_match_eku(q, cf[i].oid); +	if (ret) { +	    pk_copy_error(context, context->hx509ctx, ret, +			  "Failed setting %s OID", cf[i].type); +	    return ret; +	} + +	ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert); +	if (ret == 0) +	    break; +	pk_copy_error(context, context->hx509ctx, ret, +		      "Failed finding certificate with %s OID", cf[i].type);      } +    return ret; +} + + +static krb5_error_code +create_signature(krb5_context context, +		 const heim_oid *eContentType, +		 krb5_data *eContent, +		 struct krb5_pk_identity *id, +		 hx509_peer_info peer, +		 krb5_data *sd_data) +{ +    int ret, flags = 0; + +    if (id->cert == NULL) +	flags |= HX509_CMS_SIGNATURE_NO_SIGNER; -    ret = hx509_cms_create_signed_1(id->hx509ctx, -				    0, +    ret = hx509_cms_create_signed_1(context->hx509ctx, +				    flags,  				    eContentType,  				    eContent->data,  				    eContent->length,  				    NULL, -				    cert, +				    id->cert,  				    peer,  				    NULL,  				    id->certs,  				    sd_data); -    if (ret) -	_krb5_pk_copy_error(context, id->hx509ctx, ret, "create CMS signedData"); -    hx509_cert_free(cert); +    if (ret) { +	pk_copy_error(context, context->hx509ctx, ret, +		      "Create CMS signedData"); +	return ret; +    } -    return ret; +    return 0;  }  static int @@ -197,6 +262,9 @@ cert2epi(hx509_context context, void *ctx, hx509_cert c)      void *p;      int ret; +    if (ids->len > 10) +	return 0; +      memset(&id, 0, sizeof(id));      ret = hx509_cert_get_subject(c, &subject); @@ -211,7 +279,7 @@ cert2epi(hx509_context context, void *ctx, hx509_cert c)  	    free_ExternalPrincipalIdentifier(&id);  	    return ENOMEM;  	} -     +  	ret = hx509_name_binary(subject, id.subjectName);  	if (ret) {  	    hx509_name_free(&subject); @@ -231,8 +299,8 @@ cert2epi(hx509_context context, void *ctx, hx509_cert c)      {  	IssuerAndSerialNumber iasn;  	hx509_name issuer; -	size_t size; -	 +	size_t size = 0; +  	memset(&iasn, 0, sizeof(iasn));  	ret = hx509_cert_get_issuer(c, &issuer); @@ -247,7 +315,7 @@ cert2epi(hx509_context context, void *ctx, hx509_cert c)  	    free_ExternalPrincipalIdentifier(&id);  	    return ret;  	} -	 +  	ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);  	if (ret) {  	    free_IssuerAndSerialNumber(&iasn); @@ -256,7 +324,7 @@ cert2epi(hx509_context context, void *ctx, hx509_cert c)  	}  	ASN1_MALLOC_ENCODE(IssuerAndSerialNumber, -			   id.issuerAndSerialNumber->data,  +			   id.issuerAndSerialNumber->data,  			   id.issuerAndSerialNumber->length,  			   &iasn, &size, ret);  	free_IssuerAndSerialNumber(&iasn); @@ -268,7 +336,7 @@ cert2epi(hx509_context context, void *ctx, hx509_cert c)      id.subjectKeyIdentifier = NULL; -    p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));  +    p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));      if (p == NULL) {  	free_ExternalPrincipalIdentifier(&id);  	return ENOMEM; @@ -287,25 +355,24 @@ build_edi(krb5_context context,  	  hx509_certs certs,  	  ExternalPrincipalIdentifiers *ids)  { -    return hx509_certs_iter(hx509ctx, certs, cert2epi, ids); +    return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);  }  static krb5_error_code  build_auth_pack(krb5_context context,  		unsigned nonce,  		krb5_pk_init_ctx ctx, -		DH *dh,  		const KDC_REQ_BODY *body,  		AuthPack *a)  { -    size_t buf_size, len; +    size_t buf_size, len = 0;      krb5_error_code ret;      void *buf;      krb5_timestamp sec;      int32_t usec;      Checksum checksum; -    krb5_clear_error_string(context); +    krb5_clear_error_message(context);      memset(&checksum, 0, sizeof(checksum)); @@ -327,12 +394,13 @@ build_auth_pack(krb5_context context,  			       len,  			       &checksum);      free(buf); -    if (ret)  +    if (ret)  	return ret;      ALLOC(a->pkAuthenticator.paChecksum, 1);      if (a->pkAuthenticator.paChecksum == NULL) { -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ENOMEM, +			       N_("malloc: out of memory", ""));  	return ENOMEM;      } @@ -342,25 +410,62 @@ build_auth_pack(krb5_context context,      if (ret)  	return ret; -    if (dh) { -	DomainParameters dp; -	heim_integer dh_pub_key; +    if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) { +	const char *moduli_file; +	unsigned long dh_min_bits;  	krb5_data dhbuf; -	size_t size; +	size_t size = 0; + +	krb5_data_zero(&dhbuf); + + + +	moduli_file = krb5_config_get_string(context, NULL, +					     "libdefaults", +					     "moduli", +					     NULL); + +	dh_min_bits = +	    krb5_config_get_int_default(context, NULL, 0, +					"libdefaults", +					"pkinit_dh_min_bits", +					NULL); + +	ret = _krb5_parse_moduli(context, moduli_file, &ctx->m); +	if (ret) +	    return ret; + +	ctx->u.dh = DH_new(); +	if (ctx->u.dh == NULL) { +	    krb5_set_error_message(context, ENOMEM, +				   N_("malloc: out of memory", "")); +	    return ENOMEM; +	} + +	ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m); +	if (ret) +	    return ret; + +	if (DH_generate_key(ctx->u.dh) != 1) { +	    krb5_set_error_message(context, ENOMEM, +				   N_("pkinit: failed to generate DH key", "")); +	    return ENOMEM; +	} +  	if (1 /* support_cached_dh */) {  	    ALLOC(a->clientDHNonce, 1);  	    if (a->clientDHNonce == NULL) { -		krb5_clear_error_string(context); +		krb5_clear_error_message(context);  		return ENOMEM;  	    }  	    ret = krb5_data_alloc(a->clientDHNonce, 40);  	    if (a->clientDHNonce == NULL) { -		krb5_clear_error_string(context); -		return ENOMEM; +		krb5_clear_error_message(context); +		return ret;  	    } -	    memset(a->clientDHNonce->data, 0, a->clientDHNonce->length); -	    ret = krb5_copy_data(context, a->clientDHNonce,  +	    RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length); +	    ret = krb5_copy_data(context, a->clientDHNonce,  				 &ctx->clientDHNonce);  	    if (ret)  		return ret; @@ -369,60 +474,131 @@ build_auth_pack(krb5_context context,  	ALLOC(a->clientPublicValue, 1);  	if (a->clientPublicValue == NULL)  	    return ENOMEM; -	ret = der_copy_oid(oid_id_dhpublicnumber(), -			   &a->clientPublicValue->algorithm.algorithm); -	if (ret) -	    return ret; -	 -	memset(&dp, 0, sizeof(dp)); -	ret = BN_to_integer(context, dh->p, &dp.p); -	if (ret) { -	    free_DomainParameters(&dp); -	    return ret; -	} -	ret = BN_to_integer(context, dh->g, &dp.g); -	if (ret) { -	    free_DomainParameters(&dp); -	    return ret; -	} -	ret = BN_to_integer(context, dh->q, &dp.q); -	if (ret) { -	    free_DomainParameters(&dp); -	    return ret; -	} -	dp.j = NULL; -	dp.validationParms = NULL; +	if (ctx->keyex == USE_DH) { +	    DH *dh = ctx->u.dh; +	    DomainParameters dp; +	    heim_integer dh_pub_key; + +	    ret = der_copy_oid(&asn1_oid_id_dhpublicnumber, +			       &a->clientPublicValue->algorithm.algorithm); +	    if (ret) +		return ret; + +	    memset(&dp, 0, sizeof(dp)); + +	    ret = BN_to_integer(context, dh->p, &dp.p); +	    if (ret) { +		free_DomainParameters(&dp); +		return ret; +	    } +	    ret = BN_to_integer(context, dh->g, &dp.g); +	    if (ret) { +		free_DomainParameters(&dp); +		return ret; +	    } +	    ret = BN_to_integer(context, dh->q, &dp.q); +	    if (ret) { +		free_DomainParameters(&dp); +		return ret; +	    } +	    dp.j = NULL; +	    dp.validationParms = NULL; -	a->clientPublicValue->algorithm.parameters =  -	    malloc(sizeof(*a->clientPublicValue->algorithm.parameters)); -	if (a->clientPublicValue->algorithm.parameters == NULL) { +	    a->clientPublicValue->algorithm.parameters = +		malloc(sizeof(*a->clientPublicValue->algorithm.parameters)); +	    if (a->clientPublicValue->algorithm.parameters == NULL) { +		free_DomainParameters(&dp); +		return ret; +	    } + +	    ASN1_MALLOC_ENCODE(DomainParameters, +			       a->clientPublicValue->algorithm.parameters->data, +			       a->clientPublicValue->algorithm.parameters->length, +			       &dp, &size, ret);  	    free_DomainParameters(&dp); -	    return ret; -	} +	    if (ret) +		return ret; +	    if (size != a->clientPublicValue->algorithm.parameters->length) +		krb5_abortx(context, "Internal ASN1 encoder error"); -	ASN1_MALLOC_ENCODE(DomainParameters, -			   a->clientPublicValue->algorithm.parameters->data, -			   a->clientPublicValue->algorithm.parameters->length, -			   &dp, &size, ret); -	free_DomainParameters(&dp); -	if (ret) -	    return ret; -	if (size != a->clientPublicValue->algorithm.parameters->length) -	    krb5_abortx(context, "Internal ASN1 encoder error"); +	    ret = BN_to_integer(context, dh->pub_key, &dh_pub_key); +	    if (ret) +		return ret; -	ret = BN_to_integer(context, dh->pub_key, &dh_pub_key); -	if (ret) -	    return ret; +	    ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length, +			       &dh_pub_key, &size, ret); +	    der_free_heim_integer(&dh_pub_key); +	    if (ret) +		return ret; +	    if (size != dhbuf.length) +		krb5_abortx(context, "asn1 internal error"); +	} else if (ctx->keyex == USE_ECDH) { +#ifdef HAVE_OPENSSL +	    ECParameters ecp; +	    unsigned char *p; +	    int xlen; + +	    /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */ + +	    ecp.element = choice_ECParameters_namedCurve; +	    ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1, +			       &ecp.u.namedCurve); +	    if (ret) +		return ret; -	ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length, -			   &dh_pub_key, &size, ret); -	der_free_heim_integer(&dh_pub_key); -	if (ret) -	    return ret; -	if (size != dhbuf.length) -	    krb5_abortx(context, "asn1 internal error"); +	    ALLOC(a->clientPublicValue->algorithm.parameters, 1); +	    if (a->clientPublicValue->algorithm.parameters == NULL) { +		free_ECParameters(&ecp); +		return ENOMEM; +	    } +	    ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret); +	    free_ECParameters(&ecp); +	    if (ret) +		return ret; +	    if ((int)size != xlen) +		krb5_abortx(context, "asn1 internal error"); + +	    a->clientPublicValue->algorithm.parameters->data = p; +	    a->clientPublicValue->algorithm.parameters->length = size; + +	    /* copy in public key */ + +	    ret = der_copy_oid(&asn1_oid_id_ecPublicKey, +			       &a->clientPublicValue->algorithm.algorithm); +	    if (ret) +		return ret; + +	    ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); +	    if (ctx->u.eckey == NULL) +		return ENOMEM; + +	    ret = EC_KEY_generate_key(ctx->u.eckey); +	    if (ret != 1) +		return EINVAL; +	    /* encode onto dhkey */ + +	    xlen = i2o_ECPublicKey(ctx->u.eckey, NULL); +	    if (xlen <= 0) +		abort(); + +	    dhbuf.data = malloc(xlen); +	    if (dhbuf.data == NULL) +		abort(); +	    dhbuf.length = xlen; +	    p = dhbuf.data; + +	    xlen = i2o_ECPublicKey(ctx->u.eckey, &p); +	    if (xlen <= 0) +		abort(); + +	    /* XXX verify that this is right with RFC3279 */ +#else +	    return EINVAL; +#endif +	} else +	    krb5_abortx(context, "internal error");  	a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;  	a->clientPublicValue->subjectPublicKey.data = dhbuf.data;      } @@ -432,7 +608,8 @@ build_auth_pack(krb5_context context,  	if (a->supportedCMSTypes == NULL)  	    return ENOMEM; -	ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL, +	ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL, +				     ctx->id->cert,  				     &a->supportedCMSTypes->val,  				     &a->supportedCMSTypes->len);  	if (ret) @@ -442,9 +619,9 @@ build_auth_pack(krb5_context context,      return ret;  } -krb5_error_code KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  _krb5_pk_mk_ContentInfo(krb5_context context, -			const krb5_data *buf,  +			const krb5_data *buf,  			const heim_oid *oid,  			struct ContentInfo *content_info)  { @@ -473,16 +650,16 @@ pk_mk_padata(krb5_context context,  {      struct ContentInfo content_info;      krb5_error_code ret; -    const heim_oid *oid; -    size_t size; +    const heim_oid *oid = NULL; +    size_t size = 0;      krb5_data buf, sd_buf; -    int pa_type; +    int pa_type = -1;      krb5_data_zero(&buf);      krb5_data_zero(&sd_buf);      memset(&content_info, 0, sizeof(content_info)); -    if (ctx->type == COMPAT_WIN2K) { +    if (ctx->type == PKINIT_WIN2K) {  	AuthPack_Win2k ap;  	krb5_timestamp sec;  	int32_t usec; @@ -493,13 +670,13 @@ pk_mk_padata(krb5_context context,  	ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);  	if (ret) {  	    free_AuthPack_Win2k(&ap); -	    krb5_clear_error_string(context); +	    krb5_clear_error_message(context);  	    goto out;  	}  	ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);  	if (ret) {  	    free_AuthPack_Win2k(&ap); -	    krb5_clear_error_string(context); +	    krb5_clear_error_message(context);  	    goto out;  	} @@ -512,19 +689,21 @@ pk_mk_padata(krb5_context context,  			   &ap, &size, ret);  	free_AuthPack_Win2k(&ap);  	if (ret) { -	    krb5_set_error_string(context, "AuthPack_Win2k: %d", ret); +	    krb5_set_error_message(context, ret, +				   N_("Failed encoding AuthPackWin: %d", ""), +				   (int)ret);  	    goto out;  	}  	if (buf.length != size)  	    krb5_abortx(context, "internal ASN1 encoder error"); -	oid = oid_id_pkcs7_data(); -    } else if (ctx->type == COMPAT_IETF) { +	oid = &asn1_oid_id_pkcs7_data; +    } else if (ctx->type == PKINIT_27) {  	AuthPack ap; -	 +  	memset(&ap, 0, sizeof(ap)); -	ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap); +	ret = build_auth_pack(context, nonce, ctx, req_body, &ap);  	if (ret) {  	    free_AuthPack(&ap);  	    goto out; @@ -533,35 +712,33 @@ pk_mk_padata(krb5_context context,  	ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);  	free_AuthPack(&ap);  	if (ret) { -	    krb5_set_error_string(context, "AuthPack: %d", ret); +	    krb5_set_error_message(context, ret, +				   N_("Failed encoding AuthPack: %d", ""), +				   (int)ret);  	    goto out;  	}  	if (buf.length != size)  	    krb5_abortx(context, "internal ASN1 encoder error"); -	oid = oid_id_pkauthdata(); +	oid = &asn1_oid_id_pkauthdata;      } else  	krb5_abortx(context, "internal pkinit error"); -    ret = _krb5_pk_create_sign(context, -			       oid, -			       &buf, -			       ctx->id, -			       ctx->peer, -			       &sd_buf); +    ret = create_signature(context, oid, &buf, ctx->id, +			   ctx->peer, &sd_buf);      krb5_data_free(&buf);      if (ret)  	goto out; -    ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(), &sd_buf, &buf); +    ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);      krb5_data_free(&sd_buf);      if (ret) { -	krb5_set_error_string(context, -			      "ContentInfo wrapping of signedData failed"); +	krb5_set_error_message(context, ret, +			       N_("ContentInfo wrapping of signedData failed",""));  	goto out;      } -    if (ctx->type == COMPAT_WIN2K) { +    if (ctx->type == PKINIT_WIN2K) {  	PA_PK_AS_REQ_Win2k winreq;  	pa_type = KRB5_PADATA_PK_AS_REQ_WIN; @@ -574,26 +751,30 @@ pk_mk_padata(krb5_context context,  			   &winreq, &size, ret);  	free_PA_PK_AS_REQ_Win2k(&winreq); -    } else if (ctx->type == COMPAT_IETF) { +    } else if (ctx->type == PKINIT_27) {  	PA_PK_AS_REQ req;  	pa_type = KRB5_PADATA_PK_AS_REQ;  	memset(&req, 0, sizeof(req)); -	req.signedAuthPack = buf;	 +	req.signedAuthPack = buf;  	if (ctx->trustedCertifiers) {  	    req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));  	    if (req.trustedCertifiers == NULL) { -		krb5_set_error_string(context, "malloc: out of memory"); +		ret = ENOMEM; +		krb5_set_error_message(context, ret, +				       N_("malloc: out of memory", ""));  		free_PA_PK_AS_REQ(&req);  		goto out;  	    } -	    ret = build_edi(context, ctx->id->hx509ctx,  +	    ret = build_edi(context, context->hx509ctx,  			    ctx->id->anchors, req.trustedCertifiers);  	    if (ret) { -		krb5_set_error_string(context, "pk-init: failed to build trustedCertifiers"); +		krb5_set_error_message(context, ret, +				       N_("pk-init: failed to build " +					  "trustedCertifiers", ""));  		free_PA_PK_AS_REQ(&req);  		goto out;  	    } @@ -608,7 +789,7 @@ pk_mk_padata(krb5_context context,      } else  	krb5_abortx(context, "internal pkinit error");      if (ret) { -	krb5_set_error_string(context, "PA-PK-AS-REQ %d", ret); +	krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);  	goto out;      }      if (buf.length != size) @@ -618,19 +799,21 @@ pk_mk_padata(krb5_context context,      if (ret)  	free(buf.data); -    if (ret == 0 && ctx->type == COMPAT_WIN2K) -	krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0); +    if (ret == 0) +    	krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0); -out: + out:      free_ContentInfo(&content_info);      return ret;  } -krb5_error_code KRB5_LIB_FUNCTION  +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  _krb5_pk_mk_padata(krb5_context context,  		   void *c, +		   int ic_flags, +		   int win2k,  		   const KDC_REQ_BODY *req_body,  		   unsigned nonce,  		   METHOD_DATA *md) @@ -638,33 +821,44 @@ _krb5_pk_mk_padata(krb5_context context,      krb5_pk_init_ctx ctx = c;      int win2k_compat; +    if (ctx->id->certs == NULL && ctx->anonymous == 0) { +	krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY, +			       N_("PKINIT: No user certificate given", "")); +	return HEIM_PKINIT_NO_PRIVATE_KEY; +    } +      win2k_compat = krb5_config_get_bool_default(context, NULL, -						FALSE, +						win2k,  						"realms",  						req_body->realm,  						"pkinit_win2k",  						NULL);      if (win2k_compat) { -	ctx->require_binding =  +	ctx->require_binding =  	    krb5_config_get_bool_default(context, NULL, -					 FALSE, +					 TRUE,  					 "realms",  					 req_body->realm,  					 "pkinit_win2k_require_binding",  					 NULL); -	ctx->type = COMPAT_WIN2K; +	ctx->type = PKINIT_WIN2K;      } else -	ctx->type = COMPAT_IETF; +	ctx->type = PKINIT_27; -    ctx->require_eku =  +    ctx->require_eku =  	krb5_config_get_bool_default(context, NULL,  				     TRUE,  				     "realms",  				     req_body->realm,  				     "pkinit_require_eku",  				     NULL); -    ctx->require_krbtgt_otherName =  +    if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK) +	ctx->require_eku = 0; +    if (ctx->id->flags & PKINIT_BTMM) +	ctx->require_eku = 0; + +    ctx->require_krbtgt_otherName =  	krb5_config_get_bool_default(context, NULL,  				     TRUE,  				     "realms", @@ -672,7 +866,7 @@ _krb5_pk_mk_padata(krb5_context context,  				     "pkinit_require_krbtgt_otherName",  				     NULL); -    ctx->require_hostname_match =  +    ctx->require_hostname_match =  	krb5_config_get_bool_default(context, NULL,  				     FALSE,  				     "realms", @@ -680,7 +874,7 @@ _krb5_pk_mk_padata(krb5_context context,  				     "pkinit_require_hostname_match",  				     NULL); -    ctx->trustedCertifiers =  +    ctx->trustedCertifiers =  	krb5_config_get_bool_default(context, NULL,  				     TRUE,  				     "realms", @@ -691,22 +885,30 @@ _krb5_pk_mk_padata(krb5_context context,      return pk_mk_padata(context, ctx, req_body, nonce, md);  } -krb5_error_code KRB5_LIB_FUNCTION -_krb5_pk_verify_sign(krb5_context context, -		     const void *data, -		     size_t length, -		     struct krb5_pk_identity *id, -		     heim_oid *contentType, -		     krb5_data *content, -		     struct krb5_pk_cert **signer) +static krb5_error_code +pk_verify_sign(krb5_context context, +	       const void *data, +	       size_t length, +	       struct krb5_pk_identity *id, +	       heim_oid *contentType, +	       krb5_data *content, +	       struct krb5_pk_cert **signer)  {      hx509_certs signer_certs; -    int ret; +    int ret, flags = 0; + +    /* BTMM is broken in Leo and SnowLeo */ +    if (id->flags & PKINIT_BTMM) { +	flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; +	flags |= HX509_CMS_VS_NO_KU_CHECK; +	flags |= HX509_CMS_VS_NO_VALIDATE; +    }      *signer = NULL; -    ret = hx509_cms_verify_signed(id->hx509ctx, +    ret = hx509_cms_verify_signed(context->hx509ctx,  				  id->verify_ctx, +				  flags,  				  data,  				  length,  				  NULL, @@ -715,26 +917,26 @@ _krb5_pk_verify_sign(krb5_context context,  				  content,  				  &signer_certs);      if (ret) { -	_krb5_pk_copy_error(context, id->hx509ctx, ret, -			    "CMS verify signed failed"); +	pk_copy_error(context, context->hx509ctx, ret, +		      "CMS verify signed failed");  	return ret;      }      *signer = calloc(1, sizeof(**signer));      if (*signer == NULL) { -	krb5_clear_error_string(context); +	krb5_clear_error_message(context);  	ret = ENOMEM;  	goto out;      } -	 -    ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert); + +    ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);      if (ret) { -	_krb5_pk_copy_error(context, id->hx509ctx, ret, -			    "Failed to get on of the signer certs"); +	pk_copy_error(context, context->hx509ctx, ret, +		      "Failed to get on of the signer certs");  	goto out;      } -out: + out:      hx509_certs_free(&signer_certs);      if (ret) {  	if (*signer) { @@ -762,29 +964,32 @@ get_reply_key_win(krb5_context context,  				    &key_pack,  				    &size);      if (ret) { -	krb5_set_error_string(context, "PKINIT decoding reply key failed"); +	krb5_set_error_message(context, ret, +			       N_("PKINIT decoding reply key failed", ""));  	free_ReplyKeyPack_Win2k(&key_pack);  	return ret;      } -      -    if (key_pack.nonce != nonce) { -	krb5_set_error_string(context, "PKINIT enckey nonce is wrong"); + +    if ((unsigned)key_pack.nonce != nonce) { +	krb5_set_error_message(context, ret, +			       N_("PKINIT enckey nonce is wrong", ""));  	free_ReplyKeyPack_Win2k(&key_pack);  	return KRB5KRB_AP_ERR_MODIFIED;      }      *key = malloc (sizeof (**key));      if (*key == NULL) { -	krb5_set_error_string(context, "PKINIT failed allocating reply key");  	free_ReplyKeyPack_Win2k(&key_pack); -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ENOMEM, +			       N_("malloc: out of memory", ""));  	return ENOMEM;      }      ret = copy_EncryptionKey(&key_pack.replyKey, *key);      free_ReplyKeyPack_Win2k(&key_pack);      if (ret) { -	krb5_set_error_string(context, "PKINIT failed copying reply key"); +	krb5_set_error_message(context, ret, +			       N_("PKINIT failed copying reply key", ""));  	free(*key);  	*key = NULL;      } @@ -807,15 +1012,16 @@ get_reply_key(krb5_context context,  			      &key_pack,  			      &size);      if (ret) { -	krb5_set_error_string(context, "PKINIT decoding reply key failed"); +	krb5_set_error_message(context, ret, +			       N_("PKINIT decoding reply key failed", ""));  	free_ReplyKeyPack(&key_pack);  	return ret;      } -     +      {  	krb5_crypto crypto; -	/*  +	/*  	 * XXX Verify kp.replyKey is a allowed enctype in the  	 * configuration file  	 */ @@ -838,16 +1044,17 @@ get_reply_key(krb5_context context,      *key = malloc (sizeof (**key));      if (*key == NULL) { -	krb5_set_error_string(context, "PKINIT failed allocating reply key");  	free_ReplyKeyPack(&key_pack); -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ENOMEM, +			       N_("malloc: out of memory", ""));  	return ENOMEM;      }      ret = copy_EncryptionKey(&key_pack.replyKey, *key);      free_ReplyKeyPack(&key_pack);      if (ret) { -	krb5_set_error_string(context, "PKINIT failed copying reply key"); +	krb5_set_error_message(context, ret, +			       N_("PKINIT failed copying reply key", ""));  	free(*key);  	*key = NULL;      } @@ -866,24 +1073,27 @@ pk_verify_host(krb5_context context,      krb5_error_code ret = 0;      if (ctx->require_eku) { -	ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert, -				   oid_id_pkkdcekuoid(), 0); +	ret = hx509_cert_check_eku(context->hx509ctx, host->cert, +				   &asn1_oid_id_pkkdcekuoid, 0);  	if (ret) { -	    krb5_set_error_string(context, "No PK-INIT KDC EKU in kdc certificate"); +	    krb5_set_error_message(context, ret, +				   N_("No PK-INIT KDC EKU in kdc certificate", ""));  	    return ret;  	}      }      if (ctx->require_krbtgt_otherName) {  	hx509_octet_string_list list; -	int i; +	size_t i; -	ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx, +	ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,  						       host->cert, -						       oid_id_pkinit_san(), +						       &asn1_oid_id_pkinit_san,  						       &list);  	if (ret) { -	    krb5_set_error_string(context, "Failed to find the PK-INIT " -				  "subjectAltName in the KDC certificate"); +	    krb5_set_error_message(context, ret, +				   N_("Failed to find the PK-INIT " +				      "subjectAltName in the KDC " +				      "certificate", ""));  	    return ret;  	} @@ -896,8 +1106,10 @@ pk_verify_host(krb5_context context,  					   &r,  					   NULL);  	    if (ret) { -		krb5_set_error_string(context, "Failed to decode the PK-INIT " -				      "subjectAltName in the KDC certificate"); +		krb5_set_error_message(context, ret, +				       N_("Failed to decode the PK-INIT " +					  "subjectAltName in the " +					  "KDC certificate", ""));  		break;  	    } @@ -906,11 +1118,12 @@ pk_verify_host(krb5_context context,  		strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||  		strcmp(r.principalName.name_string.val[1], realm) != 0 ||  		strcmp(r.realm, realm) != 0) -	    { -		krb5_set_error_string(context, "KDC have wrong realm name in " -				      "the certificate"); -		ret = KRB5_KDC_ERR_INVALID_CERTIFICATE; -	    } +		{ +		    ret = KRB5_KDC_ERR_INVALID_CERTIFICATE; +		    krb5_set_error_message(context, ret, +					   N_("KDC have wrong realm name in " +					      "the certificate", "")); +		}  	    free_KRB5PrincipalName(&r);  	    if (ret) @@ -920,17 +1133,18 @@ pk_verify_host(krb5_context context,      }      if (ret)  	return ret; -     +      if (hi) { -	ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,  +	ret = hx509_verify_hostname(context->hx509ctx, host->cert,  				    ctx->require_hostname_match,  				    HX509_HN_HOSTNAME,  				    hi->hostname,  				    hi->ai->ai_addr, hi->ai->ai_addrlen);  	if (ret) -	    krb5_set_error_string(context, "Address mismatch in " -				  "the KDC certificate"); +	    krb5_set_error_message(context, ret, +				   N_("Address mismatch in " +				      "the KDC certificate", ""));      }      return ret;  } @@ -947,81 +1161,91 @@ pk_rd_pa_reply_enckey(krb5_context context,  	       	      unsigned nonce,  		      const krb5_data *req_buffer,  	       	      PA_DATA *pa, -	       	      krb5_keyblock **key)  +	       	      krb5_keyblock **key)  {      krb5_error_code ret;      struct krb5_pk_cert *host = NULL;      krb5_data content;      heim_oid contentType = { 0, NULL }; +    int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT; -    if (der_heim_oid_cmp(oid_id_pkcs7_envelopedData(), dataType)) { -	krb5_set_error_string(context, "PKINIT: Invalid content type"); +    if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) { +	krb5_set_error_message(context, EINVAL, +			       N_("PKINIT: Invalid content type", ""));  	return EINVAL;      } -    ret = hx509_cms_unenvelope(ctx->id->hx509ctx, +    if (ctx->type == PKINIT_WIN2K) +	flags |= HX509_CMS_UE_ALLOW_WEAK; + +    ret = hx509_cms_unenvelope(context->hx509ctx,  			       ctx->id->certs, -			       HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT, +			       flags,  			       indata->data,  			       indata->length,  			       NULL, +			       0,  			       &contentType,  			       &content);      if (ret) { -	_krb5_pk_copy_error(context, ctx->id->hx509ctx, ret, -			    "Failed to unenvelope CMS data in PK-INIT reply"); +	pk_copy_error(context, context->hx509ctx, ret, +		      "Failed to unenvelope CMS data in PK-INIT reply");  	return ret;      }      der_free_oid(&contentType); -#if 0 /* windows LH with interesting CMS packets, leaks memory */ -    { -	size_t ph = 1 + der_length_len (length); -	unsigned char *ptr = malloc(length + ph); -	size_t l; +    /* win2k uses ContentInfo */ +    if (type == PKINIT_WIN2K) { +	heim_oid type2; +	heim_octet_string out; -	memcpy(ptr + ph, p, length); +	ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL); +	if (ret) { +	    /* windows LH with interesting CMS packets */ +	    size_t ph = 1 + der_length_len(content.length); +	    unsigned char *ptr = malloc(content.length + ph); +	    size_t l; -	ret = der_put_length_and_tag (ptr + ph - 1, ph, length, -				      ASN1_C_UNIV, CONS, UT_Sequence, &l); -	if (ret) -	    return ret; -	ptr += ph - l; -	length += l; -	p = ptr; -    } -#endif +	    memcpy(ptr + ph, content.data, content.length); -    /* win2k uses ContentInfo */ -    if (type == COMPAT_WIN2K) { -	heim_oid type; -	heim_octet_string out; +	    ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length, +					  ASN1_C_UNIV, CONS, UT_Sequence, &l); +	    if (ret) +		return ret; +	    free(content.data); +	    content.data = ptr; +	    content.length += ph; -	ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL); -	if (der_heim_oid_cmp(&type, oid_id_pkcs7_signedData())) { +	    ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL); +	    if (ret) +		goto out; +	} +	if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {  	    ret = EINVAL; /* XXX */ -	    krb5_set_error_string(context, "PKINIT: Invalid content type"); -	    der_free_oid(&type); +	    krb5_set_error_message(context, ret, +				   N_("PKINIT: Invalid content type", "")); +	    der_free_oid(&type2);  	    der_free_octet_string(&out);  	    goto out;  	} -	der_free_oid(&type); +	der_free_oid(&type2);  	krb5_data_free(&content);  	ret = krb5_data_copy(&content, out.data, out.length);  	der_free_octet_string(&out);  	if (ret) { -	    krb5_set_error_string(context, "PKINIT: out of memory"); +	    krb5_set_error_message(context, ret, +				   N_("malloc: out of memory", ""));  	    goto out;  	}      } -    ret = _krb5_pk_verify_sign(context,  -			       content.data, -			       content.length, -			       ctx->id, -			       &contentType, -			       &content, -			       &host); +    ret = pk_verify_sign(context, +			 content.data, +			 content.length, +			 ctx->id, +			 &contentType, +			 &content, +			 &host);      if (ret)  	goto out; @@ -1032,28 +1256,28 @@ pk_rd_pa_reply_enckey(krb5_context context,      }  #if 0 -    if (type == COMPAT_WIN2K) { -	if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) { -	    krb5_set_error_string(context, "PKINIT: reply key, wrong oid"); +    if (type == PKINIT_WIN2K) { +	if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {  	    ret = KRB5KRB_AP_ERR_MSG_TYPE; +	    krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");  	    goto out;  	}      } else { -	if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) { -	    krb5_set_error_string(context, "PKINIT: reply key, wrong oid"); +	if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {  	    ret = KRB5KRB_AP_ERR_MSG_TYPE; +	    krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");  	    goto out;  	}      }  #endif      switch(type) { -    case COMPAT_WIN2K: +    case PKINIT_WIN2K:  	ret = get_reply_key(context, &content, req_buffer, key);  	if (ret != 0 && ctx->require_binding == 0)  	    ret = get_reply_key_win(context, &content, nonce, key);  	break; -    case COMPAT_IETF: +    case PKINIT_27:  	ret = get_reply_key(context, &content, req_buffer, key);  	break;      } @@ -1085,31 +1309,33 @@ pk_rd_pa_reply_dh(krb5_context context,                    PA_DATA *pa,                    krb5_keyblock **key)  { -    unsigned char *p, *dh_gen_key = NULL; +    const unsigned char *p; +    unsigned char *dh_gen_key = NULL;      struct krb5_pk_cert *host = NULL;      BIGNUM *kdc_dh_pubkey = NULL;      KDCDHKeyInfo kdc_dh_info;      heim_oid contentType = { 0, NULL };      krb5_data content;      krb5_error_code ret; -    int dh_gen_keylen; +    int dh_gen_keylen = 0;      size_t size;      krb5_data_zero(&content);      memset(&kdc_dh_info, 0, sizeof(kdc_dh_info)); -    if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), dataType)) { -	krb5_set_error_string(context, "PKINIT: Invalid content type"); +    if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) { +	krb5_set_error_message(context, EINVAL, +			       N_("PKINIT: Invalid content type", ""));  	return EINVAL;      } -    ret = _krb5_pk_verify_sign(context,  -			       indata->data, -			       indata->length, -			       ctx->id, -			       &contentType, -			       &content, -			       &host); +    ret = pk_verify_sign(context, +			 indata->data, +			 indata->length, +			 ctx->id, +			 &contentType, +			 &content, +			 &host);      if (ret)  	goto out; @@ -1118,9 +1344,10 @@ pk_rd_pa_reply_dh(krb5_context context,      if (ret)  	goto out; -    if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) { -	krb5_set_error_string(context, "pkinit - dh reply contains wrong oid"); +    if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {  	ret = KRB5KRB_AP_ERR_MSG_TYPE; +	krb5_set_error_message(context, ret, +			       N_("pkinit - dh reply contains wrong oid", ""));  	goto out;      } @@ -1130,35 +1357,40 @@ pk_rd_pa_reply_dh(krb5_context context,  			      &size);      if (ret) { -	krb5_set_error_string(context, "pkinit - " -			      "failed to decode KDC DH Key Info"); +	krb5_set_error_message(context, ret, +			       N_("pkinit - failed to decode " +				  "KDC DH Key Info", ""));  	goto out;      }      if (kdc_dh_info.nonce != nonce) { -	krb5_set_error_string(context, "PKINIT: DH nonce is wrong");  	ret = KRB5KRB_AP_ERR_MODIFIED; +	krb5_set_error_message(context, ret, +			       N_("PKINIT: DH nonce is wrong", ""));  	goto out;      }      if (kdc_dh_info.dhKeyExpiration) {  	if (k_n == NULL) { -	    krb5_set_error_string(context, "pkinit; got key expiration " -				  "without server nonce");  	    ret = KRB5KRB_ERR_GENERIC; +	    krb5_set_error_message(context, ret, +				   N_("pkinit; got key expiration " +				      "without server nonce", ""));  	    goto out;  	}  	if (c_n == NULL) { -	    krb5_set_error_string(context, "pkinit; got DH reuse but no " -				  "client nonce");  	    ret = KRB5KRB_ERR_GENERIC; +	    krb5_set_error_message(context, ret, +				   N_("pkinit; got DH reuse but no " +				      "client nonce", ""));  	    goto out;  	}      } else {  	if (k_n) { -	    krb5_set_error_string(context, "pkinit: got server nonce " -				  "without key expiration");  	    ret = KRB5KRB_ERR_GENERIC; +	    krb5_set_error_message(context, ret, +				   N_("pkinit: got server nonce " +				      "without key expiration", ""));  	    goto out;  	}  	c_n = NULL; @@ -1168,49 +1400,110 @@ pk_rd_pa_reply_dh(krb5_context context,      p = kdc_dh_info.subjectPublicKey.data;      size = (kdc_dh_info.subjectPublicKey.length + 7) / 8; -    { +    if (ctx->keyex == USE_DH) {  	DHPublicKey k;  	ret = decode_DHPublicKey(p, size, &k, NULL);  	if (ret) { -	    krb5_set_error_string(context, "pkinit: can't decode " -				  "without key expiration"); +	    krb5_set_error_message(context, ret, +				   N_("pkinit: can't decode " +				      "without key expiration", ""));  	    goto out;  	}  	kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);  	free_DHPublicKey(&k);  	if (kdc_dh_pubkey == NULL) { +	    ret = ENOMEM; +	    goto out; +	} + + +	size = DH_size(ctx->u.dh); + +	dh_gen_key = malloc(size); +	if (dh_gen_key == NULL) { +	    ret = ENOMEM; +	    krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); +	    goto out; +	} + +	dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh); +	if (dh_gen_keylen == -1) {  	    ret = KRB5KRB_ERR_GENERIC; +	    dh_gen_keylen = 0; +	    krb5_set_error_message(context, ret, +				   N_("PKINIT: Can't compute Diffie-Hellman key", ""));  	    goto out;  	} -    } -     -    dh_gen_keylen = DH_size(ctx->dh); -    size = BN_num_bytes(ctx->dh->p); -    if (size < dh_gen_keylen) -	size = dh_gen_keylen; +	if (dh_gen_keylen < (int)size) { +	    size -= dh_gen_keylen; +	    memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen); +	    memset(dh_gen_key, 0, size); +	} -    dh_gen_key = malloc(size); -    if (dh_gen_key == NULL) { -	krb5_set_error_string(context, "malloc: out of memory"); -	ret = ENOMEM; -	goto out; +    } else { +#ifdef HAVE_OPENSSL +	const EC_GROUP *group; +	EC_KEY *public = NULL; + +	group = EC_KEY_get0_group(ctx->u.eckey); + +	public = EC_KEY_new(); +	if (public == NULL) { +	    ret = ENOMEM; +	    goto out; +	} +	if (EC_KEY_set_group(public, group) != 1) { +	    EC_KEY_free(public); +	    ret = ENOMEM; +	    goto out; +	} + +	if (o2i_ECPublicKey(&public, &p, size) == NULL) { +	    EC_KEY_free(public); +	    ret = KRB5KRB_ERR_GENERIC; +	    krb5_set_error_message(context, ret, +				   N_("PKINIT: Can't parse ECDH public key", "")); +	    goto out; +	} + +	size = (EC_GROUP_get_degree(group) + 7) / 8; +	dh_gen_key = malloc(size); +	if (dh_gen_key == NULL) { +	    EC_KEY_free(public); +	    ret = ENOMEM; +	    krb5_set_error_message(context, ret, +				   N_("malloc: out of memory", "")); +	    goto out; +	} +	dh_gen_keylen = ECDH_compute_key(dh_gen_key, size, +					 EC_KEY_get0_public_key(public), ctx->u.eckey, NULL); +	EC_KEY_free(public); +	if (dh_gen_keylen == -1) { +	    ret = KRB5KRB_ERR_GENERIC; +	    dh_gen_keylen = 0; +	    krb5_set_error_message(context, ret, +				   N_("PKINIT: Can't compute ECDH public key", "")); +	    goto out; +	} +#else +	ret = EINVAL; +#endif      } -    memset(dh_gen_key, 0, size - dh_gen_keylen); -    dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen), -				   kdc_dh_pubkey, ctx->dh); -    if (dh_gen_keylen == -1) { -	krb5_set_error_string(context,  -			      "PKINIT: Can't compute Diffie-Hellman key"); -	ret = KRB5KRB_ERR_GENERIC; +    if (dh_gen_keylen <= 0) { +	ret = EINVAL; +	krb5_set_error_message(context, ret, +			       N_("PKINIT: resulting DH key <= 0", "")); +	dh_gen_keylen = 0;  	goto out;      }      *key = malloc (sizeof (**key));      if (*key == NULL) { -	krb5_set_error_string(context, "malloc: out of memory");  	ret = ENOMEM; +	krb5_set_error_message(context, ret, +			       N_("malloc: out of memory", ""));  	goto out;      } @@ -1220,8 +1513,8 @@ pk_rd_pa_reply_dh(krb5_context context,  				   c_n, k_n,  				   *key);      if (ret) { -	krb5_set_error_string(context, -			      "PKINIT: can't create key from DH key"); +	krb5_set_error_message(context, ret, +			       N_("PKINIT: can't create key from DH key", ""));  	free(*key);  	*key = NULL;  	goto out; @@ -1231,7 +1524,7 @@ pk_rd_pa_reply_dh(krb5_context context,      if (kdc_dh_pubkey)  	BN_free(kdc_dh_pubkey);      if (dh_gen_key) { -	memset(dh_gen_key, 0, DH_size(ctx->dh)); +	memset(dh_gen_key, 0, dh_gen_keylen);  	free(dh_gen_key);      }      if (host) @@ -1244,7 +1537,7 @@ pk_rd_pa_reply_dh(krb5_context context,      return ret;  } -krb5_error_code KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  _krb5_pk_rd_pa_reply(krb5_context context,  		     const char *realm,  		     void *c, @@ -1260,13 +1553,14 @@ _krb5_pk_rd_pa_reply(krb5_context context,      size_t size;      /* Check for IETF PK-INIT first */ -    if (ctx->type == COMPAT_IETF) { +    if (ctx->type == PKINIT_27) {  	PA_PK_AS_REP rep;  	heim_octet_string os, data;  	heim_oid oid; -	 +  	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) { -	    krb5_set_error_string(context, "PKINIT: wrong padata recv"); +	    krb5_set_error_message(context, EINVAL, +				   N_("PKINIT: wrong padata recv", ""));  	    return EINVAL;  	} @@ -1275,28 +1569,65 @@ _krb5_pk_rd_pa_reply(krb5_context context,  				  &rep,  				  &size);  	if (ret) { -	    krb5_set_error_string(context, "Failed to decode pkinit AS rep"); +	    krb5_set_error_message(context, ret, +				   N_("Failed to decode pkinit AS rep", ""));  	    return ret;  	}  	switch (rep.element) {  	case choice_PA_PK_AS_REP_dhInfo: +	    _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");  	    os = rep.u.dhInfo.dhSignedData;  	    break;  	case choice_PA_PK_AS_REP_encKeyPack: +	    _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");  	    os = rep.u.encKeyPack;  	    break; -	default: +	default: { +	    PA_PK_AS_REP_BTMM btmm;  	    free_PA_PK_AS_REP(&rep); -	    krb5_set_error_string(context, "PKINIT: -27 reply " -				  "invalid content type"); -	    return EINVAL; +	    memset(&rep, 0, sizeof(rep)); + +	    _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key"); + +	    ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data, +					   pa->padata_value.length, +					   &btmm, +					   &size); +	    if (ret) { +		krb5_set_error_message(context, EINVAL, +				       N_("PKINIT: -27 reply " +					  "invalid content type", "")); +		return EINVAL; +	    } + +	    if (btmm.dhSignedData || btmm.encKeyPack == NULL) { +		free_PA_PK_AS_REP_BTMM(&btmm); +		ret = EINVAL; +		krb5_set_error_message(context, ret, +				       N_("DH mode not supported for BTMM mode", "")); +		return ret; +	    } + +	    /* +	     * Transform to IETF style PK-INIT reply so that free works below +	     */ + +	    rep.element = choice_PA_PK_AS_REP_encKeyPack; +	    rep.u.encKeyPack.data = btmm.encKeyPack->data; +	    rep.u.encKeyPack.length = btmm.encKeyPack->length; +	    btmm.encKeyPack->data = NULL; +	    btmm.encKeyPack->length = 0; +	    free_PA_PK_AS_REP_BTMM(&btmm); +	    os = rep.u.encKeyPack; +	}  	}  	ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);  	if (ret) {  	    free_PA_PK_AS_REP(&rep); -	    krb5_set_error_string(context, "PKINIT: failed to unwrap CI"); +	    krb5_set_error_message(context, ret, +				   N_("PKINIT: failed to unwrap CI", ""));  	    return ret;  	} @@ -1308,7 +1639,7 @@ _krb5_pk_rd_pa_reply(krb5_context context,  				    nonce, pa, key);  	    break;  	case choice_PA_PK_AS_REP_encKeyPack: -	    ret = pk_rd_pa_reply_enckey(context, COMPAT_IETF, &data, &oid, realm,  +	    ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,  					ctx, etype, hi, nonce, req_buffer, pa, key);  	    break;  	default: @@ -1318,46 +1649,49 @@ _krb5_pk_rd_pa_reply(krb5_context context,  	der_free_oid(&oid);  	free_PA_PK_AS_REP(&rep); -    } else if (ctx->type == COMPAT_WIN2K) { +    } else if (ctx->type == PKINIT_WIN2K) {  	PA_PK_AS_REP_Win2k w2krep; -	/* Check for Windows encoding of the AS-REP pa data */  +	/* Check for Windows encoding of the AS-REP pa data */  #if 0 /* should this be ? */  	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) { -	    krb5_set_error_string(context, "PKINIT: wrong padata recv"); +	    krb5_set_error_message(context, EINVAL, +				   "PKINIT: wrong padata recv");  	    return EINVAL;  	}  #endif  	memset(&w2krep, 0, sizeof(w2krep)); -	 +  	ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,  					pa->padata_value.length,  					&w2krep,  					&size);  	if (ret) { -	    krb5_set_error_string(context, "PKINIT: Failed decoding windows " -				  "pkinit reply %d", ret); +	    krb5_set_error_message(context, ret, +				   N_("PKINIT: Failed decoding windows " +				      "pkinit reply %d", ""), (int)ret);  	    return ret;  	} -	krb5_clear_error_string(context); -	 +	krb5_clear_error_message(context); +  	switch (w2krep.element) {  	case choice_PA_PK_AS_REP_Win2k_encKeyPack: {  	    heim_octet_string data;  	    heim_oid oid; -	     -	    ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,  + +	    ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,  					       &oid, &data, NULL);  	    free_PA_PK_AS_REP_Win2k(&w2krep);  	    if (ret) { -		krb5_set_error_string(context, "PKINIT: failed to unwrap CI"); +		krb5_set_error_message(context, ret, +				       N_("PKINIT: failed to unwrap CI", ""));  		return ret;  	    } -	    ret = pk_rd_pa_reply_enckey(context, COMPAT_WIN2K, &data, &oid, realm, +	    ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,  					ctx, etype, hi, nonce, req_buffer, pa, key);  	    der_free_octet_string(&data);  	    der_free_oid(&oid); @@ -1366,15 +1700,17 @@ _krb5_pk_rd_pa_reply(krb5_context context,  	}  	default:  	    free_PA_PK_AS_REP_Win2k(&w2krep); -	    krb5_set_error_string(context, "PKINIT: win2k reply invalid " -				  "content type");  	    ret = EINVAL; +	    krb5_set_error_message(context, ret, +				   N_("PKINIT: win2k reply invalid " +				      "content type", ""));  	    break;  	} -     +      } else { -	krb5_set_error_string(context, "PKINIT: unknown reply type");  	ret = EINVAL; +	krb5_set_error_message(context, ret, +			       N_("PKINIT: unknown reply type", ""));      }      return ret; @@ -1386,14 +1722,14 @@ struct prompter {      void *prompter_data;  }; -static int  +static int  hx_pass_prompter(void *data, const hx509_prompt *prompter)  {      krb5_error_code ret;      krb5_prompt prompt;      krb5_data password_data;      struct prompter *p = data; -    +      password_data.data   = prompter->reply.data;      password_data.length = prompter->reply.length; @@ -1410,8 +1746,8 @@ hx_pass_prompter(void *data, const hx509_prompt *prompter)      default:  	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;  	break; -    }	 -    +    } +      ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);      if (ret) {  	memset (prompter->reply.data, 0, prompter->reply.length); @@ -1420,16 +1756,80 @@ hx_pass_prompter(void *data, const hx509_prompt *prompter)      return 0;  } - -void KRB5_LIB_FUNCTION -_krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id, -				 int boolean) +static krb5_error_code +_krb5_pk_set_user_id(krb5_context context, +		     krb5_principal principal, +		     krb5_pk_init_ctx ctx, +		     struct hx509_certs_data *certs)  { -    hx509_verify_set_proxy_certificate(id->verify_ctx, boolean); -} +    hx509_certs c = hx509_certs_ref(certs); +    hx509_query *q = NULL; +    int ret; +    if (ctx->id->certs) +	hx509_certs_free(&ctx->id->certs); +    if (ctx->id->cert) { +	hx509_cert_free(ctx->id->cert); +	ctx->id->cert = NULL; +    } -krb5_error_code KRB5_LIB_FUNCTION +    ctx->id->certs = c; +    ctx->anonymous = 0; + +    ret = hx509_query_alloc(context->hx509ctx, &q); +    if (ret) { +	pk_copy_error(context, context->hx509ctx, ret, +		      "Allocate query to find signing certificate"); +	return ret; +    } + +    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); +    hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); + +    if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) { +	ctx->id->flags |= PKINIT_BTMM; +    } + +    ret = find_cert(context, ctx->id, q, &ctx->id->cert); +    hx509_query_free(context->hx509ctx, q); + +    if (ret == 0 && _krb5_have_debug(context, 2)) { +	hx509_name name; +	char *str, *sn; +	heim_integer i; + +	ret = hx509_cert_get_subject(ctx->id->cert, &name); +	if (ret) +	    goto out; + +	ret = hx509_name_to_string(name, &str); +	hx509_name_free(&name); +	if (ret) +	    goto out; + +	ret = hx509_cert_get_serialnumber(ctx->id->cert, &i); +	if (ret) { +	    free(str); +	    goto out; +	} + +	ret = der_print_hex_heim_integer(&i, &sn); +	der_free_heim_integer(&i); +	if (ret) { +	    free(name); +	    goto out; +	} + +	_krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn); +	free(str); +	free(sn); +    } + out: + +    return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  _krb5_pk_load_id(krb5_context context,  		 struct krb5_pk_identity **ret_id,  		 const char *user_id, @@ -1441,190 +1841,187 @@ _krb5_pk_load_id(krb5_context context,  		 char *password)  {      struct krb5_pk_identity *id = NULL; -    hx509_lock lock = NULL;      struct prompter p;      int ret;      *ret_id = NULL;      if (anchor_id == NULL) { -	krb5_set_error_string(context, "PKINIT: No anchor given"); +	krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA, +			       N_("PKINIT: No anchor given", ""));  	return HEIM_PKINIT_NO_VALID_CA;      } -    if (user_id == NULL) { -	krb5_set_error_string(context, -			      "PKINIT: No user certificate given"); -	return HEIM_PKINIT_NO_PRIVATE_KEY; -    } -      /* load cert */      id = calloc(1, sizeof(*id));      if (id == NULL) { -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ENOMEM, +			       N_("malloc: out of memory", ""));  	return ENOMEM; -    }	 +    } -    ret = hx509_context_init(&id->hx509ctx); -    if (ret) -	goto out; +    if (user_id) { +	hx509_lock lock; -    ret = hx509_lock_init(id->hx509ctx, &lock); -    if (password && password[0]) -	hx509_lock_add_password(lock, password); +	ret = hx509_lock_init(context->hx509ctx, &lock); +	if (ret) { +	    pk_copy_error(context, context->hx509ctx, ret, "Failed init lock"); +	    goto out; +	} -    if (prompter) { -	p.context = context; -	p.prompter = prompter; -	p.prompter_data = prompter_data; +	if (password && password[0]) +	    hx509_lock_add_password(lock, password); -	ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p); -	if (ret) -	    goto out; -    } +	if (prompter) { +	    p.context = context; +	    p.prompter = prompter; +	    p.prompter_data = prompter_data; -    ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs); -    if (ret) { -	_krb5_pk_copy_error(context, id->hx509ctx, ret, -			    "Failed to init cert certs"); -	goto out; +	    ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p); +	    if (ret) { +		hx509_lock_free(lock); +		goto out; +	    } +	} + +	ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs); +        hx509_lock_free(lock); +	if (ret) { +	    pk_copy_error(context, context->hx509ctx, ret, +			  "Failed to init cert certs"); +	    goto out; +	} +    } else { +	id->certs = NULL;      } -    ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors); +    ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);      if (ret) { -	_krb5_pk_copy_error(context, id->hx509ctx, ret, -			    "Failed to init anchors"); +	pk_copy_error(context, context->hx509ctx, ret, +		      "Failed to init anchors");  	goto out;      } -    ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",  +    ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",  			   0, NULL, &id->certpool);      if (ret) { -	_krb5_pk_copy_error(context, id->hx509ctx, ret, -			    "Failed to init chain"); +	pk_copy_error(context, context->hx509ctx, ret, +		      "Failed to init chain");  	goto out;      }      while (chain_list && *chain_list) { -	ret = hx509_certs_append(id->hx509ctx, id->certpool, +	ret = hx509_certs_append(context->hx509ctx, id->certpool,  				 NULL, *chain_list);  	if (ret) { -	    _krb5_pk_copy_error(context, id->hx509ctx, ret, -				"Failed to laod chain %s", -				*chain_list); +	    pk_copy_error(context, context->hx509ctx, ret, +			  "Failed to laod chain %s", +			  *chain_list);  	    goto out;  	}  	chain_list++;      }      if (revoke_list) { -	ret = hx509_revoke_init(id->hx509ctx, &id->revokectx); +	ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);  	if (ret) { -	    _krb5_pk_copy_error(context, id->hx509ctx, ret, -				"Failed init revoke list"); +	    pk_copy_error(context, context->hx509ctx, ret, +			  "Failed init revoke list");  	    goto out;  	}  	while (*revoke_list) { -	    ret = hx509_revoke_add_crl(id->hx509ctx,  +	    ret = hx509_revoke_add_crl(context->hx509ctx,  				       id->revokectx,  				       *revoke_list);  	    if (ret) { -		_krb5_pk_copy_error(context, id->hx509ctx, ret,  -				    "Failed load revoke list"); +		pk_copy_error(context, context->hx509ctx, ret, +			      "Failed load revoke list");  		goto out;  	    }  	    revoke_list++;  	}      } else -	hx509_context_set_missing_revoke(id->hx509ctx, 1); +	hx509_context_set_missing_revoke(context->hx509ctx, 1); -    ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx); +    ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);      if (ret) { -	_krb5_pk_copy_error(context, id->hx509ctx, ret,  -			    "Failed init verify context"); +	pk_copy_error(context, context->hx509ctx, ret, +		      "Failed init verify context");  	goto out;      }      hx509_verify_attach_anchors(id->verify_ctx, id->anchors);      hx509_verify_attach_revoke(id->verify_ctx, id->revokectx); -out: + out:      if (ret) {  	hx509_verify_destroy_ctx(id->verify_ctx);  	hx509_certs_free(&id->certs);  	hx509_certs_free(&id->anchors);  	hx509_certs_free(&id->certpool);  	hx509_revoke_free(&id->revokectx); -	hx509_context_free(&id->hx509ctx);  	free(id);      } else  	*ret_id = id; -    hx509_lock_free(lock); -      return ret;  } -static krb5_error_code -select_dh_group(krb5_context context, DH *dh, unsigned long bits,  -		struct krb5_dh_moduli **moduli) +/* + * + */ + +static void +pk_copy_error(krb5_context context, +	      hx509_context hx509ctx, +	      int hxret, +	      const char *fmt, +	      ...)  { -    const struct krb5_dh_moduli *m; +    va_list va; +    char *s, *f; +    int ret; -    if (bits == 0) { -	m = moduli[1]; /* XXX */ -	if (m == NULL) -	    m = moduli[0]; /* XXX */ -    } else { -	int i; -	for (i = 0; moduli[i] != NULL; i++) { -	    if (bits < moduli[i]->bits) -		break; -	} -	if (moduli[i] == NULL) { -	    krb5_set_error_string(context,  -				  "Did not find a DH group parameter " -				  "matching requirement of %lu bits", -				  bits); -	    return EINVAL; -	} -	m = moduli[i]; +    va_start(va, fmt); +    ret = vasprintf(&f, fmt, va); +    va_end(va); +    if (ret == -1 || f == NULL) { +	krb5_clear_error_message(context); +	return;      } -    dh->p = integer_to_BN(context, "p", &m->p); -    if (dh->p == NULL) -	return ENOMEM; -    dh->g = integer_to_BN(context, "g", &m->g); -    if (dh->g == NULL) -	return ENOMEM; -    dh->q = integer_to_BN(context, "q", &m->q); -    if (dh->q == NULL) -	return ENOMEM; - -    return 0; +    s = hx509_get_error_string(hx509ctx, hxret); +    if (s == NULL) { +	krb5_clear_error_message(context); +	free(f); +	return; +    } +    krb5_set_error_message(context, hxret, "%s: %s", f, s); +    free(s); +    free(f);  } -#endif /* PKINIT */ -  static int -parse_integer(krb5_context context, char **p, const char *file, int lineno,  +parse_integer(krb5_context context, char **p, const char *file, int lineno,  	      const char *name, heim_integer *integer)  {      int ret;      char *p1;      p1 = strsep(p, " \t");      if (p1 == NULL) { -	krb5_set_error_string(context, "moduli file %s missing %s on line %d", -			      file, name, lineno); +	krb5_set_error_message(context, EINVAL, +			       N_("moduli file %s missing %s on line %d", ""), +			       file, name, lineno);  	return EINVAL;      }      ret = der_parse_hex_heim_integer(p1, integer);      if (ret) { -	krb5_set_error_string(context, "moduli file %s failed parsing %s " -			      "on line %d", -			      file, name, lineno); +	krb5_set_error_message(context, ret, +			       N_("moduli file %s failed parsing %s " +				  "on line %d", ""), +			       file, name, lineno);  	return ret;      } @@ -1632,7 +2029,7 @@ parse_integer(krb5_context context, char **p, const char *file, int lineno,  }  krb5_error_code -_krb5_parse_moduli_line(krb5_context context,  +_krb5_parse_moduli_line(krb5_context context,  			const char *file,  			int lineno,  			char *p, @@ -1646,43 +2043,49 @@ _krb5_parse_moduli_line(krb5_context context,      m1 = calloc(1, sizeof(*m1));      if (m1 == NULL) { -	krb5_set_error_string(context, "malloc - out of memory"); +	krb5_set_error_message(context, ENOMEM, +			       N_("malloc: out of memory", ""));  	return ENOMEM;      }      while (isspace((unsigned char)*p))  	p++; -    if (*p  == '#') +    if (*p  == '#') { +        free(m1);  	return 0; +    }      ret = EINVAL;      p1 = strsep(&p, " \t");      if (p1 == NULL) { -	krb5_set_error_string(context, "moduli file %s missing name " -			      "on line %d", file, lineno); +	krb5_set_error_message(context, ret, +			       N_("moduli file %s missing name on line %d", ""), +			       file, lineno);  	goto out;      }      m1->name = strdup(p1); -    if (p1 == NULL) { -	krb5_set_error_string(context, "malloc - out of memeory"); +    if (m1->name == NULL) {  	ret = ENOMEM; +	krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));  	goto out;      }      p1 = strsep(&p, " \t");      if (p1 == NULL) { -	krb5_set_error_string(context, "moduli file %s missing bits on line %d", -			      file, lineno); +	krb5_set_error_message(context, ret, +			       N_("moduli file %s missing bits on line %d", ""), +			       file, lineno);  	goto out;      }      m1->bits = atoi(p1);      if (m1->bits == 0) { -	krb5_set_error_string(context, "moduli file %s have un-parsable " -			      "bits on line %d", file, lineno); +	krb5_set_error_message(context, ret, +			       N_("moduli file %s have un-parsable " +				  "bits on line %d", ""), file, lineno);  	goto out;      } -	 +      ret = parse_integer(context, &p, file, lineno, "p", &m1->p);      if (ret)  	goto out; @@ -1696,7 +2099,7 @@ _krb5_parse_moduli_line(krb5_context context,      *m = m1;      return 0; -out: + out:      free(m1->name);      der_free_heim_integer(&m1->p);      der_free_heim_integer(&m1->g); @@ -1788,7 +2191,8 @@ _krb5_parse_moduli(krb5_context context, const char *file,      m = calloc(1, sizeof(m[0]) * 3);      if (m == NULL) { -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ENOMEM, +			       N_("malloc: out of memory", ""));  	return ENOMEM;      } @@ -1812,11 +2216,26 @@ _krb5_parse_moduli(krb5_context context, const char *file,      if (file == NULL)  	file = MODULI_FILE; +#ifdef KRB5_USE_PATH_TOKENS +    { +        char * exp_file; + +        if (_krb5_expand_path_tokens(context, file, &exp_file) == 0) { +            f = fopen(exp_file, "r"); +            krb5_xfree(exp_file); +        } else { +            f = NULL; +        } +    } +#else      f = fopen(file, "r"); +#endif +      if (f == NULL) {  	*moduli = m;  	return 0;      } +    rk_cloexec_file(f);      while(fgets(buf, sizeof(buf), f) != NULL) {  	struct krb5_dh_moduli *element; @@ -1826,12 +2245,13 @@ _krb5_parse_moduli(krb5_context context, const char *file,  	m2 = realloc(m, (n + 2) * sizeof(m[0]));  	if (m2 == NULL) { -	    krb5_set_error_string(context, "malloc: out of memory");  	    _krb5_free_moduli(m); +	    krb5_set_error_message(context, ENOMEM, +				   N_("malloc: out of memory", ""));  	    return ENOMEM;  	}  	m = m2; -	 +  	m[n] = NULL;  	ret = _krb5_parse_moduli_line(context, file, lineno, buf,  &element); @@ -1865,23 +2285,29 @@ _krb5_dh_group_ok(krb5_context context, unsigned long bits,  	if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&  	    der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&  	    (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0)) -	{ -	    if (bits && bits > moduli[i]->bits) { -		krb5_set_error_string(context, "PKINIT: DH group parameter %s " -				      "no accepted, not enough bits generated", -				      moduli[i]->name); -		return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; +	    { +		if (bits && bits > moduli[i]->bits) { +		    krb5_set_error_message(context, +					   KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED, +					   N_("PKINIT: DH group parameter %s " +					      "no accepted, not enough bits " +					      "generated", ""), +					   moduli[i]->name); +		    return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; +		} +		if (name) +		    *name = strdup(moduli[i]->name); +		return 0;  	    } -	    if (name) -		*name = strdup(moduli[i]->name); -	    return 0; -	}      } -    krb5_set_error_string(context, "PKINIT: DH group parameter no ok"); +    krb5_set_error_message(context, +			   KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED, +			   N_("PKINIT: DH group parameter no ok", ""));      return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;  } +#endif /* PKINIT */ -void KRB5_LIB_FUNCTION +KRB5_LIB_FUNCTION void KRB5_LIB_CALL  _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)  {  #ifdef PKINIT @@ -1890,15 +2316,26 @@ _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)      if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)  	return;      ctx = opt->opt_private->pk_init_ctx; -    if (ctx->dh) -	DH_free(ctx->dh); -	ctx->dh = NULL; +    switch (ctx->keyex) { +    case USE_DH: +	if (ctx->u.dh) +	    DH_free(ctx->u.dh); +	break; +    case USE_RSA: +	break; +    case USE_ECDH: +#ifdef HAVE_OPENSSL +	if (ctx->u.eckey) +	    EC_KEY_free(ctx->u.eckey); +#endif +	break; +    }      if (ctx->id) {  	hx509_verify_destroy_ctx(ctx->id->verify_ctx);  	hx509_certs_free(&ctx->id->certs); +	hx509_cert_free(ctx->id->cert);  	hx509_certs_free(&ctx->id->anchors);  	hx509_certs_free(&ctx->id->certpool); -	hx509_context_free(&ctx->id->hx509ctx);  	if (ctx->clientDHNonce) {  	    krb5_free_data(NULL, ctx->clientDHNonce); @@ -1913,8 +2350,8 @@ _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)      opt->opt_private->pk_init_ctx = NULL;  #endif  } -     -krb5_error_code KRB5_LIB_FUNCTION + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL  krb5_get_init_creds_opt_set_pkinit(krb5_context context,  				   krb5_get_init_creds_opt *opt,  				   krb5_principal principal, @@ -1932,19 +2369,18 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context,      char *anchors = NULL;      if (opt->opt_private == NULL) { -	krb5_set_error_string(context, "PKINIT: on non extendable opt"); +	krb5_set_error_message(context, EINVAL, +			       N_("PKINIT: on non extendable opt", ""));  	return EINVAL;      } -    opt->opt_private->pk_init_ctx =  +    opt->opt_private->pk_init_ctx =  	calloc(1, sizeof(*opt->opt_private->pk_init_ctx));      if (opt->opt_private->pk_init_ctx == NULL) { -	krb5_set_error_string(context, "malloc: out of memory"); +	krb5_set_error_message(context, ENOMEM, +			       N_("malloc: out of memory", ""));  	return ENOMEM;      } -    opt->opt_private->pk_init_ctx->dh = NULL; -    opt->opt_private->pk_init_ctx->id = NULL; -    opt->opt_private->pk_init_ctx->clientDHNonce = NULL;      opt->opt_private->pk_init_ctx->require_binding = 0;      opt->opt_private->pk_init_ctx->require_eku = 1;      opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1; @@ -1953,23 +2389,26 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context,      /* XXX implement krb5_appdefault_strings  */      if (pool == NULL)  	pool = krb5_config_get_strings(context, NULL, -				       "appdefaults",  -				       "pkinit_pool",  +				       "appdefaults", +				       "pkinit_pool",  				       NULL);      if (pki_revoke == NULL)  	pki_revoke = krb5_config_get_strings(context, NULL, -					     "appdefaults",  -					     "pkinit_revoke",  +					     "appdefaults", +					     "pkinit_revoke",  					     NULL);      if (x509_anchors == NULL) {  	krb5_appdefault_string(context, "kinit", -			       krb5_principal_get_realm(context, principal),  +			       krb5_principal_get_realm(context, principal),  			       "pkinit_anchors", NULL, &anchors);  	x509_anchors = anchors;      } +    if (flags & 4) +	opt->opt_private->pk_init_ctx->anonymous = 1; +      ret = _krb5_pk_load_id(context,  			   &opt->opt_private->pk_init_ctx->id,  			   user_id, @@ -1985,86 +2424,218 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context,  	return ret;      } -    if ((flags & 2) == 0) { -	const char *moduli_file; -	unsigned long dh_min_bits; +    if (opt->opt_private->pk_init_ctx->id->certs) { +	_krb5_pk_set_user_id(context, +			     principal, +			     opt->opt_private->pk_init_ctx, +			     opt->opt_private->pk_init_ctx->id->certs); +    } else +	opt->opt_private->pk_init_ctx->id->cert = NULL; -	moduli_file = krb5_config_get_string(context, NULL, -					     "libdefaults", -					     "moduli", -					     NULL); +    if ((flags & 2) == 0) { +	hx509_context hx509ctx = context->hx509ctx; +	hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert; -	dh_min_bits = -	    krb5_config_get_int_default(context, NULL, 0, -					"libdefaults", -					"pkinit_dh_min_bits", -					NULL); +	opt->opt_private->pk_init_ctx->keyex = USE_DH; -	ret = _krb5_parse_moduli(context, moduli_file,  -				 &opt->opt_private->pk_init_ctx->m); -	if (ret) { -	    _krb5_get_init_creds_opt_free_pkinit(opt); -	    return ret; -	} -	 -	opt->opt_private->pk_init_ctx->dh = DH_new(); -	if (opt->opt_private->pk_init_ctx->dh == NULL) { -	    krb5_set_error_string(context, "malloc: out of memory"); -	    _krb5_get_init_creds_opt_free_pkinit(opt); -	    return ENOMEM; +	/* +	 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm. +	 */ +	if (cert) { +	    AlgorithmIdentifier alg; + +	    ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg); +	    if (ret == 0) { +		if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0) +		    opt->opt_private->pk_init_ctx->keyex = USE_ECDH; +		free_AlgorithmIdentifier(&alg); +	    }  	} -	ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh, -			      dh_min_bits,  -			      opt->opt_private->pk_init_ctx->m); -	if (ret) { -	    _krb5_get_init_creds_opt_free_pkinit(opt); -	    return ret; -	} +    } else { +	opt->opt_private->pk_init_ctx->keyex = USE_RSA; -	if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) { -	    krb5_set_error_string(context, "pkinit: failed to generate DH key"); -	    _krb5_get_init_creds_opt_free_pkinit(opt); -	    return ENOMEM; +	if (opt->opt_private->pk_init_ctx->id->certs == NULL) { +	    krb5_set_error_message(context, EINVAL, +				   N_("No anonymous pkinit support in RSA mode", "")); +	    return EINVAL;  	}      }      return 0;  #else -    krb5_set_error_string(context, "no support for PKINIT compiled in"); +    krb5_set_error_message(context, EINVAL, +			   N_("no support for PKINIT compiled in", "")); +    return EINVAL; +#endif +} + +krb5_error_code KRB5_LIB_FUNCTION +krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context, +					      krb5_get_init_creds_opt *opt, +					      struct hx509_certs_data *certs) +{ +#ifdef PKINIT +    if (opt->opt_private == NULL) { +	krb5_set_error_message(context, EINVAL, +			       N_("PKINIT: on non extendable opt", "")); +	return EINVAL; +    } +    if (opt->opt_private->pk_init_ctx == NULL) { +	krb5_set_error_message(context, EINVAL, +			       N_("PKINIT: on pkinit context", "")); +	return EINVAL; +    } + +    _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs); + +    return 0; +#else +    krb5_set_error_message(context, EINVAL, +			   N_("no support for PKINIT compiled in", ""));      return EINVAL;  #endif  } +#ifdef PKINIT + +static int +get_ms_san(hx509_context context, hx509_cert cert, char **upn) +{ +    hx509_octet_string_list list; +    int ret; + +    *upn = NULL; + +    ret = hx509_cert_find_subjectAltName_otherName(context, +						   cert, +						   &asn1_oid_id_pkinit_ms_san, +						   &list); +    if (ret) +	return 0; + +    if (list.len > 0 && list.val[0].length > 0) +	ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, +				upn, NULL); +    else +	ret = 1; +    hx509_free_octet_string_list(&list); + +    return ret; +} + +static int +find_ms_san(hx509_context context, hx509_cert cert, void *ctx) +{ +    char *upn; +    int ret; + +    ret = get_ms_san(context, cert, &upn); +    if (ret == 0) +	free(upn); +    return ret; +} + + + +#endif +  /* - * + * Private since it need to be redesigned using krb5_get_init_creds()   */ -static void -_krb5_pk_copy_error(krb5_context context, -		    hx509_context hx509ctx, -		    int hxret, -		    const char *fmt, -		    ...) +KRB5_LIB_FUNCTION krb5_error_code  KRB5_LIB_CALL +krb5_pk_enterprise_cert(krb5_context context, +			const char *user_id, +			krb5_const_realm realm, +			krb5_principal *principal, +			struct hx509_certs_data **res)  { -    va_list va; -    char *s, *f; +#ifdef PKINIT +    krb5_error_code ret; +    hx509_certs certs, result; +    hx509_cert cert = NULL; +    hx509_query *q; +    char *name; -    va_start(va, fmt); -    vasprintf(&f, fmt, va); -    va_end(va); -    if (f == NULL) { -	krb5_clear_error_string(context); -	return; +    *principal = NULL; +    if (res) +	*res = NULL; + +    if (user_id == NULL) { +	krb5_set_error_message(context, ENOENT, "no user id"); +	return ENOENT;      } -    s = hx509_get_error_string(hx509ctx, hxret); -    if (s == NULL) { -	krb5_clear_error_string(context); -	free(f); -	return; +    ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs); +    if (ret) { +	pk_copy_error(context, context->hx509ctx, ret, +		      "Failed to init cert certs"); +	goto out;      } -    krb5_set_error_string(context, "%s: %s", f, s); -    free(s); -    free(f); + +    ret = hx509_query_alloc(context->hx509ctx, &q); +    if (ret) { +	krb5_set_error_message(context, ret, "out of memory"); +	hx509_certs_free(&certs); +	goto out; +    } + +    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); +    hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); +    hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku); +    hx509_query_match_cmp_func(q, find_ms_san, NULL); + +    ret = hx509_certs_filter(context->hx509ctx, certs, q, &result); +    hx509_query_free(context->hx509ctx, q); +    hx509_certs_free(&certs); +    if (ret) { +	pk_copy_error(context, context->hx509ctx, ret, +		      "Failed to find PKINIT certificate"); +	return ret; +    } + +    ret = hx509_get_one_cert(context->hx509ctx, result, &cert); +    hx509_certs_free(&result); +    if (ret) { +	pk_copy_error(context, context->hx509ctx, ret, +		      "Failed to get one cert"); +	goto out; +    } + +    ret = get_ms_san(context->hx509ctx, cert, &name); +    if (ret) { +	pk_copy_error(context, context->hx509ctx, ret, +		      "Failed to get MS SAN"); +	goto out; +    } + +    ret = krb5_make_principal(context, principal, realm, name, NULL); +    free(name); +    if (ret) +	goto out; + +    krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL); + +    if (res) { +	ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res); +	if (ret) +	    goto out; + +	ret = hx509_certs_add(context->hx509ctx, *res, cert); +	if (ret) { +	    hx509_certs_free(res); +	    goto out; +	} +    } + + out: +    hx509_cert_free(cert); + +    return ret; +#else +    krb5_set_error_message(context, EINVAL, +			   N_("no support for PKINIT compiled in", "")); +    return EINVAL; +#endif  } | 
