git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@21379 ec53bebd-3082-4978-b11e-865c3cabbd6b
		
			
				
	
	
		
			1195 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1195 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2006 - 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"
 | 
						|
#include <pkinit_asn1.h>
 | 
						|
RCSID("$Id$");
 | 
						|
 | 
						|
struct hx509_ca_tbs {
 | 
						|
    hx509_name subject;
 | 
						|
    SubjectPublicKeyInfo spki;
 | 
						|
    ExtKeyUsage eku;
 | 
						|
    GeneralNames san;
 | 
						|
    unsigned key_usage;
 | 
						|
    heim_integer serial;
 | 
						|
    struct {
 | 
						|
	unsigned int proxy:1;
 | 
						|
	unsigned int ca:1;
 | 
						|
	unsigned int key:1;
 | 
						|
	unsigned int serial:1;
 | 
						|
	unsigned int domaincontroller:1;
 | 
						|
    } flags;
 | 
						|
    time_t notBefore;
 | 
						|
    time_t notAfter;
 | 
						|
    int pathLenConstraint; /* both for CA and Proxy */
 | 
						|
    CRLDistributionPoints crldp;
 | 
						|
};
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_init(hx509_context context, hx509_ca_tbs *tbs)
 | 
						|
{
 | 
						|
    *tbs = calloc(1, sizeof(**tbs));
 | 
						|
    if (*tbs == NULL)
 | 
						|
	return ENOMEM;
 | 
						|
 | 
						|
    (*tbs)->subject = NULL;
 | 
						|
    (*tbs)->san.len = 0;
 | 
						|
    (*tbs)->san.val = NULL;
 | 
						|
    (*tbs)->eku.len = 0;
 | 
						|
    (*tbs)->eku.val = NULL;
 | 
						|
    (*tbs)->pathLenConstraint = 0;
 | 
						|
    (*tbs)->crldp.len = 0;
 | 
						|
    (*tbs)->crldp.val = NULL;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
hx509_ca_tbs_free(hx509_ca_tbs *tbs)
 | 
						|
{
 | 
						|
    if (tbs == NULL || *tbs == NULL)
 | 
						|
	return;
 | 
						|
 | 
						|
    free_SubjectPublicKeyInfo(&(*tbs)->spki);
 | 
						|
    free_GeneralNames(&(*tbs)->san);
 | 
						|
    free_ExtKeyUsage(&(*tbs)->eku);
 | 
						|
    der_free_heim_integer(&(*tbs)->serial);
 | 
						|
    free_CRLDistributionPoints(&(*tbs)->crldp);
 | 
						|
 | 
						|
    hx509_name_free(&(*tbs)->subject);
 | 
						|
 | 
						|
    memset(*tbs, 0, sizeof(**tbs));
 | 
						|
    free(*tbs);
 | 
						|
    *tbs = NULL;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_set_notBefore(hx509_context context,
 | 
						|
			   hx509_ca_tbs tbs,
 | 
						|
			   time_t t)
 | 
						|
{
 | 
						|
    tbs->notBefore = t;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_set_notAfter(hx509_context context,
 | 
						|
			   hx509_ca_tbs tbs,
 | 
						|
			   time_t t)
 | 
						|
{
 | 
						|
    tbs->notAfter = t;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_set_notAfter_lifetime(hx509_context context,
 | 
						|
				   hx509_ca_tbs tbs,
 | 
						|
				   time_t delta)
 | 
						|
{
 | 
						|
    return hx509_ca_tbs_set_notAfter(context, tbs, time(NULL) + delta);
 | 
						|
}
 | 
						|
 | 
						|
static const struct units templatebits[] = {
 | 
						|
    { "ExtendedKeyUsage", HX509_CA_TEMPLATE_EKU },
 | 
						|
    { "KeyUsage", HX509_CA_TEMPLATE_KU },
 | 
						|
    { "SPKI", HX509_CA_TEMPLATE_SPKI },
 | 
						|
    { "notAfter", HX509_CA_TEMPLATE_NOTAFTER },
 | 
						|
    { "notBefore", HX509_CA_TEMPLATE_NOTBEFORE },
 | 
						|
    { "serial", HX509_CA_TEMPLATE_SERIAL },
 | 
						|
    { "subject", HX509_CA_TEMPLATE_SUBJECT },
 | 
						|
    { NULL, 0 }
 | 
						|
};
 | 
						|
 | 
						|
const struct units *
 | 
						|
hx509_ca_tbs_template_units(void)
 | 
						|
{
 | 
						|
    return templatebits;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_set_template(hx509_context context,
 | 
						|
			  hx509_ca_tbs tbs,
 | 
						|
			  int flags,
 | 
						|
			  hx509_cert cert)
 | 
						|
{
 | 
						|
    int ret;
 | 
						|
 | 
						|
    if (flags & HX509_CA_TEMPLATE_SUBJECT) {
 | 
						|
	if (tbs->subject)
 | 
						|
	    hx509_name_free(&tbs->subject);
 | 
						|
	ret = hx509_cert_get_subject(cert, &tbs->subject);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, 
 | 
						|
				   "Failed to get subject from template");
 | 
						|
	    return ret;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    if (flags & HX509_CA_TEMPLATE_SERIAL) {
 | 
						|
	der_free_heim_integer(&tbs->serial);
 | 
						|
	ret = hx509_cert_get_serialnumber(cert, &tbs->serial);
 | 
						|
	tbs->flags.serial = !ret;
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, 
 | 
						|
				   "Failed to copy serial number");
 | 
						|
	    return ret;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    if (flags & HX509_CA_TEMPLATE_NOTBEFORE)
 | 
						|
	tbs->notBefore = hx509_cert_get_notBefore(cert);
 | 
						|
    if (flags & HX509_CA_TEMPLATE_NOTAFTER)
 | 
						|
	tbs->notAfter = hx509_cert_get_notAfter(cert);
 | 
						|
    if (flags & HX509_CA_TEMPLATE_SPKI) {
 | 
						|
	free_SubjectPublicKeyInfo(&tbs->spki);
 | 
						|
	ret = hx509_cert_get_SPKI(cert, &tbs->spki);
 | 
						|
	tbs->flags.key = !ret;
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Failed to copy SPKI");
 | 
						|
	    return ret;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    if (flags & HX509_CA_TEMPLATE_KU) {
 | 
						|
	KeyUsage ku;
 | 
						|
	ret = _hx509_cert_get_keyusage(context, cert, &ku);
 | 
						|
	if (ret)
 | 
						|
	    return ret;
 | 
						|
	tbs->key_usage = KeyUsage2int(ku);
 | 
						|
    }
 | 
						|
    if (flags & HX509_CA_TEMPLATE_EKU) {
 | 
						|
	ExtKeyUsage eku;
 | 
						|
	int i;
 | 
						|
	ret = _hx509_cert_get_eku(context, cert, &eku);
 | 
						|
	if (ret)
 | 
						|
	    return ret;
 | 
						|
	for (i = 0; i < eku.len; i++) {
 | 
						|
	    ret = hx509_ca_tbs_add_eku(context, tbs, &eku.val[i]);
 | 
						|
	    if (ret) {
 | 
						|
		free_ExtKeyUsage(&eku);
 | 
						|
		return ret;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	free_ExtKeyUsage(&eku);
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_set_ca(hx509_context context,
 | 
						|
		    hx509_ca_tbs tbs,
 | 
						|
		    int pathLenConstraint)
 | 
						|
{
 | 
						|
    tbs->flags.ca = 1;
 | 
						|
    tbs->pathLenConstraint = pathLenConstraint;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_set_proxy(hx509_context context,
 | 
						|
		       hx509_ca_tbs tbs,
 | 
						|
		       int pathLenConstraint)
 | 
						|
{
 | 
						|
    tbs->flags.proxy = 1;
 | 
						|
    tbs->pathLenConstraint = pathLenConstraint;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_set_domaincontroller(hx509_context context,
 | 
						|
				  hx509_ca_tbs tbs)
 | 
						|
{
 | 
						|
    tbs->flags.domaincontroller = 1;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_set_spki(hx509_context context,
 | 
						|
		      hx509_ca_tbs tbs,
 | 
						|
		      const SubjectPublicKeyInfo *spki)
 | 
						|
{
 | 
						|
    int ret;
 | 
						|
    free_SubjectPublicKeyInfo(&tbs->spki);
 | 
						|
    ret = copy_SubjectPublicKeyInfo(spki, &tbs->spki);
 | 
						|
    tbs->flags.key = !ret;
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_set_serialnumber(hx509_context context,
 | 
						|
			      hx509_ca_tbs tbs,
 | 
						|
			      const heim_integer *serialNumber)
 | 
						|
{
 | 
						|
    int ret;
 | 
						|
    der_free_heim_integer(&tbs->serial);
 | 
						|
    ret = der_copy_heim_integer(serialNumber, &tbs->serial);
 | 
						|
    tbs->flags.serial = !ret;
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_add_eku(hx509_context context,
 | 
						|
		     hx509_ca_tbs tbs,
 | 
						|
		     const heim_oid *oid)
 | 
						|
{
 | 
						|
    void *ptr;
 | 
						|
    int ret;
 | 
						|
    unsigned i;
 | 
						|
 | 
						|
    /* search for duplicates */
 | 
						|
    for (i = 0; i < tbs->eku.len; i++) {
 | 
						|
	if (der_heim_oid_cmp(oid, &tbs->eku.val[i]) == 0)
 | 
						|
	    return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    ptr = realloc(tbs->eku.val, sizeof(tbs->eku.val[0]) * (tbs->eku.len + 1));
 | 
						|
    if (ptr == NULL) {
 | 
						|
	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
 | 
						|
	return ENOMEM;
 | 
						|
    }
 | 
						|
    tbs->eku.val = ptr;
 | 
						|
    ret = der_copy_oid(oid, &tbs->eku.val[tbs->eku.len]);
 | 
						|
    if (ret) {
 | 
						|
	hx509_set_error_string(context, 0, ret, "out of memory");
 | 
						|
	return ret;
 | 
						|
    }
 | 
						|
    tbs->eku.len += 1;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_add_crl_dp_uri(hx509_context context,
 | 
						|
			    hx509_ca_tbs tbs,
 | 
						|
			    const char *uri,
 | 
						|
			    hx509_name issuername)
 | 
						|
{
 | 
						|
    DistributionPoint dp;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    memset(&dp, 0, sizeof(dp));
 | 
						|
    
 | 
						|
    dp.distributionPoint = ecalloc(1, sizeof(*dp.distributionPoint));
 | 
						|
 | 
						|
    {
 | 
						|
	DistributionPointName name;
 | 
						|
	GeneralName gn;
 | 
						|
	size_t size;
 | 
						|
 | 
						|
	name.element = choice_DistributionPointName_fullName;
 | 
						|
	name.u.fullName.len = 1;
 | 
						|
	name.u.fullName.val = &gn;
 | 
						|
 | 
						|
	gn.element = choice_GeneralName_uniformResourceIdentifier;
 | 
						|
	gn.u.uniformResourceIdentifier = rk_UNCONST(uri);
 | 
						|
 | 
						|
	ASN1_MALLOC_ENCODE(DistributionPointName, 
 | 
						|
			   dp.distributionPoint->data, 
 | 
						|
			   dp.distributionPoint->length,
 | 
						|
			   &name, &size, ret);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret,
 | 
						|
				   "Failed to encoded DistributionPointName");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	if (dp.distributionPoint->length != size)
 | 
						|
	    _hx509_abort("internal ASN.1 encoder error");
 | 
						|
    }
 | 
						|
 | 
						|
    if (issuername) {
 | 
						|
#if 1
 | 
						|
	hx509_set_error_string(context, 0, EINVAL,
 | 
						|
			       "CRLDistributionPoints.name.issuername not yet supported");
 | 
						|
	return EINVAL;
 | 
						|
#else 
 | 
						|
	GeneralNames *crlissuer;
 | 
						|
	GeneralName gn;
 | 
						|
	Name n;
 | 
						|
 | 
						|
	crlissuer = calloc(1, sizeof(*crlissuer));
 | 
						|
	if (crlissuer == NULL) {
 | 
						|
	    return ENOMEM;
 | 
						|
	}
 | 
						|
	memset(&gn, 0, sizeof(gn));
 | 
						|
 | 
						|
	gn.element = choice_GeneralName_directoryName;
 | 
						|
	ret = hx509_name_to_Name(issuername, &n);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	gn.u.directoryName.element = n.element;
 | 
						|
	gn.u.directoryName.u.rdnSequence = n.u.rdnSequence;
 | 
						|
 | 
						|
	ret = add_GeneralNames(&crlissuer, &gn);
 | 
						|
	free_Name(&n);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	dp.cRLIssuer = &crlissuer;
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    ret = add_CRLDistributionPoints(&tbs->crldp, &dp);
 | 
						|
    if (ret) {
 | 
						|
	hx509_set_error_string(context, 0, ret, "out of memory");
 | 
						|
	goto out;
 | 
						|
    }
 | 
						|
 | 
						|
out:
 | 
						|
    free_DistributionPoint(&dp);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_add_san_otherName(hx509_context context,
 | 
						|
			       hx509_ca_tbs tbs,
 | 
						|
			       const heim_oid *oid,
 | 
						|
			       const heim_octet_string *os)
 | 
						|
{
 | 
						|
    GeneralName gn;
 | 
						|
 | 
						|
    memset(&gn, 0, sizeof(gn));
 | 
						|
    gn.element = choice_GeneralName_otherName;
 | 
						|
    gn.u.otherName.type_id = *oid;
 | 
						|
    gn.u.otherName.value = *os;
 | 
						|
    
 | 
						|
    return add_GeneralNames(&tbs->san, &gn);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_add_san_pkinit(hx509_context context,
 | 
						|
			    hx509_ca_tbs tbs,
 | 
						|
			    const char *principal)
 | 
						|
{
 | 
						|
    heim_octet_string os;
 | 
						|
    KRB5PrincipalName p;
 | 
						|
    size_t size;
 | 
						|
    int ret;
 | 
						|
    char *s = NULL;
 | 
						|
 | 
						|
    memset(&p, 0, sizeof(p));
 | 
						|
 | 
						|
    /* parse principal */
 | 
						|
    {
 | 
						|
	const char *str;
 | 
						|
	char *q;
 | 
						|
	int n;
 | 
						|
	
 | 
						|
	/* count number of component */
 | 
						|
	n = 1;
 | 
						|
	for(str = principal; *str != '\0' && *str != '@'; str++){
 | 
						|
	    if(*str=='\\'){
 | 
						|
		if(str[1] == '\0' || str[1] == '@') {
 | 
						|
		    ret = HX509_PARSING_NAME_FAILED;
 | 
						|
		    hx509_set_error_string(context, 0, ret, 
 | 
						|
					   "trailing \\ in principal name");
 | 
						|
		    goto out;
 | 
						|
		}
 | 
						|
		str++;
 | 
						|
	    } else if(*str == '/')
 | 
						|
		n++;
 | 
						|
	}
 | 
						|
	p.principalName.name_string.val = 
 | 
						|
	    calloc(n, sizeof(*p.principalName.name_string.val));
 | 
						|
	if (p.principalName.name_string.val == NULL) {
 | 
						|
	    ret = ENOMEM;
 | 
						|
	    hx509_set_error_string(context, 0, ret, "malloc: out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	p.principalName.name_string.len = n;
 | 
						|
	
 | 
						|
	p.principalName.name_type = KRB5_NT_PRINCIPAL;
 | 
						|
	q = s = strdup(principal);
 | 
						|
	if (q == NULL) {
 | 
						|
	    ret = ENOMEM;
 | 
						|
	    hx509_set_error_string(context, 0, ret, "malloc: out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	p.realm = strrchr(q, '@');
 | 
						|
	if (p.realm == NULL) {
 | 
						|
	    ret = HX509_PARSING_NAME_FAILED;
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Missing @ in principal");
 | 
						|
	    goto out;
 | 
						|
	};
 | 
						|
	*p.realm++ = '\0';
 | 
						|
 | 
						|
	n = 0;
 | 
						|
	while (q) {
 | 
						|
	    p.principalName.name_string.val[n++] = q;
 | 
						|
	    q = strchr(q, '/');
 | 
						|
	    if (q)
 | 
						|
		*q++ = '\0';
 | 
						|
	}
 | 
						|
    }
 | 
						|
    
 | 
						|
    ASN1_MALLOC_ENCODE(KRB5PrincipalName, os.data, os.length, &p, &size, ret);
 | 
						|
    if (ret) {
 | 
						|
	hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	goto out;
 | 
						|
    }
 | 
						|
    if (size != os.length)
 | 
						|
	_hx509_abort("internal ASN.1 encoder error");
 | 
						|
    
 | 
						|
    ret = hx509_ca_tbs_add_san_otherName(context,
 | 
						|
					 tbs,
 | 
						|
					 oid_id_pkinit_san(),
 | 
						|
					 &os);
 | 
						|
    free(os.data);
 | 
						|
out:
 | 
						|
    if (p.principalName.name_string.val)
 | 
						|
	free (p.principalName.name_string.val);
 | 
						|
    if (s)
 | 
						|
	free(s);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
    
 | 
						|
/*
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
static int
 | 
						|
add_utf8_san(hx509_context context,
 | 
						|
	     hx509_ca_tbs tbs,
 | 
						|
	     const heim_oid *oid,
 | 
						|
	     const char *string)
 | 
						|
{
 | 
						|
    const PKIXXmppAddr ustring = (const PKIXXmppAddr)string;
 | 
						|
    heim_octet_string os;
 | 
						|
    size_t size;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    os.length = 0;
 | 
						|
    os.data = NULL;
 | 
						|
 | 
						|
    ASN1_MALLOC_ENCODE(PKIXXmppAddr, os.data, os.length, &ustring, &size, ret);
 | 
						|
    if (ret) {
 | 
						|
	hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	goto out;
 | 
						|
    }
 | 
						|
    if (size != os.length)
 | 
						|
	_hx509_abort("internal ASN.1 encoder error");
 | 
						|
    
 | 
						|
    ret = hx509_ca_tbs_add_san_otherName(context,
 | 
						|
					 tbs,
 | 
						|
					 oid,
 | 
						|
					 &os);
 | 
						|
    free(os.data);
 | 
						|
out:
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_add_san_ms_upn(hx509_context context,
 | 
						|
			    hx509_ca_tbs tbs,
 | 
						|
			    const char *principal)
 | 
						|
{
 | 
						|
    return add_utf8_san(context, tbs, oid_id_pkinit_ms_san(), principal);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_add_san_jid(hx509_context context,
 | 
						|
			 hx509_ca_tbs tbs,
 | 
						|
			 const char *jid)
 | 
						|
{
 | 
						|
    return add_utf8_san(context, tbs, oid_id_pkix_on_xmppAddr(), jid);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_add_san_hostname(hx509_context context,
 | 
						|
			      hx509_ca_tbs tbs,
 | 
						|
			      const char *dnsname)
 | 
						|
{
 | 
						|
    GeneralName gn;
 | 
						|
 | 
						|
    memset(&gn, 0, sizeof(gn));
 | 
						|
    gn.element = choice_GeneralName_dNSName;
 | 
						|
    gn.u.dNSName = rk_UNCONST(dnsname);
 | 
						|
    
 | 
						|
    return add_GeneralNames(&tbs->san, &gn);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_add_san_rfc822name(hx509_context context,
 | 
						|
				hx509_ca_tbs tbs,
 | 
						|
				const char *rfc822Name)
 | 
						|
{
 | 
						|
    GeneralName gn;
 | 
						|
 | 
						|
    memset(&gn, 0, sizeof(gn));
 | 
						|
    gn.element = choice_GeneralName_rfc822Name;
 | 
						|
    gn.u.rfc822Name = rk_UNCONST(rfc822Name);
 | 
						|
    
 | 
						|
    return add_GeneralNames(&tbs->san, &gn);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_set_subject(hx509_context context,
 | 
						|
			 hx509_ca_tbs tbs,
 | 
						|
			 hx509_name subject)
 | 
						|
{
 | 
						|
    if (tbs->subject)
 | 
						|
	hx509_name_free(&tbs->subject);
 | 
						|
    return hx509_name_copy(context, subject, &tbs->subject);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_tbs_subject_expand(hx509_context context,
 | 
						|
			    hx509_ca_tbs tbs,
 | 
						|
			    hx509_env env)
 | 
						|
{
 | 
						|
    return hx509_name_expand(context, tbs->subject, env);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
add_extension(hx509_context context,
 | 
						|
	      TBSCertificate *tbsc,
 | 
						|
	      int critical_flag,
 | 
						|
	      const heim_oid *oid,
 | 
						|
	      const heim_octet_string *data)
 | 
						|
{
 | 
						|
    Extension ext;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    memset(&ext, 0, sizeof(ext));
 | 
						|
 | 
						|
    if (critical_flag) {
 | 
						|
	ext.critical = malloc(sizeof(*ext.critical));
 | 
						|
	if (ext.critical == NULL) {
 | 
						|
	    ret = ENOMEM;
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	*ext.critical = TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
    ret = der_copy_oid(oid, &ext.extnID);
 | 
						|
    if (ret) {
 | 
						|
	hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	goto out;
 | 
						|
    }
 | 
						|
    ret = der_copy_octet_string(data, &ext.extnValue);
 | 
						|
    if (ret) {
 | 
						|
	hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	goto out;
 | 
						|
    }
 | 
						|
    ret = add_Extensions(tbsc->extensions, &ext);
 | 
						|
    if (ret) {
 | 
						|
	hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	goto out;
 | 
						|
    }
 | 
						|
out:
 | 
						|
    free_Extension(&ext);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
build_proxy_prefix(hx509_context context, const Name *issuer, Name *subject)
 | 
						|
{
 | 
						|
    char *tstr;
 | 
						|
    time_t t;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    ret = copy_Name(issuer, subject);
 | 
						|
    if (ret) {
 | 
						|
	hx509_set_error_string(context, 0, ret,
 | 
						|
			       "Failed to copy subject name");
 | 
						|
	return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    t = time(NULL);
 | 
						|
    asprintf(&tstr, "ts-%lu", (unsigned long)t);
 | 
						|
    if (tstr == NULL) {
 | 
						|
	hx509_set_error_string(context, 0, ENOMEM,
 | 
						|
			       "Failed to copy subject name");
 | 
						|
	return ENOMEM;
 | 
						|
    }
 | 
						|
    /* prefix with CN=<ts>,...*/
 | 
						|
    ret = _hx509_name_modify(context, subject, 1, oid_id_at_commonName(), tstr);
 | 
						|
    free(tstr);
 | 
						|
    if (ret)
 | 
						|
	free_Name(subject);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
ca_sign(hx509_context context,
 | 
						|
	hx509_ca_tbs tbs,
 | 
						|
	hx509_private_key signer,
 | 
						|
	const AuthorityKeyIdentifier *ai,
 | 
						|
	const Name *issuername,
 | 
						|
	hx509_cert *certificate)
 | 
						|
{
 | 
						|
    heim_octet_string data;
 | 
						|
    Certificate c;
 | 
						|
    TBSCertificate *tbsc;
 | 
						|
    size_t size;
 | 
						|
    int ret;
 | 
						|
    const AlgorithmIdentifier *sigalg;
 | 
						|
    time_t notBefore;
 | 
						|
    time_t notAfter;
 | 
						|
    unsigned key_usage;
 | 
						|
 | 
						|
    sigalg = _hx509_crypto_default_sig_alg;
 | 
						|
 | 
						|
    memset(&c, 0, sizeof(c));
 | 
						|
 | 
						|
    /*
 | 
						|
     * Default values are: Valid since 24h ago, valid one year into
 | 
						|
     * the future, KeyUsage digitalSignature and keyEncipherment set,
 | 
						|
     * and keyCertSign for CA certificates.
 | 
						|
     */
 | 
						|
    notBefore = tbs->notBefore;
 | 
						|
    if (notBefore == 0)
 | 
						|
	notBefore = time(NULL) - 3600 * 24;
 | 
						|
    notAfter = tbs->notAfter;
 | 
						|
    if (notAfter == 0)
 | 
						|
	notAfter = time(NULL) + 3600 * 24 * 365;
 | 
						|
 | 
						|
    key_usage = tbs->key_usage;
 | 
						|
    if (key_usage == 0) {
 | 
						|
	KeyUsage ku;
 | 
						|
	memset(&ku, 0, sizeof(ku));
 | 
						|
	ku.digitalSignature = 1;
 | 
						|
	ku.keyEncipherment = 1;
 | 
						|
	key_usage = KeyUsage2int(ku);
 | 
						|
    }
 | 
						|
 | 
						|
    if (tbs->flags.ca) {
 | 
						|
	KeyUsage ku;
 | 
						|
	memset(&ku, 0, sizeof(ku));
 | 
						|
	ku.keyCertSign = 1;
 | 
						|
	ku.cRLSign = 1;
 | 
						|
	key_usage |= KeyUsage2int(ku);
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     *
 | 
						|
     */
 | 
						|
 | 
						|
    tbsc = &c.tbsCertificate;
 | 
						|
 | 
						|
    if (tbs->flags.key == 0) {
 | 
						|
	ret = EINVAL;
 | 
						|
	hx509_set_error_string(context, 0, ret, "No public key set");
 | 
						|
	return ret;
 | 
						|
    }
 | 
						|
    /*
 | 
						|
     * Don't put restrictions on proxy certificate's subject name, it
 | 
						|
     * will be generated below.
 | 
						|
     */
 | 
						|
    if (!tbs->flags.proxy) {
 | 
						|
	if (tbs->subject == NULL) {
 | 
						|
	    hx509_set_error_string(context, 0, EINVAL, "No subject name set");
 | 
						|
	    return EINVAL;
 | 
						|
	}
 | 
						|
	if (hx509_name_is_null_p(tbs->subject) && tbs->san.len == 0) {
 | 
						|
	    hx509_set_error_string(context, 0, EINVAL, 
 | 
						|
				   "NULL subject and no SubjectAltNames");
 | 
						|
	    return EINVAL;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    if (tbs->flags.ca && tbs->flags.proxy) {
 | 
						|
	hx509_set_error_string(context, 0, EINVAL, "Can't be proxy and CA "
 | 
						|
			       "at the same time");
 | 
						|
	return EINVAL;
 | 
						|
    }
 | 
						|
    if (tbs->flags.proxy) {
 | 
						|
	if (tbs->san.len > 0) {
 | 
						|
	    hx509_set_error_string(context, 0, EINVAL, 
 | 
						|
				   "Proxy certificate is not allowed "
 | 
						|
				   "to have SubjectAltNames");
 | 
						|
	    return EINVAL;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /* version         [0]  Version OPTIONAL, -- EXPLICIT nnn DEFAULT 1, */
 | 
						|
    tbsc->version = calloc(1, sizeof(*tbsc->version));
 | 
						|
    if (tbsc->version == NULL) {
 | 
						|
	ret = ENOMEM;
 | 
						|
	hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	goto out;
 | 
						|
    }
 | 
						|
    *tbsc->version = rfc3280_version_3;
 | 
						|
    /* serialNumber         CertificateSerialNumber, */
 | 
						|
    if (tbs->flags.serial) {
 | 
						|
	ret = der_copy_heim_integer(&tbs->serial, &tbsc->serialNumber);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
    } else {
 | 
						|
	tbsc->serialNumber.length = 20;
 | 
						|
	tbsc->serialNumber.data = malloc(tbsc->serialNumber.length);
 | 
						|
	if (tbsc->serialNumber.data == NULL){
 | 
						|
	    ret = ENOMEM;
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	/* XXX diffrent */
 | 
						|
	RAND_bytes(tbsc->serialNumber.data, tbsc->serialNumber.length);
 | 
						|
	((unsigned char *)tbsc->serialNumber.data)[0] &= 0x7f;
 | 
						|
    }
 | 
						|
    /* signature            AlgorithmIdentifier, */
 | 
						|
    ret = copy_AlgorithmIdentifier(sigalg, &tbsc->signature);
 | 
						|
    if (ret) {
 | 
						|
	hx509_set_error_string(context, 0, ret, "Failed to copy sigature alg");
 | 
						|
	goto out;
 | 
						|
    }
 | 
						|
    /* issuer               Name, */
 | 
						|
    if (issuername)
 | 
						|
	ret = copy_Name(issuername, &tbsc->issuer);
 | 
						|
    else
 | 
						|
	ret = hx509_name_to_Name(tbs->subject, &tbsc->issuer);
 | 
						|
    if (ret) {
 | 
						|
	hx509_set_error_string(context, 0, ret, "Failed to copy issuer name");
 | 
						|
	goto out;
 | 
						|
    }
 | 
						|
    /* validity             Validity, */
 | 
						|
    tbsc->validity.notBefore.element = choice_Time_generalTime;
 | 
						|
    tbsc->validity.notBefore.u.generalTime = notBefore;
 | 
						|
    tbsc->validity.notAfter.element = choice_Time_generalTime;
 | 
						|
    tbsc->validity.notAfter.u.generalTime = notAfter;
 | 
						|
    /* subject              Name, */
 | 
						|
    if (tbs->flags.proxy) {
 | 
						|
	ret = build_proxy_prefix(context, &tbsc->issuer, &tbsc->subject);
 | 
						|
	if (ret)
 | 
						|
	    goto out;
 | 
						|
    } else {
 | 
						|
	ret = hx509_name_to_Name(tbs->subject, &tbsc->subject);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret,
 | 
						|
				   "Failed to copy subject name");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    /* subjectPublicKeyInfo SubjectPublicKeyInfo, */
 | 
						|
    ret = copy_SubjectPublicKeyInfo(&tbs->spki, &tbsc->subjectPublicKeyInfo);
 | 
						|
    if (ret) {
 | 
						|
	hx509_set_error_string(context, 0, ret, "Failed to copy spki");
 | 
						|
	goto out;
 | 
						|
    }
 | 
						|
    /* issuerUniqueID  [1]  IMPLICIT BIT STRING OPTIONAL */
 | 
						|
    /* subjectUniqueID [2]  IMPLICIT BIT STRING OPTIONAL */
 | 
						|
    /* extensions      [3]  EXPLICIT Extensions OPTIONAL */
 | 
						|
    tbsc->extensions = calloc(1, sizeof(*tbsc->extensions));
 | 
						|
    if (tbsc->extensions == NULL) {
 | 
						|
	ret = ENOMEM;
 | 
						|
	hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	goto out;
 | 
						|
    }
 | 
						|
    
 | 
						|
    /* Add the text BMP string Domaincontroller to the cert */
 | 
						|
    if (tbs->flags.domaincontroller) {
 | 
						|
	data.data = rk_UNCONST("\x1e\x20\x00\x44\x00\x6f\x00\x6d"
 | 
						|
			       "\x00\x61\x00\x69\x00\x6e\x00\x43"
 | 
						|
			       "\x00\x6f\x00\x6e\x00\x74\x00\x72"
 | 
						|
			       "\x00\x6f\x00\x6c\x00\x6c\x00\x65"
 | 
						|
			       "\x00\x72");
 | 
						|
	data.length = 34;
 | 
						|
 | 
						|
	ret = add_extension(context, tbsc, 0,
 | 
						|
			    oid_id_ms_cert_enroll_domaincontroller(),
 | 
						|
			    &data);
 | 
						|
	if (ret)
 | 
						|
	    goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    /* add KeyUsage */
 | 
						|
    {
 | 
						|
	KeyUsage ku;
 | 
						|
 | 
						|
	ku = int2KeyUsage(key_usage);
 | 
						|
	ASN1_MALLOC_ENCODE(KeyUsage, data.data, data.length, &ku, &size, ret);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	if (size != data.length)
 | 
						|
	    _hx509_abort("internal ASN.1 encoder error");
 | 
						|
	ret = add_extension(context, tbsc, 1,
 | 
						|
			    oid_id_x509_ce_keyUsage(), &data);
 | 
						|
	free(data.data);
 | 
						|
	if (ret)
 | 
						|
	    goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    /* add ExtendedKeyUsage */
 | 
						|
    if (tbs->eku.len > 0) {
 | 
						|
	ASN1_MALLOC_ENCODE(ExtKeyUsage, data.data, data.length, 
 | 
						|
			   &tbs->eku, &size, ret);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	if (size != data.length)
 | 
						|
	    _hx509_abort("internal ASN.1 encoder error");
 | 
						|
	ret = add_extension(context, tbsc, 0,
 | 
						|
			    oid_id_x509_ce_extKeyUsage(), &data);
 | 
						|
	free(data.data);
 | 
						|
	if (ret)
 | 
						|
	    goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    /* add Subject Alternative Name */
 | 
						|
    if (tbs->san.len > 0) {
 | 
						|
	ASN1_MALLOC_ENCODE(GeneralNames, data.data, data.length, 
 | 
						|
			   &tbs->san, &size, ret);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	if (size != data.length)
 | 
						|
	    _hx509_abort("internal ASN.1 encoder error");
 | 
						|
	ret = add_extension(context, tbsc, 0,
 | 
						|
			    oid_id_x509_ce_subjectAltName(),
 | 
						|
			    &data);
 | 
						|
	free(data.data);
 | 
						|
	if (ret)
 | 
						|
	    goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Add Authority Key Identifier */
 | 
						|
    if (ai) {
 | 
						|
	ASN1_MALLOC_ENCODE(AuthorityKeyIdentifier, data.data, data.length, 
 | 
						|
			   ai, &size, ret);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	if (size != data.length)
 | 
						|
	    _hx509_abort("internal ASN.1 encoder error");
 | 
						|
	ret = add_extension(context, tbsc, 0,
 | 
						|
			    oid_id_x509_ce_authorityKeyIdentifier(),
 | 
						|
			    &data);
 | 
						|
	free(data.data);
 | 
						|
	if (ret)
 | 
						|
	    goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Add Subject Key Identifier */
 | 
						|
    {
 | 
						|
	SubjectKeyIdentifier si;
 | 
						|
	unsigned char hash[SHA_DIGEST_LENGTH];
 | 
						|
 | 
						|
	{
 | 
						|
	    SHA_CTX m;
 | 
						|
	    
 | 
						|
	    SHA1_Init(&m);
 | 
						|
	    SHA1_Update(&m, tbs->spki.subjectPublicKey.data,
 | 
						|
			tbs->spki.subjectPublicKey.length / 8);
 | 
						|
	    SHA1_Final (hash, &m);
 | 
						|
	}
 | 
						|
 | 
						|
	si.data = hash;
 | 
						|
	si.length = sizeof(hash);
 | 
						|
 | 
						|
	ASN1_MALLOC_ENCODE(SubjectKeyIdentifier, data.data, data.length, 
 | 
						|
			   &si, &size, ret);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	if (size != data.length)
 | 
						|
	    _hx509_abort("internal ASN.1 encoder error");
 | 
						|
	ret = add_extension(context, tbsc, 0,
 | 
						|
			    oid_id_x509_ce_subjectKeyIdentifier(),
 | 
						|
			    &data);
 | 
						|
	free(data.data);
 | 
						|
	if (ret)
 | 
						|
	    goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Add BasicConstraints */ 
 | 
						|
    {
 | 
						|
	BasicConstraints bc;
 | 
						|
	int aCA = 1;
 | 
						|
	uint32_t path;
 | 
						|
 | 
						|
	memset(&bc, 0, sizeof(bc));
 | 
						|
 | 
						|
	if (tbs->flags.ca) {
 | 
						|
	    bc.cA = &aCA;
 | 
						|
	    if (tbs->pathLenConstraint >= 0) {
 | 
						|
		path = tbs->pathLenConstraint;
 | 
						|
		bc.pathLenConstraint = &path;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
	ASN1_MALLOC_ENCODE(BasicConstraints, data.data, data.length, 
 | 
						|
			   &bc, &size, ret);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	if (size != data.length)
 | 
						|
	    _hx509_abort("internal ASN.1 encoder error");
 | 
						|
	/* Critical if this is a CA */
 | 
						|
	ret = add_extension(context, tbsc, tbs->flags.ca,
 | 
						|
			    oid_id_x509_ce_basicConstraints(),
 | 
						|
			    &data);
 | 
						|
	free(data.data);
 | 
						|
	if (ret)
 | 
						|
	    goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    /* add Proxy */
 | 
						|
    if (tbs->flags.proxy) {
 | 
						|
	ProxyCertInfo info;
 | 
						|
 | 
						|
	memset(&info, 0, sizeof(info));
 | 
						|
 | 
						|
	if (tbs->pathLenConstraint >= 0) {
 | 
						|
	    info.pCPathLenConstraint = 
 | 
						|
		malloc(sizeof(*info.pCPathLenConstraint));
 | 
						|
	    if (info.pCPathLenConstraint == NULL) {
 | 
						|
		ret = ENOMEM;
 | 
						|
		hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
		goto out;
 | 
						|
	    }
 | 
						|
	    *info.pCPathLenConstraint = tbs->pathLenConstraint;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = der_copy_oid(oid_id_pkix_ppl_inheritAll(),
 | 
						|
			   &info.proxyPolicy.policyLanguage);
 | 
						|
	if (ret) {
 | 
						|
	    free_ProxyCertInfo(&info);
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	ASN1_MALLOC_ENCODE(ProxyCertInfo, data.data, data.length, 
 | 
						|
			   &info, &size, ret);
 | 
						|
	free_ProxyCertInfo(&info);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	if (size != data.length)
 | 
						|
	    _hx509_abort("internal ASN.1 encoder error");
 | 
						|
	ret = add_extension(context, tbsc, 0,
 | 
						|
			    oid_id_pkix_pe_proxyCertInfo(),
 | 
						|
			    &data);
 | 
						|
	free(data.data);
 | 
						|
	if (ret)
 | 
						|
	    goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    if (tbs->crldp.len) {
 | 
						|
 | 
						|
	ASN1_MALLOC_ENCODE(CRLDistributionPoints, data.data, data.length,
 | 
						|
			   &tbs->crldp, &size, ret);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	if (size != data.length)
 | 
						|
	    _hx509_abort("internal ASN.1 encoder error");
 | 
						|
	ret = add_extension(context, tbsc, FALSE,
 | 
						|
			    oid_id_x509_ce_cRLDistributionPoints(),
 | 
						|
			    &data);
 | 
						|
	free(data.data);
 | 
						|
	if (ret)
 | 
						|
	    goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    ASN1_MALLOC_ENCODE(TBSCertificate, data.data, data.length,tbsc, &size, ret);
 | 
						|
    if (ret) {
 | 
						|
	hx509_set_error_string(context, 0, ret, "malloc out of memory");
 | 
						|
	goto out;
 | 
						|
    }
 | 
						|
    if (data.length != size)
 | 
						|
	_hx509_abort("internal ASN.1 encoder error");
 | 
						|
 | 
						|
    ret = _hx509_create_signature_bitstring(context,
 | 
						|
					    signer,
 | 
						|
					    sigalg,
 | 
						|
					    &data,
 | 
						|
					    &c.signatureAlgorithm,
 | 
						|
					    &c.signatureValue);
 | 
						|
    free(data.data);
 | 
						|
    if (ret)
 | 
						|
	goto out;
 | 
						|
 | 
						|
    ret = hx509_cert_init(context, &c, certificate);
 | 
						|
    if (ret)
 | 
						|
	goto out;
 | 
						|
 | 
						|
    free_Certificate(&c);
 | 
						|
 | 
						|
    return 0;
 | 
						|
 | 
						|
out:
 | 
						|
    free_Certificate(&c);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
get_AuthorityKeyIdentifier(hx509_context context,
 | 
						|
			   const Certificate *certificate,
 | 
						|
			   AuthorityKeyIdentifier *ai)
 | 
						|
{
 | 
						|
    SubjectKeyIdentifier si;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    ret = _hx509_find_extension_subject_key_id(certificate, &si);
 | 
						|
    if (ret == 0) {
 | 
						|
	ai->keyIdentifier = calloc(1, sizeof(*ai->keyIdentifier));
 | 
						|
	if (ai->keyIdentifier == NULL) {
 | 
						|
	    free_SubjectKeyIdentifier(&si);
 | 
						|
	    ret = ENOMEM;
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	ret = der_copy_octet_string(&si, ai->keyIdentifier);
 | 
						|
	free_SubjectKeyIdentifier(&si);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
    } else {
 | 
						|
	GeneralNames gns;
 | 
						|
	GeneralName gn;
 | 
						|
	Name name;
 | 
						|
 | 
						|
	memset(&gn, 0, sizeof(gn));
 | 
						|
	memset(&gns, 0, sizeof(gns));
 | 
						|
	memset(&name, 0, sizeof(name));
 | 
						|
 | 
						|
	ai->authorityCertIssuer = 
 | 
						|
	    calloc(1, sizeof(*ai->authorityCertIssuer));
 | 
						|
	if (ai->authorityCertIssuer == NULL) {
 | 
						|
	    ret = ENOMEM;
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
	ai->authorityCertSerialNumber = 
 | 
						|
	    calloc(1, sizeof(*ai->authorityCertSerialNumber));
 | 
						|
	if (ai->authorityCertSerialNumber == NULL) {
 | 
						|
	    ret = ENOMEM;
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	/* 
 | 
						|
	 * XXX unbreak when asn1 compiler handle IMPLICIT
 | 
						|
	 *
 | 
						|
	 * This is so horrible.
 | 
						|
	 */
 | 
						|
 | 
						|
	ret = copy_Name(&certificate->tbsCertificate.subject, &name);
 | 
						|
	if (ai->authorityCertSerialNumber == NULL) {
 | 
						|
	    ret = ENOMEM;
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	memset(&gn, 0, sizeof(gn));
 | 
						|
	gn.element = choice_GeneralName_directoryName;
 | 
						|
	gn.u.directoryName.element = 
 | 
						|
	    choice_GeneralName_directoryName_rdnSequence;
 | 
						|
	gn.u.directoryName.u.rdnSequence = name.u.rdnSequence;
 | 
						|
 | 
						|
	ret = add_GeneralNames(&gns, &gn);
 | 
						|
	if (ret) {
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	ai->authorityCertIssuer->val = gns.val;
 | 
						|
	ai->authorityCertIssuer->len = gns.len;
 | 
						|
 | 
						|
	ret = der_copy_heim_integer(&certificate->tbsCertificate.serialNumber,
 | 
						|
				    ai->authorityCertSerialNumber);
 | 
						|
	if (ai->authorityCertSerialNumber == NULL) {
 | 
						|
	    ret = ENOMEM;
 | 
						|
	    hx509_set_error_string(context, 0, ret, "Out of memory");
 | 
						|
	    goto out;
 | 
						|
	}
 | 
						|
    }
 | 
						|
out:
 | 
						|
    if (ret)
 | 
						|
	free_AuthorityKeyIdentifier(ai);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_sign(hx509_context context,
 | 
						|
	      hx509_ca_tbs tbs,
 | 
						|
	      hx509_cert signer,
 | 
						|
	      hx509_cert *certificate)
 | 
						|
{
 | 
						|
    const Certificate *signer_cert;
 | 
						|
    AuthorityKeyIdentifier ai;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    memset(&ai, 0, sizeof(ai));
 | 
						|
 | 
						|
    signer_cert = _hx509_get_cert(signer);
 | 
						|
 | 
						|
    ret = get_AuthorityKeyIdentifier(context, signer_cert, &ai);
 | 
						|
    if (ret)
 | 
						|
	goto out;
 | 
						|
 | 
						|
    ret = ca_sign(context,
 | 
						|
		  tbs, 
 | 
						|
		  _hx509_cert_private_key(signer),
 | 
						|
		  &ai,
 | 
						|
		  &signer_cert->tbsCertificate.subject,
 | 
						|
		  certificate);
 | 
						|
 | 
						|
out:
 | 
						|
    free_AuthorityKeyIdentifier(&ai);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
hx509_ca_sign_self(hx509_context context,
 | 
						|
		   hx509_ca_tbs tbs,
 | 
						|
		   hx509_private_key signer,
 | 
						|
		   hx509_cert *certificate)
 | 
						|
{
 | 
						|
    return ca_sign(context,
 | 
						|
		   tbs, 
 | 
						|
		   signer,
 | 
						|
		   NULL,
 | 
						|
		   NULL,
 | 
						|
		   certificate);
 | 
						|
}
 |