diff options
Diffstat (limited to 'lib/hx509/cert.c')
| -rw-r--r-- | lib/hx509/cert.c | 1042 | 
1 files changed, 773 insertions, 269 deletions
| diff --git a/lib/hx509/cert.c b/lib/hx509/cert.c index 1520e23cb1dd..70e575603779 100644 --- a/lib/hx509/cert.c +++ b/lib/hx509/cert.c @@ -1,38 +1,37 @@  /* - * Copyright (c) 2004 - 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:  - * - * 1. Redistributions of source code must retain the above copyright  - *    notice, this list of conditions and the following disclaimer.  - * - * 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.  - * - * 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.  + * Copyright (c) 2004 - 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: + * + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * + * 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. + * + * 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 "hx_locl.h" -RCSID("$Id: cert.c 22450 2008-01-15 19:39:14Z lha $");  #include "crypto-headers.h"  #include <rtbl.h> @@ -59,6 +58,7 @@ struct hx509_verify_ctx_data {  #define HX509_VERIFY_CTX_F_REQUIRE_RFC3280		4  #define HX509_VERIFY_CTX_F_CHECK_TRUST_ANCHORS		8  #define HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS		16 +#define HX509_VERIFY_CTX_F_NO_BEST_BEFORE_CHECK		32      time_t time_now;      unsigned int max_depth;  #define HX509_VERIFY_MAX_DEPTH 30 @@ -138,10 +138,10 @@ hx509_context_init(hx509_context *context)  /**   * Selects if the hx509_revoke_verify() function is going to require - * the existans of a revokation method (OSCP, CRL) or not. Note that + * the existans of a revokation method (OCSP, CRL) or not. Note that   * hx509_verify_path(), hx509_cms_verify_signed(), and other function   * call hx509_revoke_verify(). - *  + *   * @param context hx509 context to change the flag for.   * @param flag zero, revokation method required, non zero missing   * revokation method ok @@ -160,7 +160,7 @@ hx509_context_set_missing_revoke(hx509_context context, int flag)  /**   * Free the context allocated by hx509_context_init(). - *  + *   * @param context context to be freed.   *   * @ingroup hx509 @@ -205,7 +205,7 @@ _hx509_cert_get_version(const Certificate *t)  /**   * Allocate and init an hx509 certificate object from the decoded - * certificate `c´. + * certificate `c´.   *   * @param context A hx509 context.   * @param c @@ -268,7 +268,7 @@ hx509_cert_init(hx509_context context, const Certificate *c, hx509_cert *cert)   */  int -hx509_cert_init_data(hx509_context context,  +hx509_cert_init_data(hx509_context context,  		     const void *ptr,  		     size_t len,  		     hx509_cert *cert) @@ -283,6 +283,7 @@ hx509_cert_init_data(hx509_context context,  	return ret;      }      if (size != len) { +	free_Certificate(&t);  	hx509_set_error_string(context, 0, HX509_EXTRA_DATA_AFTER_STRUCTURE,  			       "Extra data after certificate");  	return HX509_EXTRA_DATA_AFTER_STRUCTURE; @@ -294,7 +295,7 @@ hx509_cert_init_data(hx509_context context,  }  void -_hx509_cert_set_release(hx509_cert cert,  +_hx509_cert_set_release(hx509_cert cert,  			_hx509_cert_release_func release,  			void *ctx)  { @@ -309,7 +310,7 @@ int  _hx509_cert_assign_key(hx509_cert cert, hx509_private_key private_key)  {      if (cert->private_key) -	_hx509_private_key_free(&cert->private_key); +	hx509_private_key_free(&cert->private_key);      cert->private_key = _hx509_private_key_ref(private_key);      return 0;  } @@ -326,7 +327,7 @@ _hx509_cert_assign_key(hx509_cert cert, hx509_private_key private_key)  void  hx509_cert_free(hx509_cert cert)  { -    int i; +    size_t i;      if (cert == NULL)  	return; @@ -340,7 +341,7 @@ hx509_cert_free(hx509_cert cert)  	(cert->release)(cert, cert->ctx);      if (cert->private_key) -	_hx509_private_key_free(&cert->private_key); +	hx509_private_key_free(&cert->private_key);      free_Certificate(cert->data);      free(cert->data); @@ -354,7 +355,7 @@ hx509_cert_free(hx509_cert cert)      free(cert->friendlyname);      if (cert->basename)  	hx509_name_free(&cert->basename); -    memset(cert, 0, sizeof(cert)); +    memset(cert, 0, sizeof(*cert));      free(cert);  } @@ -383,7 +384,7 @@ hx509_cert_ref(hx509_cert cert)  /**   * Allocate an verification context that is used fo control the - * verification process.  + * verification process.   *   * @param context A hx509 context.   * @param ctx returns a pointer to a hx509_verify_ctx object. @@ -405,7 +406,7 @@ hx509_verify_init_ctx(hx509_context context, hx509_verify_ctx *ctx)      c->max_depth = HX509_VERIFY_MAX_DEPTH;      *ctx = c; -     +      return 0;  } @@ -432,6 +433,7 @@ hx509_verify_destroy_ctx(hx509_verify_ctx ctx)   * Set the trust anchors in the verification context, makes an   * reference to the keyset, so the consumer can free the keyset   * independent of the destruction of the verification context (ctx). + * If there already is a keyset attached, it's released.   *   * @param ctx a verification context   * @param set a keyset containing the trust anchors. @@ -442,7 +444,9 @@ hx509_verify_destroy_ctx(hx509_verify_ctx ctx)  void  hx509_verify_attach_anchors(hx509_verify_ctx ctx, hx509_certs set)  { -    ctx->trust_anchors = _hx509_certs_ref(set); +    if (ctx->trust_anchors) +	hx509_certs_free(&ctx->trust_anchors); +    ctx->trust_anchors = hx509_certs_ref(set);  }  /** @@ -485,6 +489,12 @@ hx509_verify_set_time(hx509_verify_ctx ctx, time_t t)      ctx->time_now = t;  } +time_t +_hx509_verify_get_time(hx509_verify_ctx ctx) +{ +    return ctx->time_now; +} +  /**   * Set the maximum depth of the certificate chain that the path   * builder is going to try. @@ -563,14 +573,24 @@ hx509_verify_ctx_f_allow_default_trustanchors(hx509_verify_ctx ctx, int boolean)  	ctx->flags |= HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS;  } +void +hx509_verify_ctx_f_allow_best_before_signature_algs(hx509_context ctx, +						    int boolean) +{ +    if (boolean) +	ctx->flags &= ~HX509_VERIFY_CTX_F_NO_BEST_BEFORE_CHECK; +    else +	ctx->flags |= HX509_VERIFY_CTX_F_NO_BEST_BEFORE_CHECK; +} +  static const Extension * -find_extension(const Certificate *cert, const heim_oid *oid, int *idx) +find_extension(const Certificate *cert, const heim_oid *oid, size_t *idx)  {      const TBSCertificate *c = &cert->tbsCertificate;      if (c->version == NULL || *c->version < 2 || c->extensions == NULL)  	return NULL; -     +      for (;*idx < c->extensions->len; (*idx)++) {  	if (der_heim_oid_cmp(&c->extensions->val[*idx].extnID, oid) == 0)  	    return &c->extensions->val[(*idx)++]; @@ -579,21 +599,21 @@ find_extension(const Certificate *cert, const heim_oid *oid, int *idx)  }  static int -find_extension_auth_key_id(const Certificate *subject,  +find_extension_auth_key_id(const Certificate *subject,  			   AuthorityKeyIdentifier *ai)  {      const Extension *e;      size_t size; -    int i = 0; +    size_t i = 0;      memset(ai, 0, sizeof(*ai)); -    e = find_extension(subject, oid_id_x509_ce_authorityKeyIdentifier(), &i); +    e = find_extension(subject, &asn1_oid_id_x509_ce_authorityKeyIdentifier, &i);      if (e == NULL)  	return HX509_EXTENSION_NOT_FOUND; -     -    return decode_AuthorityKeyIdentifier(e->extnValue.data,  -					 e->extnValue.length,  + +    return decode_AuthorityKeyIdentifier(e->extnValue.data, +					 e->extnValue.length,  					 ai, &size);  } @@ -603,40 +623,40 @@ _hx509_find_extension_subject_key_id(const Certificate *issuer,  {      const Extension *e;      size_t size; -    int i = 0; +    size_t i = 0;      memset(si, 0, sizeof(*si)); -    e = find_extension(issuer, oid_id_x509_ce_subjectKeyIdentifier(), &i); +    e = find_extension(issuer, &asn1_oid_id_x509_ce_subjectKeyIdentifier, &i);      if (e == NULL)  	return HX509_EXTENSION_NOT_FOUND; -     -    return decode_SubjectKeyIdentifier(e->extnValue.data,  + +    return decode_SubjectKeyIdentifier(e->extnValue.data,  				       e->extnValue.length,  				       si, &size);  }  static int -find_extension_name_constraints(const Certificate *subject,  +find_extension_name_constraints(const Certificate *subject,  				NameConstraints *nc)  {      const Extension *e;      size_t size; -    int i = 0; +    size_t i = 0;      memset(nc, 0, sizeof(*nc)); -    e = find_extension(subject, oid_id_x509_ce_nameConstraints(), &i); +    e = find_extension(subject, &asn1_oid_id_x509_ce_nameConstraints, &i);      if (e == NULL)  	return HX509_EXTENSION_NOT_FOUND; -     -    return decode_NameConstraints(e->extnValue.data,  -				  e->extnValue.length,  + +    return decode_NameConstraints(e->extnValue.data, +				  e->extnValue.length,  				  nc, &size);  }  static int -find_extension_subject_alt_name(const Certificate *cert, int *i, +find_extension_subject_alt_name(const Certificate *cert, size_t *i,  				GeneralNames *sa)  {      const Extension *e; @@ -644,11 +664,11 @@ find_extension_subject_alt_name(const Certificate *cert, int *i,      memset(sa, 0, sizeof(*sa)); -    e = find_extension(cert, oid_id_x509_ce_subjectAltName(), i); +    e = find_extension(cert, &asn1_oid_id_x509_ce_subjectAltName, i);      if (e == NULL)  	return HX509_EXTENSION_NOT_FOUND; -     -    return decode_GeneralNames(e->extnValue.data,  + +    return decode_GeneralNames(e->extnValue.data,  			       e->extnValue.length,  			       sa, &size);  } @@ -658,15 +678,15 @@ find_extension_eku(const Certificate *cert, ExtKeyUsage *eku)  {      const Extension *e;      size_t size; -    int i = 0; +    size_t i = 0;      memset(eku, 0, sizeof(*eku)); -    e = find_extension(cert, oid_id_x509_ce_extKeyUsage(), &i); +    e = find_extension(cert, &asn1_oid_id_x509_ce_extKeyUsage, &i);      if (e == NULL)  	return HX509_EXTENSION_NOT_FOUND; -     -    return decode_ExtKeyUsage(e->extnValue.data,  + +    return decode_ExtKeyUsage(e->extnValue.data,  			      e->extnValue.length,  			      eku, &size);  } @@ -700,7 +720,7 @@ add_to_list(hx509_octet_string_list *list, const heim_octet_string *entry)  void  hx509_free_octet_string_list(hx509_octet_string_list *list)  { -    int i; +    size_t i;      for (i = 0; i < list->len; i++)  	der_free_octet_string(&list->val[i]);      free(list->val); @@ -710,7 +730,7 @@ hx509_free_octet_string_list(hx509_octet_string_list *list)  /**   * Return a list of subjectAltNames specified by oid in the - * certificate. On error the  + * certificate. On error the   *   * The returned list of octet string should be freed with   * hx509_free_octet_string_list(). @@ -732,7 +752,8 @@ hx509_cert_find_subjectAltName_otherName(hx509_context context,  					 hx509_octet_string_list *list)  {      GeneralNames sa; -    int ret, i, j; +    int ret; +    size_t i, j;      list->val = NULL;      list->len = 0; @@ -742,8 +763,7 @@ hx509_cert_find_subjectAltName_otherName(hx509_context context,  	ret = find_extension_subject_alt_name(_hx509_get_cert(cert), &i, &sa);  	i++;  	if (ret == HX509_EXTENSION_NOT_FOUND) { -	    ret = 0; -	    break; +	    return 0;  	} else if (ret != 0) {  	    hx509_set_error_string(context, 0, ret, "Error searching for SAN");  	    hx509_free_octet_string_list(list); @@ -752,11 +772,11 @@ hx509_cert_find_subjectAltName_otherName(hx509_context context,  	for (j = 0; j < sa.len; j++) {  	    if (sa.val[j].element == choice_GeneralName_otherName && -		der_heim_oid_cmp(&sa.val[j].u.otherName.type_id, oid) == 0)  +		der_heim_oid_cmp(&sa.val[j].u.otherName.type_id, oid) == 0)  	    {  		ret = add_to_list(list, &sa.val[j].u.otherName.value);  		if (ret) { -		    hx509_set_error_string(context, 0, ret,  +		    hx509_set_error_string(context, 0, ret,  					   "Error adding an exra SAN to "  					   "return list");  		    hx509_free_octet_string_list(list); @@ -767,24 +787,24 @@ hx509_cert_find_subjectAltName_otherName(hx509_context context,  	}  	free_GeneralNames(&sa);      } -    return 0;  }  static int -check_key_usage(hx509_context context, const Certificate *cert,  +check_key_usage(hx509_context context, const Certificate *cert,  		unsigned flags, int req_present)  {      const Extension *e;      KeyUsage ku;      size_t size; -    int ret, i = 0; +    int ret; +    size_t i = 0;      unsigned ku_flags;      if (_hx509_cert_get_version(cert) < 3)  	return 0; -    e = find_extension(cert, oid_id_x509_ce_keyUsage(), &i); +    e = find_extension(cert, &asn1_oid_id_x509_ce_keyUsage, &i);      if (e == NULL) {  	if (req_present) {  	    hx509_set_error_string(context, 0, HX509_KU_CERT_MISSING, @@ -794,7 +814,7 @@ check_key_usage(hx509_context context, const Certificate *cert,  	}  	return 0;      } -     +      ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length, &ku, &size);      if (ret)  	return ret; @@ -821,7 +841,7 @@ check_key_usage(hx509_context context, const Certificate *cert,   */  int -_hx509_check_key_usage(hx509_context context, hx509_cert cert,  +_hx509_check_key_usage(hx509_context context, hx509_cert cert,  		       unsigned flags, int req_present)  {      return check_key_usage(context, _hx509_get_cert(cert), flags, req_present); @@ -830,18 +850,19 @@ _hx509_check_key_usage(hx509_context context, hx509_cert cert,  enum certtype { PROXY_CERT, EE_CERT, CA_CERT };  static int -check_basic_constraints(hx509_context context, const Certificate *cert,  -			enum certtype type, int depth) +check_basic_constraints(hx509_context context, const Certificate *cert, +			enum certtype type, size_t depth)  {      BasicConstraints bc;      const Extension *e;      size_t size; -    int ret, i = 0; +    int ret; +    size_t i = 0;      if (_hx509_cert_get_version(cert) < 3)  	return 0; -    e = find_extension(cert, oid_id_x509_ce_basicConstraints(), &i); +    e = find_extension(cert, &asn1_oid_id_x509_ce_basicConstraints, &i);      if (e == NULL) {  	switch(type) {  	case PROXY_CERT: @@ -859,8 +880,8 @@ check_basic_constraints(hx509_context context, const Certificate *cert,  	}  	}      } -     -    ret = decode_BasicConstraints(e->extnValue.data,  + +    ret = decode_BasicConstraints(e->extnValue.data,  				  e->extnValue.length, &bc,  				  &size);      if (ret) @@ -893,13 +914,16 @@ _hx509_cert_is_parent_cmp(const Certificate *subject,      int diff;      AuthorityKeyIdentifier ai;      SubjectKeyIdentifier si; -    int ret_ai, ret_si; +    int ret_ai, ret_si, ret; -    diff = _hx509_name_cmp(&issuer->tbsCertificate.subject,  -			   &subject->tbsCertificate.issuer); +    ret = _hx509_name_cmp(&issuer->tbsCertificate.subject, +			  &subject->tbsCertificate.issuer, +			  &diff); +    if (ret) +	return ret;      if (diff)  	return diff; -     +      memset(&ai, 0, sizeof(ai));      memset(&si, 0, sizeof(si)); @@ -928,7 +952,7 @@ _hx509_cert_is_parent_cmp(const Certificate *subject,  	    goto out;  	}      } -     +      if (ai.keyIdentifier == NULL) {  	Name name; @@ -937,7 +961,7 @@ _hx509_cert_is_parent_cmp(const Certificate *subject,  	if (ai.authorityCertSerialNumber == NULL)  	    return -1; -	diff = der_heim_integer_cmp(ai.authorityCertSerialNumber,  +	diff = der_heim_integer_cmp(ai.authorityCertSerialNumber,  				    &issuer->tbsCertificate.serialNumber);  	if (diff)  	    return diff; @@ -945,14 +969,17 @@ _hx509_cert_is_parent_cmp(const Certificate *subject,  	    return -1;  	if (ai.authorityCertIssuer->val[0].element != choice_GeneralName_directoryName)  	    return -1; -	 -	name.element =  + +	name.element =  	    ai.authorityCertIssuer->val[0].u.directoryName.element; -	name.u.rdnSequence =  +	name.u.rdnSequence =  	    ai.authorityCertIssuer->val[0].u.directoryName.u.rdnSequence; -	diff = _hx509_name_cmp(&issuer->tbsCertificate.subject,  -			       &name); +	ret = _hx509_name_cmp(&issuer->tbsCertificate.subject, +			      &name, +			      &diff); +	if (ret) +	    return ret;  	if (diff)  	    return diff;  	diff = 0; @@ -991,10 +1018,21 @@ certificate_is_anchor(hx509_context context,  }  static int -certificate_is_self_signed(const Certificate *cert) -{ -    return _hx509_name_cmp(&cert->tbsCertificate.subject,  -			   &cert->tbsCertificate.issuer) == 0; +certificate_is_self_signed(hx509_context context, +			   const Certificate *cert, +			   int *self_signed) +{ +    int ret, diff; +    ret = _hx509_name_cmp(&cert->tbsCertificate.subject, +			  &cert->tbsCertificate.issuer, &diff); +    *self_signed = (diff == 0); +    if (ret) { +	hx509_set_error_string(context, 0, ret, +			       "Failed to check if self signed"); +    } else +	ret = _hx509_self_signed_valid(context, &cert->signatureAlgorithm); + +    return ret;  }  /* @@ -1013,7 +1051,7 @@ find_parent(hx509_context context,  	    time_t time_now,  	    hx509_certs trust_anchors,  	    hx509_path *path, -	    hx509_certs pool,  +	    hx509_certs pool,  	    hx509_cert current,  	    hx509_cert *parent)  { @@ -1023,7 +1061,7 @@ find_parent(hx509_context context,      *parent = NULL;      memset(&ai, 0, sizeof(ai)); -     +      _hx509_query_clear(&q);      if (!subject_null_p(current->data)) { @@ -1088,7 +1126,7 @@ find_parent(hx509_context context,  	    hx509_clear_error_string(context);  	    return HX509_ISSUER_NOT_FOUND;  	} -	 +  	hx509_set_error_string(context, 0, HX509_ISSUER_NOT_FOUND,  			       "Failed to find issuer for "  			       "certificate with subject: '%s'", str); @@ -1102,26 +1140,27 @@ find_parent(hx509_context context,   */  static int -is_proxy_cert(hx509_context context,  -	      const Certificate *cert,  +is_proxy_cert(hx509_context context, +	      const Certificate *cert,  	      ProxyCertInfo *rinfo)  {      ProxyCertInfo info;      const Extension *e;      size_t size; -    int ret, i = 0; +    int ret; +    size_t i = 0;      if (rinfo)  	memset(rinfo, 0, sizeof(*rinfo)); -    e = find_extension(cert, oid_id_pkix_pe_proxyCertInfo(), &i); +    e = find_extension(cert, &asn1_oid_id_pkix_pe_proxyCertInfo, &i);      if (e == NULL) {  	hx509_clear_error_string(context);  	return HX509_EXTENSION_NOT_FOUND;      } -    ret = decode_ProxyCertInfo(e->extnValue.data,  -			       e->extnValue.length,  +    ret = decode_ProxyCertInfo(e->extnValue.data, +			       e->extnValue.length,  			       &info,  			       &size);      if (ret) { @@ -1131,7 +1170,7 @@ is_proxy_cert(hx509_context context,      if (size != e->extnValue.length) {  	free_ProxyCertInfo(&info);  	hx509_clear_error_string(context); -	return HX509_EXTRA_DATA_AFTER_STRUCTURE;  +	return HX509_EXTRA_DATA_AFTER_STRUCTURE;      }      if (rinfo == NULL)  	free_ProxyCertInfo(&info); @@ -1167,7 +1206,7 @@ void  _hx509_path_free(hx509_path *path)  {      unsigned i; -     +      for (i = 0; i < path->len; i++)  	hx509_cert_free(path->val[i]);      free(path->val); @@ -1188,7 +1227,7 @@ _hx509_path_free(hx509_path *path)   * The path includes a path from the top certificate to the anchor   * certificate.   * - * The caller needs to free `path´ both on successful built path and + * The caller needs to free `path´ both on successful built path and   * failure.   */ @@ -1216,7 +1255,7 @@ _hx509_calculate_path(hx509_context context,      while (!certificate_is_anchor(context, anchors, current)) { -	ret = find_parent(context, time_now, anchors, path,  +	ret = find_parent(context, time_now, anchors, path,  			  pool, current, &parent);  	hx509_cert_free(current);  	if (ret) @@ -1236,8 +1275,8 @@ _hx509_calculate_path(hx509_context context,  	}      } -    if ((flags & HX509_CALCULATE_PATH_NO_ANCHOR) &&  -	path->len > 0 &&  +    if ((flags & HX509_CALCULATE_PATH_NO_ANCHOR) && +	path->len > 0 &&  	certificate_is_anchor(context, anchors, path->val[path->len - 1]))      {  	hx509_cert_free(path->val[path->len - 1]); @@ -1277,7 +1316,7 @@ _hx509_Certificate_cmp(const Certificate *p, const Certificate *q)      diff = der_heim_bit_string_cmp(&p->signatureValue, &q->signatureValue);      if (diff)  	return diff; -    diff = _hx509_AlgorithmIdentifier_cmp(&p->signatureAlgorithm,  +    diff = _hx509_AlgorithmIdentifier_cmp(&p->signatureAlgorithm,  					  &q->signatureAlgorithm);      if (diff)  	return diff; @@ -1452,7 +1491,9 @@ hx509_cert_get_SPKI(hx509_context context, hx509_cert p, SubjectPublicKeyInfo *s   * @param context a hx509 context.   * @param p a hx509 certificate object.   * @param alg AlgorithmIdentifier, should be freed with - * free_AlgorithmIdentifier(). + *            free_AlgorithmIdentifier(). The algorithmidentifier is + *            typicly rsaEncryption, or id-ecPublicKey, or some other + *            public key mechanism.   *   * @return An hx509 error code, see hx509_get_error_string().   * @@ -1461,7 +1502,7 @@ hx509_cert_get_SPKI(hx509_context context, hx509_cert p, SubjectPublicKeyInfo *s  int  hx509_cert_get_SPKI_AlgorithmIdentifier(hx509_context context, -					hx509_cert p,  +					hx509_cert p,  					AlgorithmIdentifier *alg)  {      int ret; @@ -1473,6 +1514,65 @@ hx509_cert_get_SPKI_AlgorithmIdentifier(hx509_context context,      return ret;  } +static int +get_x_unique_id(hx509_context context, const char *name, +		const heim_bit_string *cert, heim_bit_string *subject) +{ +    int ret; + +    if (cert == NULL) { +	ret = HX509_EXTENSION_NOT_FOUND; +	hx509_set_error_string(context, 0, ret, "%s unique id doesn't exists", name); +	return ret; +    } +    ret = der_copy_bit_string(cert, subject); +    if (ret) { +	hx509_set_error_string(context, 0, ret, "malloc out of memory", name); +	return ret; +    } +    return 0; +} + +/** + * Get a copy of the Issuer Unique ID + * + * @param context a hx509_context + * @param p a hx509 certificate + * @param issuer the issuer id returned, free with der_free_bit_string() + * + * @return An hx509 error code, see hx509_get_error_string(). The + * error code HX509_EXTENSION_NOT_FOUND is returned if the certificate + * doesn't have a issuerUniqueID + * + * @ingroup hx509_cert + */ + +int +hx509_cert_get_issuer_unique_id(hx509_context context, hx509_cert p, heim_bit_string *issuer) +{ +    return get_x_unique_id(context, "issuer", p->data->tbsCertificate.issuerUniqueID, issuer); +} + +/** + * Get a copy of the Subect Unique ID + * + * @param context a hx509_context + * @param p a hx509 certificate + * @param subject the subject id returned, free with der_free_bit_string() + * + * @return An hx509 error code, see hx509_get_error_string(). The + * error code HX509_EXTENSION_NOT_FOUND is returned if the certificate + * doesn't have a subjectUniqueID + * + * @ingroup hx509_cert + */ + +int +hx509_cert_get_subject_unique_id(hx509_context context, hx509_cert p, heim_bit_string *subject) +{ +    return get_x_unique_id(context, "subject", p->data->tbsCertificate.subjectUniqueID, subject); +} +  hx509_private_key  _hx509_cert_private_key(hx509_cert p) @@ -1511,15 +1611,15 @@ _hx509_cert_private_decrypt(hx509_context context,  	return HX509_PRIVATE_KEY_MISSING;      } -    return _hx509_private_key_private_decrypt(context, +    return hx509_private_key_private_decrypt(context,  					      ciphertext,  					      encryption_oid, -					      p->private_key,  +					      p->private_key,  					      cleartext);  }  int -_hx509_cert_public_encrypt(hx509_context context, +hx509_cert_public_encrypt(hx509_context context,  			   const heim_octet_string *cleartext,  			   const hx509_cert p,  			   heim_oid *encryption_oid, @@ -1599,15 +1699,20 @@ static int  match_RDN(const RelativeDistinguishedName *c,  	  const RelativeDistinguishedName *n)  { -    int i; +    size_t i;      if (c->len != n->len)  	return HX509_NAME_CONSTRAINT_ERROR; -     +      for (i = 0; i < n->len; i++) { +	int diff, ret; +  	if (der_heim_oid_cmp(&c->val[i].type, &n->val[i].type) != 0)  	    return HX509_NAME_CONSTRAINT_ERROR; -	if (_hx509_name_ds_cmp(&c->val[i].value, &n->val[i].value) != 0) +	ret = _hx509_name_ds_cmp(&c->val[i].value, &n->val[i].value, &diff); +	if (ret) +	    return ret; +	if (diff != 0)  	    return HX509_NAME_CONSTRAINT_ERROR;      }      return 0; @@ -1616,7 +1721,8 @@ match_RDN(const RelativeDistinguishedName *c,  static int  match_X501Name(const Name *c, const Name *n)  { -    int i, ret; +    size_t i; +    int ret;      if (c->element != choice_Name_rdnSequence  	|| n->element != choice_Name_rdnSequence) @@ -1629,13 +1735,13 @@ match_X501Name(const Name *c, const Name *n)  	    return ret;      }      return 0; -}  +}  static int  match_general_name(const GeneralName *c, const GeneralName *n, int *match)  { -    /*  +    /*       * Name constraints only apply to the same name type, see RFC3280,       * 4.2.1.11.       */ @@ -1654,19 +1760,20 @@ match_general_name(const GeneralName *c, const GeneralName *n, int *match)      case choice_GeneralName_rfc822Name: {  	const char *s;  	size_t len1, len2; -	s = strchr(c->u.rfc822Name, '@'); +	s = memchr(c->u.rfc822Name.data, '@', c->u.rfc822Name.length);  	if (s) { -	    if (strcasecmp(c->u.rfc822Name, n->u.rfc822Name) != 0) +	    if (der_printable_string_cmp(&c->u.rfc822Name, &n->u.rfc822Name) != 0)  		return HX509_NAME_CONSTRAINT_ERROR;  	} else { -	    s = strchr(n->u.rfc822Name, '@'); +	    s = memchr(n->u.rfc822Name.data, '@', n->u.rfc822Name.length);  	    if (s == NULL)  		return HX509_NAME_CONSTRAINT_ERROR; -	    len1 = strlen(c->u.rfc822Name); -	    len2 = strlen(s + 1); +	    len1 = c->u.rfc822Name.length; +	    len2 = n->u.rfc822Name.length - +		(s - ((char *)n->u.rfc822Name.data));  	    if (len1 > len2)  		return HX509_NAME_CONSTRAINT_ERROR; -	    if (strcasecmp(s + 1 + len2 - len1, c->u.rfc822Name) != 0) +	    if (memcmp(s + 1 + len2 - len1, c->u.rfc822Name.data, len1) != 0)  		return HX509_NAME_CONSTRAINT_ERROR;  	    if (len1 < len2 && s[len2 - len1 + 1] != '.')  		return HX509_NAME_CONSTRAINT_ERROR; @@ -1676,14 +1783,16 @@ match_general_name(const GeneralName *c, const GeneralName *n, int *match)      }      case choice_GeneralName_dNSName: {  	size_t lenc, lenn; +	char *ptr; -	lenc = strlen(c->u.dNSName); -	lenn = strlen(n->u.dNSName); +	lenc = c->u.dNSName.length; +	lenn = n->u.dNSName.length;  	if (lenc > lenn)  	    return HX509_NAME_CONSTRAINT_ERROR; -	if (strcasecmp(&n->u.dNSName[lenn - lenc], c->u.dNSName) != 0) +	ptr = n->u.dNSName.data; +	if (memcmp(&ptr[lenn - lenc], c->u.dNSName.data, lenc) != 0)  	    return HX509_NAME_CONSTRAINT_ERROR; -	if (lenc != lenn && n->u.dNSName[lenn - lenc - 1] != '.') +	if (lenn != lenc && ptr[lenn - lenc - 1] != '.')  	    return HX509_NAME_CONSTRAINT_ERROR;  	*match = 1;  	return 0; @@ -1716,11 +1825,12 @@ match_general_name(const GeneralName *c, const GeneralName *n, int *match)  }  static int -match_alt_name(const GeneralName *n, const Certificate *c,  +match_alt_name(const GeneralName *n, const Certificate *c,  	       int *same, int *match)  {      GeneralNames sa; -    int ret, i, j; +    int ret; +    size_t i, j;      i = 0;      do { @@ -1765,14 +1875,14 @@ match_tree(const GeneralSubtrees *t, const Certificate *c, int *match)  	    && !subject_null_p(c))  	{  	    GeneralName certname; -	     +  	    memset(&certname, 0, sizeof(certname));  	    certname.element = choice_GeneralName_directoryName; -	    certname.u.directoryName.element =  +	    certname.u.directoryName.element =  		c->tbsCertificate.subject.element; -	    certname.u.directoryName.u.rdnSequence =  +	    certname.u.directoryName.u.rdnSequence =  		c->tbsCertificate.subject.u.rdnSequence; -     +  	    ret = match_general_name(&t->val[i].base, &certname, &name);  	} @@ -1789,12 +1899,12 @@ match_tree(const GeneralSubtrees *t, const Certificate *c, int *match)  }  static int -check_name_constraints(hx509_context context,  +check_name_constraints(hx509_context context,  		       const hx509_name_constraints *nc,  		       const Certificate *c)  {      int match, ret; -    int i; +    size_t i;      for (i = 0 ; i < nc->len; i++) {  	GeneralSubtrees gs; @@ -1837,7 +1947,7 @@ check_name_constraints(hx509_context context,  static void  free_name_constraints(hx509_name_constraints *nc)  { -    int i; +    size_t i;      for (i = 0 ; i < nc->len; i++)  	free_NameConstraints(&nc->val[i]); @@ -1867,10 +1977,8 @@ hx509_verify_path(hx509_context context,  {      hx509_name_constraints nc;      hx509_path path; -#if 0 -    const AlgorithmIdentifier *alg_id; -#endif -    int ret, i, proxy_cert_depth, selfsigned_depth; +    int ret, proxy_cert_depth, selfsigned_depth, diff; +    size_t i, k;      enum certtype type;      Name proxy_issuer;      hx509_certs anchors = NULL; @@ -1878,7 +1986,7 @@ hx509_verify_path(hx509_context context,      memset(&proxy_issuer, 0, sizeof(proxy_issuer));      ret = init_name_constraints(&nc); -    if (ret)	 +    if (ret)  	return ret;      path.val = NULL; @@ -1891,9 +1999,9 @@ hx509_verify_path(hx509_context context,       *       */      if (ctx->trust_anchors) -	anchors = _hx509_certs_ref(ctx->trust_anchors); +	anchors = hx509_certs_ref(ctx->trust_anchors);      else if (context->default_trust_anchors && ALLOW_DEF_TA(ctx)) -	anchors = _hx509_certs_ref(context->default_trust_anchors); +	anchors = hx509_certs_ref(context->default_trust_anchors);      else {  	ret = hx509_certs_init(context, "MEMORY:no-TA", 0, NULL, &anchors);  	if (ret) @@ -1910,10 +2018,6 @@ hx509_verify_path(hx509_context context,      if (ret)  	goto out; -#if 0 -    alg_id = path.val[path->len - 1]->data->tbsCertificate.signature; -#endif -      /*       * Check CA and proxy certificate chain from the top of the       * certificate chain. Also check certificate is valid with respect @@ -1934,7 +2038,7 @@ hx509_verify_path(hx509_context context,  	time_t t;  	c = _hx509_get_cert(path.val[i]); -	 +  	/*  	 * Lets do some basic check on issuer like  	 * keyUsage.keyCertSign and basicConstraints.cA bit depending @@ -1943,6 +2047,7 @@ hx509_verify_path(hx509_context context,  	switch (type) {  	case CA_CERT: +  	    /* XXX make constants for keyusage */  	    ret = check_key_usage(context, c, 1 << 5,  				  REQUIRE_RFC3280(ctx) ? TRUE : FALSE); @@ -1952,15 +2057,23 @@ hx509_verify_path(hx509_context context,  		goto out;  	    } -	    if (i + 1 != path.len && certificate_is_self_signed(c))  -		selfsigned_depth++; +	    /* self signed cert doesn't add to path length */ +	    if (i + 1 != path.len) { +		int selfsigned; + +		ret = certificate_is_self_signed(context, c, &selfsigned); +		if (ret) +		    goto out; +		if (selfsigned) +		    selfsigned_depth++; +	    }  	    break;  	case PROXY_CERT: { -	    ProxyCertInfo info;	     +	    ProxyCertInfo info;  	    if (is_proxy_cert(context, c, &info) == 0) { -		int j; +		size_t j;  		if (info.pCPathLenConstraint != NULL &&  		    *info.pCPathLenConstraint < i) @@ -1974,26 +2087,26 @@ hx509_verify_path(hx509_context context,  		}  		/* XXX MUST check info.proxyPolicy */  		free_ProxyCertInfo(&info); -		 +  		j = 0; -		if (find_extension(c, oid_id_x509_ce_subjectAltName(), &j)) { +		if (find_extension(c, &asn1_oid_id_x509_ce_subjectAltName, &j)) {  		    ret = HX509_PROXY_CERT_INVALID; -		    hx509_set_error_string(context, 0, ret,  +		    hx509_set_error_string(context, 0, ret,  					   "Proxy certificate have explicity "  					   "forbidden subjectAltName");  		    goto out;  		}  		j = 0; -		if (find_extension(c, oid_id_x509_ce_issuerAltName(), &j)) { +		if (find_extension(c, &asn1_oid_id_x509_ce_issuerAltName, &j)) {  		    ret = HX509_PROXY_CERT_INVALID; -		    hx509_set_error_string(context, 0, ret,  +		    hx509_set_error_string(context, 0, ret,  					   "Proxy certificate have explicity "  					   "forbidden issuerAltName");  		    goto out;  		} -			 -		/*  + +		/*  		 * The subject name of the proxy certificate should be  		 * CN=XXX,<proxy issuer>, prune of CN and check if its  		 * the same over the whole chain of proxy certs and @@ -2001,8 +2114,12 @@ hx509_verify_path(hx509_context context,  		 */  		if (proxy_cert_depth) { -		    ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.subject); +		    ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.subject, &diff);  		    if (ret) { +			hx509_set_error_string(context, 0, ret, "Out of memory"); +			goto out; +		    } +		    if (diff) {  			ret = HX509_PROXY_CERT_NAME_WRONG;  			hx509_set_error_string(context, 0, ret,  					       "Base proxy name not right"); @@ -2019,10 +2136,10 @@ hx509_verify_path(hx509_context context,  		}  		j = proxy_issuer.u.rdnSequence.len; -		if (proxy_issuer.u.rdnSequence.len < 2  +		if (proxy_issuer.u.rdnSequence.len < 2  		    || proxy_issuer.u.rdnSequence.val[j - 1].len > 1  		    || der_heim_oid_cmp(&proxy_issuer.u.rdnSequence.val[j - 1].val[0].type, -					oid_id_at_commonName())) +					&asn1_oid_id_at_commonName))  		{  		    ret = HX509_PROXY_CERT_NAME_WRONG;  		    hx509_set_error_string(context, 0, ret, @@ -2035,8 +2152,12 @@ hx509_verify_path(hx509_context context,  		free_RelativeDistinguishedName(&proxy_issuer.u.rdnSequence.val[j - 1]);  		proxy_issuer.u.rdnSequence.len -= 1; -		ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.issuer); -		if (ret != 0) { +		ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.issuer, &diff); +		if (ret) { +		    hx509_set_error_string(context, 0, ret, "Out of memory"); +		    goto out; +		} +		if (diff != 0) {  		    ret = HX509_PROXY_CERT_NAME_WRONG;  		    hx509_set_error_string(context, 0, ret,  					   "Proxy issuer name not as expected"); @@ -2045,7 +2166,7 @@ hx509_verify_path(hx509_context context,  		break;  	    } else { -		/*  +		/*  		 * Now we are done with the proxy certificates, this  		 * cert was an EE cert and we we will fall though to  		 * EE checking below. @@ -2063,15 +2184,19 @@ hx509_verify_path(hx509_context context,  	    if (proxy_cert_depth) {  		ret = _hx509_name_cmp(&proxy_issuer, -				      &c->tbsCertificate.subject); +				      &c->tbsCertificate.subject, &diff);  		if (ret) { +		    hx509_set_error_string(context, 0, ret, "out of memory"); +		    goto out; +		} +		if (diff) {  		    ret = HX509_PROXY_CERT_NAME_WRONG;  		    hx509_clear_error_string(context);  		    goto out;  		}  		if (cert->basename)  		    hx509_name_free(&cert->basename); -		 +  		ret = _hx509_name_from_Name(&proxy_issuer, &cert->basename);  		if (ret) {  		    hx509_clear_error_string(context); @@ -2082,11 +2207,11 @@ hx509_verify_path(hx509_context context,  	    break;  	} -	ret = check_basic_constraints(context, c, type,  +	ret = check_basic_constraints(context, c, type,  				      i - proxy_cert_depth - selfsigned_depth);  	if (ret)  	    goto out; -	     +  	/*  	 * Don't check the trust anchors expiration time since they  	 * are transported out of band, from RFC3820. @@ -2118,13 +2243,19 @@ hx509_verify_path(hx509_context context,       * checked in the right order.       */ -    for (ret = 0, i = path.len - 1; i >= 0; i--) { +    for (ret = 0, k = path.len; k > 0; k--) {  	Certificate *c; +	int selfsigned; +	i = k - 1;  	c = _hx509_get_cert(path.val[i]); +	ret = certificate_is_self_signed(context, c, &selfsigned); +	if (ret) +	    goto out; +  	/* verify name constraints, not for selfsigned and anchor */ -	if (!certificate_is_self_signed(c) || i + 1 != path.len) { +	if (!selfsigned || i + 1 != path.len) {  	    ret = check_name_constraints(context, &nc, c);  	    if (ret) {  		goto out; @@ -2164,10 +2295,10 @@ hx509_verify_path(hx509_context context,  	}  	for (i = 0; i < path.len - 1; i++) { -	    int parent = (i < path.len - 1) ? i + 1 : i; +	    size_t parent = (i < path.len - 1) ? i + 1 : i;  	    ret = hx509_revoke_verify(context, -				      ctx->revoke_ctx,  +				      ctx->revoke_ctx,  				      certs,  				      ctx->time_now,  				      path.val[i], @@ -2185,21 +2316,29 @@ hx509_verify_path(hx509_context context,       * parameter is passed up from the anchor up though the chain.       */ -    for (i = path.len - 1; i >= 0; i--) { -	Certificate *signer, *c; +    for (k = path.len; k > 0; k--) { +	hx509_cert signer; +	Certificate *c; +	i = k - 1;  	c = _hx509_get_cert(path.val[i]);  	/* is last in chain (trust anchor) */  	if (i + 1 == path.len) { -	    signer = path.val[i]->data; +	    int selfsigned; + +	    signer = path.val[i]; + +	    ret = certificate_is_self_signed(context, signer->data, &selfsigned); +	    if (ret) +		goto out;  	    /* if trust anchor is not self signed, don't check sig */ -	    if (!certificate_is_self_signed(signer)) +	    if (!selfsigned)  		continue;  	} else {  	    /* take next certificate in chain */ -	    signer = path.val[i + 1]->data; +	    signer = path.val[i + 1];  	}  	/* verify signatureValue */ @@ -2213,6 +2352,24 @@ hx509_verify_path(hx509_context context,  				   "Failed to verify signature of certificate");  	    goto out;  	} +	/* +	 * Verify that the sigature algorithm "best-before" date is +	 * before the creation date of the certificate, do this for +	 * trust anchors too, since any trust anchor that is created +	 * after a algorithm is known to be bad deserved to be invalid. +	 * +	 * Skip the leaf certificate for now... +	 */ + +	if (i != 0 && (ctx->flags & HX509_VERIFY_CTX_F_NO_BEST_BEFORE_CHECK) == 0) { +	    time_t notBefore = +		_hx509_Time2time_t(&c->tbsCertificate.validity.notBefore); +	    ret = _hx509_signature_best_before(context, +					       &c->signatureAlgorithm, +					       notBefore); +	    if (ret) +		goto out; +	}      }  out: @@ -2245,10 +2402,32 @@ hx509_verify_signature(hx509_context context,  		       const heim_octet_string *data,  		       const heim_octet_string *sig)  { -    return _hx509_verify_signature(context, signer->data, alg, data, sig); +    return _hx509_verify_signature(context, signer, alg, data, sig); +} + +int +_hx509_verify_signature_bitstring(hx509_context context, +				  const hx509_cert signer, +				  const AlgorithmIdentifier *alg, +				  const heim_octet_string *data, +				  const heim_bit_string *sig) +{ +    heim_octet_string os; + +    if (sig->length & 7) { +	hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT, +			       "signature not multiple of 8 bits"); +	return HX509_CRYPTO_SIG_INVALID_FORMAT; +    } + +    os.data = sig->data; +    os.length = sig->length / 8; + +    return _hx509_verify_signature(context, signer, alg, data, &os);  } +  /**   * Verify that the certificate is allowed to be used for the hostname   * and address. @@ -2276,10 +2455,12 @@ hx509_verify_hostname(hx509_context context,  		      hx509_hostname_type type,  		      const char *hostname,  		      const struct sockaddr *sa, -		      /* XXX krb5_socklen_t */ int sa_size)  +		      /* XXX krb5_socklen_t */ int sa_size)  {      GeneralNames san; -    int ret, i, j; +    const Name *name; +    int ret; +    size_t i, j, k;      if (sa && sa_size <= 0)  	return EINVAL; @@ -2289,20 +2470,24 @@ hx509_verify_hostname(hx509_context context,      i = 0;      do {  	ret = find_extension_subject_alt_name(cert->data, &i, &san); -	if (ret == HX509_EXTENSION_NOT_FOUND) { -	    ret = 0; -	    break; -	} else if (ret != 0) +	if (ret == HX509_EXTENSION_NOT_FOUND)  	    break; +	else if (ret != 0) +	    return HX509_PARSING_NAME_FAILED;  	for (j = 0; j < san.len; j++) {  	    switch (san.val[j].element) { -	    case choice_GeneralName_dNSName: -		if (strcasecmp(san.val[j].u.dNSName, hostname) == 0) { +	    case choice_GeneralName_dNSName: { +		heim_printable_string hn; +		hn.data = rk_UNCONST(hostname); +		hn.length = strlen(hostname); + +		if (der_printable_string_cmp(&san.val[j].u.dNSName, &hn) == 0) {  		    free_GeneralNames(&san);  		    return 0;  		}  		break; +	    }  	    default:  		break;  	    } @@ -2310,31 +2495,42 @@ hx509_verify_hostname(hx509_context context,  	free_GeneralNames(&san);      } while (1); -    { -	Name *name = &cert->data->tbsCertificate.subject; - -	/* match if first component is a CN= */ -	if (name->u.rdnSequence.len > 0 -	    && name->u.rdnSequence.val[0].len == 1 -	    && der_heim_oid_cmp(&name->u.rdnSequence.val[0].val[0].type, -				oid_id_at_commonName()) == 0) -	{ -	    DirectoryString *ds = &name->u.rdnSequence.val[0].val[0].value; - -	    switch (ds->element) { -	    case choice_DirectoryString_printableString: -		if (strcasecmp(ds->u.printableString, hostname) == 0) -		    return 0; -		break; -	    case choice_DirectoryString_ia5String: -		if (strcasecmp(ds->u.ia5String, hostname) == 0) -		    return 0; -		break; -	    case choice_DirectoryString_utf8String: -		if (strcasecmp(ds->u.utf8String, hostname) == 0) -		    return 0; -	    default: -		break; +    name = &cert->data->tbsCertificate.subject; + +    /* Find first CN= in the name, and try to match the hostname on that */ +    for (ret = 0, k = name->u.rdnSequence.len; ret == 0 && k > 0; k--) { +	i = k - 1; +	for (j = 0; ret == 0 && j < name->u.rdnSequence.val[i].len; j++) { +	    AttributeTypeAndValue *n = &name->u.rdnSequence.val[i].val[j]; + +	    if (der_heim_oid_cmp(&n->type, &asn1_oid_id_at_commonName) == 0) { +		DirectoryString *ds = &n->value; +		switch (ds->element) { +		case choice_DirectoryString_printableString: { +		    heim_printable_string hn; +		    hn.data = rk_UNCONST(hostname); +		    hn.length = strlen(hostname); + +		    if (der_printable_string_cmp(&ds->u.printableString, &hn) == 0) +			return 0; +		    break; +		} +		case choice_DirectoryString_ia5String: { +		    heim_ia5_string hn; +		    hn.data = rk_UNCONST(hostname); +		    hn.length = strlen(hostname); + +		    if (der_ia5_string_cmp(&ds->u.ia5String, &hn) == 0) +			return 0; +		    break; +		} +		case choice_DirectoryString_utf8String: +		    if (strcasecmp(ds->u.utf8String, hostname) == 0) +			return 0; +		default: +		    break; +		} +		ret = HX509_NAME_CONSTRAINT_ERROR;  	    }  	}      } @@ -2347,8 +2543,8 @@ hx509_verify_hostname(hx509_context context,  int  _hx509_set_cert_attribute(hx509_context context, -			  hx509_cert cert,  -			  const heim_oid *oid,  +			  hx509_cert cert, +			  const heim_oid *oid,  			  const heim_octet_string *attr)  {      hx509_cert_attribute a; @@ -2357,7 +2553,7 @@ _hx509_set_cert_attribute(hx509_context context,      if (hx509_cert_get_attribute(cert, oid) != NULL)  	return 0; -    d = realloc(cert->attrs.val,  +    d = realloc(cert->attrs.val,  		sizeof(cert->attrs.val[0]) * (cert->attrs.len + 1));      if (d == NULL) {  	hx509_clear_error_string(context); @@ -2371,7 +2567,7 @@ _hx509_set_cert_attribute(hx509_context context,      der_copy_octet_string(attr, &a->data);      der_copy_oid(oid, &a->oid); -     +      cert->attrs.val[cert->attrs.len] = a;      cert->attrs.len++; @@ -2394,7 +2590,7 @@ _hx509_set_cert_attribute(hx509_context context,  hx509_cert_attribute  hx509_cert_get_attribute(hx509_cert cert, const heim_oid *oid)  { -    int i; +    size_t i;      for (i = 0; i < cert->attrs.len; i++)  	if (der_heim_oid_cmp(oid, &cert->attrs.val[i]->oid) == 0)  	    return cert->attrs.val[i]; @@ -2440,32 +2636,41 @@ hx509_cert_get_friendly_name(hx509_cert cert)      hx509_cert_attribute a;      PKCS9_friendlyName n;      size_t sz; -    int ret, i; +    int ret; +    size_t i;      if (cert->friendlyname)  	return cert->friendlyname; -    a = hx509_cert_get_attribute(cert, oid_id_pkcs_9_at_friendlyName()); +    a = hx509_cert_get_attribute(cert, &asn1_oid_id_pkcs_9_at_friendlyName);      if (a == NULL) { -	/* XXX use subject name ? */ -	return NULL;  +	hx509_name name; + +	ret = hx509_cert_get_subject(cert, &name); +	if (ret) +	    return NULL; +	ret = hx509_name_to_string(name, &cert->friendlyname); +	hx509_name_free(&name); +	if (ret) +	    return NULL; +	return cert->friendlyname;      }      ret = decode_PKCS9_friendlyName(a->data.data, a->data.length, &n, &sz);      if (ret)  	return NULL; -	 +      if (n.len != 1) {  	free_PKCS9_friendlyName(&n);  	return NULL;      } -     +      cert->friendlyname = malloc(n.val[0].length + 1);      if (cert->friendlyname == NULL) {  	free_PKCS9_friendlyName(&n);  	return NULL;      } -     +      for (i = 0; i < n.val[0].length; i++) {  	if (n.val[0].data[i] <= 0xff)  	    cert->friendlyname[i] = n.val[0].data[i] & 0xff; @@ -2504,6 +2709,7 @@ hx509_query_alloc(hx509_context context, hx509_query **q)      return 0;  } +  /**   * Set match options for the hx509 query controller.   * @@ -2552,7 +2758,7 @@ hx509_query_match_option(hx509_query *q, hx509_query_option option)  int  hx509_query_match_issuer_serial(hx509_query *q, -				const Name *issuer,  +				const Name *issuer,  				const heim_integer *serialNumber)  {      int ret; @@ -2610,6 +2816,69 @@ hx509_query_match_friendly_name(hx509_query *q, const char *name)  }  /** + * Set the query controller to require an one specific EKU (extended + * key usage). Any previous EKU matching is overwitten. If NULL is + * passed in as the eku, the EKU requirement is reset. + * + * @param q a hx509 query controller. + * @param eku an EKU to match on. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + +int +hx509_query_match_eku(hx509_query *q, const heim_oid *eku) +{ +    int ret; + +    if (eku == NULL) { +	if (q->eku) { +	    der_free_oid(q->eku); +	    free(q->eku); +	    q->eku = NULL; +	} +	q->match &= ~HX509_QUERY_MATCH_EKU; +    } else { +	if (q->eku) { +	    der_free_oid(q->eku); +	} else { +	    q->eku = calloc(1, sizeof(*q->eku)); +	    if (q->eku == NULL) +		return ENOMEM; +	} +	ret = der_copy_oid(eku, q->eku); +	if (ret) { +	    free(q->eku); +	    q->eku = NULL; +	    return ret; +	} +	q->match |= HX509_QUERY_MATCH_EKU; +    } +    return 0; +} + +int +hx509_query_match_expr(hx509_context context, hx509_query *q, const char *expr) +{ +    if (q->expr) { +	_hx509_expr_free(q->expr); +	q->expr = NULL; +    } + +    if (expr == NULL) { +	q->match &= ~HX509_QUERY_MATCH_EXPR; +    } else { +	q->expr = _hx509_expr_parse(expr); +	if (q->expr) +	    q->match |= HX509_QUERY_MATCH_EXPR; +    } + +    return 0; +} + +/**   * Set the query controller to match using a specific match function.   *   * @param q a hx509 query controller. @@ -2624,7 +2893,7 @@ hx509_query_match_friendly_name(hx509_query *q, const char *name)  int  hx509_query_match_cmp_func(hx509_query *q, -			   int (*func)(void *, hx509_cert), +			   int (*func)(hx509_context, hx509_cert, void *),  			   void *ctx)  {      if (func) @@ -2648,20 +2917,27 @@ hx509_query_match_cmp_func(hx509_query *q,  void  hx509_query_free(hx509_context context, hx509_query *q)  { +    if (q == NULL) +	return; +      if (q->serial) {  	der_free_heim_integer(q->serial);  	free(q->serial); -	q->serial = NULL;      }      if (q->issuer_name) {  	free_Name(q->issuer_name);  	free(q->issuer_name); -	q->issuer_name = NULL;      } -    if (q) { -	free(q->friendlyname); -	memset(q, 0, sizeof(*q)); +    if (q->eku) { +	der_free_oid(q->eku); +	free(q->eku);      } +    if (q->friendlyname) +	free(q->friendlyname); +    if (q->expr) +	_hx509_expr_free(q->expr); + +    memset(q, 0, sizeof(*q));      free(q);  } @@ -2669,6 +2945,7 @@ int  _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert cert)  {      Certificate *c = _hx509_get_cert(cert); +    int ret, diff;      _hx509_query_statistic(context, 1, q); @@ -2684,17 +2961,20 @@ _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert  	&& der_heim_integer_cmp(&c->tbsCertificate.serialNumber, q->serial) != 0)  	return 0; -    if ((q->match & HX509_QUERY_MATCH_ISSUER_NAME) -	&& _hx509_name_cmp(&c->tbsCertificate.issuer, q->issuer_name) != 0) -	return 0; +    if (q->match & HX509_QUERY_MATCH_ISSUER_NAME) { +	ret = _hx509_name_cmp(&c->tbsCertificate.issuer, q->issuer_name, &diff); +	if (ret || diff) +	    return 0; +    } -    if ((q->match & HX509_QUERY_MATCH_SUBJECT_NAME) -	&& _hx509_name_cmp(&c->tbsCertificate.subject, q->subject_name) != 0) -	return 0; +    if (q->match & HX509_QUERY_MATCH_SUBJECT_NAME) { +	ret = _hx509_name_cmp(&c->tbsCertificate.subject, q->subject_name, &diff); +	if (ret || diff) +	    return 0; +    }      if (q->match & HX509_QUERY_MATCH_SUBJECT_KEY_ID) {  	SubjectKeyIdentifier si; -	int ret;  	ret = _hx509_find_extension_subject_key_id(c, &si);  	if (ret == 0) { @@ -2707,7 +2987,7 @@ _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert      }      if ((q->match & HX509_QUERY_MATCH_ISSUER_ID))  	return 0; -    if ((q->match & HX509_QUERY_PRIVATE_KEY) &&  +    if ((q->match & HX509_QUERY_PRIVATE_KEY) &&  	_hx509_cert_private_key(cert) == NULL)  	return 0; @@ -2736,7 +3016,7 @@ _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert      if (q->match & HX509_QUERY_MATCH_LOCAL_KEY_ID) {  	hx509_cert_attribute a; -	a = hx509_cert_get_attribute(cert, oid_id_pkcs_9_at_localKeyId()); +	a = hx509_cert_get_attribute(cert, &asn1_oid_id_pkcs_9_at_localKeyId);  	if (a == NULL)  	    return 0;  	if (der_heim_octet_string_cmp(&a->data, q->local_key_id) != 0) @@ -2758,17 +3038,16 @@ _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert  	    return 0;      }      if (q->match & HX509_QUERY_MATCH_FUNCTION) { -	int ret = (*q->cmp_func)(q->cmp_func_ctx, cert); +	ret = (*q->cmp_func)(context, cert, q->cmp_func_ctx);  	if (ret != 0)  	    return 0;      }      if (q->match & HX509_QUERY_MATCH_KEY_HASH_SHA1) {  	heim_octet_string os; -	int ret;  	os.data = c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data; -	os.length =  +	os.length =  	    c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;  	ret = _hx509_verify_signature(context, @@ -2790,6 +3069,24 @@ _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert  	    return 0;      } +    /* If an EKU is required, check the cert for it. */ +    if ((q->match & HX509_QUERY_MATCH_EKU) && +	hx509_cert_check_eku(context, cert, q->eku, 0)) +	return 0; + +    if ((q->match & HX509_QUERY_MATCH_EXPR)) { +	hx509_env env = NULL; + +	ret = _hx509_cert_to_env(context, cert, &env); +	if (ret) +	    return 0; + +	ret = _hx509_expr_eval(context, env, q->expr); +	hx509_env_free(&env); +	if (ret == 0) +	    return 0; +    } +      if (q->match & ~HX509_QUERY_MASK)  	return 0; @@ -2822,6 +3119,7 @@ _hx509_query_statistic(hx509_context context, int type, const hx509_query *q)      f = fopen(context->querystat, "a");      if (f == NULL)  	return; +    rk_cloexec_file(f);      fprintf(f, "%d %d\n", type, q->match);      fclose(f);  } @@ -2880,7 +3178,8 @@ hx509_query_unparse_stats(hx509_context context, int printtype, FILE *out)  {      rtbl_t t;      FILE *f; -    int type, mask, i, num; +    int type, mask, num; +    size_t i;      unsigned long multiqueries = 0, totalqueries = 0;      struct stat_el stats[32]; @@ -2888,11 +3187,12 @@ hx509_query_unparse_stats(hx509_context context, int printtype, FILE *out)  	return;      f = fopen(context->querystat, "r");      if (f == NULL) { -	fprintf(out, "No statistic file %s: %s.\n",  +	fprintf(out, "No statistic file %s: %s.\n",  		context->querystat, strerror(errno));  	return;      } -     +    rk_cloexec_file(f); +      for (i = 0; i < sizeof(stats)/sizeof(stats[0]); i++) {  	stats[i].index = i;  	stats[i].stats = 0; @@ -2923,7 +3223,7 @@ hx509_query_unparse_stats(hx509_context context, int printtype, FILE *out)  	errx(1, "out of memory");      rtbl_set_separator (t, "  "); -     +      rtbl_add_column_by_id (t, 0, "Name", 0);      rtbl_add_column_by_id (t, 1, "Counter", 0); @@ -2931,7 +3231,7 @@ hx509_query_unparse_stats(hx509_context context, int printtype, FILE *out)      for (i = 0; i < sizeof(stats)/sizeof(stats[0]); i++) {  	char str[10]; -	if (stats[i].index < sizeof(statname)/sizeof(statname[0]))  +	if (stats[i].index < sizeof(statname)/sizeof(statname[0]))  	    rtbl_add_column_entry_by_id (t, 0, statname[stats[i].index]);  	else {  	    snprintf(str, sizeof(str), "%d", stats[i].index); @@ -2944,7 +3244,7 @@ hx509_query_unparse_stats(hx509_context context, int printtype, FILE *out)      rtbl_format(t, out);      rtbl_destroy(t); -    fprintf(out, "\nQueries: multi %lu total %lu\n",  +    fprintf(out, "\nQueries: multi %lu total %lu\n",  	    multiqueries, totalqueries);  } @@ -2967,7 +3267,8 @@ hx509_cert_check_eku(hx509_context context, hx509_cert cert,  		     const heim_oid *eku, int allow_any_eku)  {      ExtKeyUsage e; -    int ret, i; +    int ret; +    size_t i;      ret = find_extension_eku(_hx509_get_cert(cert), &e);      if (ret) { @@ -3002,7 +3303,8 @@ _hx509_cert_get_keyusage(hx509_context context,      Certificate *cert;      const Extension *e;      size_t size; -    int ret, i = 0; +    int ret; +    size_t i = 0;      memset(ku, 0, sizeof(*ku)); @@ -3011,10 +3313,10 @@ _hx509_cert_get_keyusage(hx509_context context,      if (_hx509_cert_get_version(cert) < 3)  	return 0; -    e = find_extension(cert, oid_id_x509_ce_keyUsage(), &i); +    e = find_extension(cert, &asn1_oid_id_x509_ce_keyUsage, &i);      if (e == NULL)  	return HX509_KU_CERT_MISSING; -     +      ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length, ku, &size);      if (ret)  	return ret; @@ -3044,7 +3346,7 @@ _hx509_cert_get_eku(hx509_context context,   * @param context A hx509 context.   * @param c the certificate to encode.   * @param os the encode certificate, set to NULL, 0 on case of - * error. Free the returned structure with hx509_xfree(). + * error. Free the os->data with hx509_xfree().   *   * @return An hx509 error code, see hx509_get_error_string().   * @@ -3060,7 +3362,7 @@ hx509_cert_binary(hx509_context context, hx509_cert c, heim_octet_string *os)      os->data = NULL;      os->length = 0; -    ASN1_MALLOC_ENCODE(Certificate, os->data, os->length,  +    ASN1_MALLOC_ENCODE(Certificate, os->data, os->length,  		       _hx509_get_cert(c), &size, ret);      if (ret) {  	os->data = NULL; @@ -3106,3 +3408,205 @@ hx509_xfree(void *ptr)  {      free(ptr);  } + +/** + * + */ + +int +_hx509_cert_to_env(hx509_context context, hx509_cert cert, hx509_env *env) +{ +    ExtKeyUsage eku; +    hx509_name name; +    char *buf; +    int ret; +    hx509_env envcert = NULL; + +    *env = NULL; + +    /* version */ +    asprintf(&buf, "%d", _hx509_cert_get_version(_hx509_get_cert(cert))); +    ret = hx509_env_add(context, &envcert, "version", buf); +    free(buf); +    if (ret) +	goto out; + +    /* subject */ +    ret = hx509_cert_get_subject(cert, &name); +    if (ret) +	goto out; + +    ret = hx509_name_to_string(name, &buf); +    if (ret) { +	hx509_name_free(&name); +	goto out; +    } + +    ret = hx509_env_add(context, &envcert, "subject", buf); +    hx509_name_free(&name); +    if (ret) +	goto out; + +    /* issuer */ +    ret = hx509_cert_get_issuer(cert, &name); +    if (ret) +	goto out; + +    ret = hx509_name_to_string(name, &buf); +    hx509_name_free(&name); +    if (ret) +	goto out; + +    ret = hx509_env_add(context, &envcert, "issuer", buf); +    hx509_xfree(buf); +    if (ret) +	goto out; + +    /* eku */ + +    ret = _hx509_cert_get_eku(context, cert, &eku); +    if (ret == HX509_EXTENSION_NOT_FOUND) +	; +    else if (ret != 0) +	goto out; +    else { +	size_t i; +	hx509_env enveku = NULL; + +	for (i = 0; i < eku.len; i++) { + +	    ret = der_print_heim_oid(&eku.val[i], '.', &buf); +	    if (ret) { +		free_ExtKeyUsage(&eku); +		hx509_env_free(&enveku); +		goto out; +	    } +	    ret = hx509_env_add(context, &enveku, buf, "oid-name-here"); +	    free(buf); +	    if (ret) { +		free_ExtKeyUsage(&eku); +		hx509_env_free(&enveku); +		goto out; +	    } +	} +	free_ExtKeyUsage(&eku); + +	ret = hx509_env_add_binding(context, &envcert, "eku", enveku); +	if (ret) { +	    hx509_env_free(&enveku); +	    goto out; +	} +    } + +    { +	Certificate *c = _hx509_get_cert(cert); +        heim_octet_string os, sig; +	hx509_env envhash = NULL; + +	os.data = c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data; +	os.length = +	  c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8; + +	ret = _hx509_create_signature(context, +				      NULL, +				      hx509_signature_sha1(), +				      &os, +				      NULL, +				      &sig); +	if (ret != 0) +	    goto out; + +	ret = hex_encode(sig.data, sig.length, &buf); +	der_free_octet_string(&sig); +	if (ret < 0) { +	    ret = ENOMEM; +	    hx509_set_error_string(context, 0, ret, +				   "Out of memory"); +	    goto out; +	} + +	ret = hx509_env_add(context, &envhash, "sha1", buf); +	free(buf); +	if (ret) +	    goto out; + +	ret = hx509_env_add_binding(context, &envcert, "hash", envhash); +	if (ret) { +	  hx509_env_free(&envhash); +	  goto out; +	} +    } + +    ret = hx509_env_add_binding(context, env, "certificate", envcert); +    if (ret) +	goto out; + +    return 0; + +out: +    hx509_env_free(&envcert); +    return ret; +} + +/** + * Print a simple representation of a certificate + * + * @param context A hx509 context, can be NULL + * @param cert certificate to print + * @param out the stdio output stream, if NULL, stdout is used + * + * @return An hx509 error code + * + * @ingroup hx509_cert + */ + +int +hx509_print_cert(hx509_context context, hx509_cert cert, FILE *out) +{ +    hx509_name name; +    char *str; +    int ret; + +    if (out == NULL) +	out = stderr; + +    ret = hx509_cert_get_issuer(cert, &name); +    if (ret) +	return ret; +    hx509_name_to_string(name, &str); +    hx509_name_free(&name); +    fprintf(out, "    issuer:  \"%s\"\n", str); +    free(str); + +    ret = hx509_cert_get_subject(cert, &name); +    if (ret) +	return ret; +    hx509_name_to_string(name, &str); +    hx509_name_free(&name); +    fprintf(out, "    subject: \"%s\"\n", str); +    free(str); + +    { +	heim_integer serialNumber; + +	ret = hx509_cert_get_serialnumber(cert, &serialNumber); +	if (ret) +	    return ret; +	ret = der_print_hex_heim_integer(&serialNumber, &str); +	if (ret) +	    return ret; +	der_free_heim_integer(&serialNumber); +	fprintf(out, "    serial: %s\n", str); +	free(str); +    } + +    printf("    keyusage: "); +    ret = hx509_cert_keyusage_print(context, cert, &str); +    if (ret == 0) { +	fprintf(out, "%s\n", str); +	free(str); +    } else +	fprintf(out, "no"); + +    return 0; +} | 
