コード例 #1
0
ファイル: error.c プロジェクト: lha/heimdal
char *
hx509_get_error_string(hx509_context context, int error_code)
{
    heim_error_t msg = context->error;
    heim_string_t s;
    char *str = NULL;

    if (msg == NULL || heim_error_get_code(msg) != error_code) {
	const char *cstr;

	cstr = com_right(context->et_list, error_code);
	if (cstr)
	    return strdup(cstr);
	cstr = strerror(error_code);
	if (cstr)
	    return strdup(cstr);
	if (asprintf(&str, "<unknown error: %d>", error_code) == -1)
	    return NULL;
	return str;
    }

    s = heim_error_copy_string(msg);
    if (s) {
	const char *cstr = heim_string_get_utf8(s);
	if (cstr)
	    str = strdup(cstr);
	heim_release(s);
    }
    return str;
}
コード例 #2
0
ファイル: cms.c プロジェクト: InvLim/heimdal
static int
any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs)
{
    int ret;
    size_t i;

    if (sd->certificates == NULL)
	return 0;

    for (i = 0; i < sd->certificates->len; i++) {
	heim_error_t error;
	hx509_cert c;

	c = hx509_cert_init_data(context,
				 sd->certificates->val[i].data,
				 sd->certificates->val[i].length,
				 &error);
	if (c == NULL) {
	    ret = heim_error_get_code(error);
	    heim_release(error);
	    return ret;
	}
	ret = hx509_certs_add(context, certs, c);
	hx509_cert_free(c);
	if (ret)
	    return ret;
    }

    return 0;
}
コード例 #3
0
ファイル: db.c プロジェクト: InvLim/heimdal
static int
read_json(const char *dbname, heim_object_t *out, heim_error_t *error)
{
    struct stat st;
    char *str = NULL;
    int ret;
    int fd = -1;
    ssize_t bytes;

    *out = NULL;
    ret = open_file(dbname, 0, 0, &fd, error);
    if (ret)
	return ret;

    ret = fstat(fd, &st);
    if (ret == -1) {
	(void) close(fd);
	return HEIM_ERROR(error, errno,
			  (ret, N_("Could not stat JSON DB %s: %s", ""),
			   dbname, strerror(errno)));
    }

    if (st.st_size == 0) {
	(void) close(fd);
	return 0;
    }

    str = malloc(st.st_size + 1);
    if (str == NULL) {
	 (void) close(fd);
	return HEIM_ENOMEM(error);
    }

    bytes = read(fd, str, st.st_size);
     (void) close(fd);
    if (bytes != st.st_size) {
	free(str);
	if (bytes >= 0)
	    errno = EINVAL; /* ?? */
	return HEIM_ERROR(error, errno,
			  (ret, N_("Could not read JSON DB %s: %s", ""),
			   dbname, strerror(errno)));
    }
    str[st.st_size] = '\0';
    *out = heim_json_create(str, 10, 0, error);
    free(str);
    if (*out == NULL)
	return (error && *error) ? heim_error_get_code(*error) : EINVAL;
    return 0;
}
コード例 #4
0
ファイル: ks_file.c プロジェクト: IIJ-NetBSD/netbsd-src
static int
parse_certificate(hx509_context context, const char *fn,
		  struct hx509_collector *c,
		  const hx509_pem_header *headers,
		  const void *data, size_t len,
		  const AlgorithmIdentifier *ai)
{
    heim_error_t error = NULL;
    hx509_cert cert;
    int ret;

    cert = hx509_cert_init_data(context, data, len, &error);
    if (cert == NULL) {
	ret = heim_error_get_code(error);
	heim_release(error);
	return ret;
    }

    ret = _hx509_collector_certs_add(context, c, cert);
    hx509_cert_free(cert);
    return ret;
}
コード例 #5
0
ファイル: db.c プロジェクト: InvLim/heimdal
static int
json_db_sync(void *db, heim_error_t *error)
{
    json_db_t jsondb = db;
    size_t len, bytes;
    heim_error_t e;
    heim_string_t json;
    const char *json_text = NULL;
    int ret = 0;
    int fd = -1;
#ifdef WIN32
    int tries = 3;
#endif

    heim_assert(jsondb->fd > -1, "DB not locked when sync attempted");

    json = heim_json_copy_serialize(jsondb->dict, 0, &e);
    if (json == NULL) {
	if (error)
	    *error = e;
	else
	    heim_release(e);
	return heim_error_get_code(e);
    }

    json_text = heim_string_get_utf8(json);
    len = strlen(json_text);
    errno = 0;

#ifdef WIN32
    while (tries--) {
	ret = open_file(heim_string_get_utf8(jsondb->dbname), 1, 0, &fd, error);
	if (ret == 0)
	    break;
	sleep(1);
    }
    if (ret) {
	heim_release(json);
	return ret;
    }
#else
    fd = jsondb->fd;
#endif /* WIN32 */

    bytes = write(fd, json_text, len);
    heim_release(json);
    if (bytes != len)
	return errno ? errno : EIO;
    ret = fsync(fd);
    if (ret)
	return ret;

#ifdef WIN32
    ret = close(fd);
    if (ret)
	return GetLastError();
#else
    ret = rename(heim_string_get_utf8(jsondb->bkpname), heim_string_get_utf8(jsondb->dbname));
    if (ret == 0) {
	jsondb->locked_needs_unlink = 0;
	return 0;
    }
#endif /* WIN32 */

    return errno;
}
コード例 #6
0
ファイル: aname_to_localname.c プロジェクト: abartlet/heimdal
static krb5_error_code
an2ln_def_plug_an2ln(void *plug_ctx, krb5_context context,
		     const char *rule,
		     krb5_const_principal aname,
		     set_result_f set_res_f, void *set_res_ctx)
{
    krb5_error_code ret;
    const char *an2ln_db_fname;
    heim_db_t dbh = NULL;
    heim_dict_t db_options;
    heim_data_t k, v;
    heim_error_t error;
    char *unparsed = NULL;
    char *value = NULL;

    _krb5_load_db_plugins(context);
    heim_base_once_f(&sorted_text_db_init_once, NULL, sorted_text_db_init_f);

    if (strncmp(rule, "DB:", strlen("DB:") != 0))
	return KRB5_PLUGIN_NO_HANDLE;

    an2ln_db_fname = &rule[strlen("DB:")];
    if (!*an2ln_db_fname)
	return KRB5_PLUGIN_NO_HANDLE;

    ret = krb5_unparse_name(context, aname, &unparsed);
    if (ret)
	return ret;

    db_options = heim_dict_create(11);
    if (db_options != NULL)
	heim_dict_set_value(db_options, HSTR("read-only"),
			    heim_number_create(1));
    dbh = heim_db_create(NULL, an2ln_db_fname, db_options, &error);
    if (dbh == NULL) {
	krb5_set_error_message(context, heim_error_get_code(error),
			       N_("Couldn't open aname2lname-text-db", ""));
	ret = KRB5_PLUGIN_NO_HANDLE;
	goto cleanup;
    }

    /* Binary search; file should be sorted (in C locale) */
    k = heim_data_ref_create(unparsed, strlen(unparsed), NULL);
    if (k == NULL)
	return krb5_enomem(context);
    v = heim_db_copy_value(dbh, NULL, k, &error);
    heim_release(k);
    if (v == NULL && error != NULL) {
	krb5_set_error_message(context, heim_error_get_code(error),
			       N_("Lookup in aname2lname-text-db failed", ""));
	ret = heim_error_get_code(error);
	goto cleanup;
    } else if (v == NULL) {
	ret = KRB5_PLUGIN_NO_HANDLE;
	goto cleanup;
    } else {
	/* found */
	if (heim_data_get_length(v) == 0) {
	    krb5_set_error_message(context, ret,
				   N_("Principal mapped to empty username", ""));
	    ret = KRB5_NO_LOCALNAME;
	    goto cleanup;
	}
	ret = set_res_f(set_res_ctx, heim_data_get_ptr(v));
	heim_release(v);
    }

cleanup:
    heim_release(dbh);
    free(unparsed);
    free(value);
    return ret;
}
コード例 #7
0
ファイル: ks_p11.c プロジェクト: hyc/heimdal
static int
collect_cert(hx509_context context,
	     struct p11_module *p, struct p11_slot *slot,
	     CK_SESSION_HANDLE session,
	     CK_OBJECT_HANDLE object,
	     void *ptr, CK_ATTRIBUTE *query, int num_query)
{
    struct hx509_collector *collector = ptr;
    heim_error_t error = NULL;
    hx509_cert cert;
    int ret;

    if ((CK_LONG)query[0].ulValueLen == -1 ||
	(CK_LONG)query[1].ulValueLen == -1)
    {
	return 0;
    }

    cert = hx509_cert_init_data(context, query[1].pValue,
			       query[1].ulValueLen, &error);
    if (cert == NULL) {
	ret = heim_error_get_code(error);
	heim_release(error);
	return ret;
    }

    if (p->ref == 0)
	_hx509_abort("pkcs11 ref == 0 on alloc");
    p->ref++;
    if (p->ref == UINT_MAX)
	_hx509_abort("pkcs11 ref to high");

    _hx509_cert_set_release(cert, p11_cert_release, p);

    {
	heim_octet_string data;

	data.data = query[0].pValue;
	data.length = query[0].ulValueLen;

	_hx509_set_cert_attribute(context,
				  cert,
				  &asn1_oid_id_pkcs_9_at_localKeyId,
				  &data);
    }

    if ((CK_LONG)query[2].ulValueLen != -1) {
	char *str;

	ret = asprintf(&str, "%.*s",
		       (int)query[2].ulValueLen, (char *)query[2].pValue);
	if (ret != -1 && str) {
	    hx509_cert_set_friendly_name(cert, str);
	    free(str);
	}
    }

    ret = _hx509_collector_certs_add(context, collector, cert);
    hx509_cert_free(cert);

    return ret;
}
コード例 #8
0
ファイル: ks_keychain.c プロジェクト: Sp1l/heimdal
static int
keychain_iter(hx509_context context,
	      hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
{
    SecKeychainAttributeList *attrs = NULL;
    SecKeychainAttributeInfo attrInfo;
    UInt32 attrFormat[1] = { 0 };
    SecKeychainItemRef itemRef;
    SecItemAttr item[1];
    heim_error_t error = NULL;
    struct iter *iter = cursor;
    OSStatus ret;
    UInt32 len;
    void *ptr = NULL;

    if (iter->certs)
	return hx509_certs_next_cert(context, iter->certs, iter->cursor, cert);

    *cert = NULL;

    ret = SecKeychainSearchCopyNext(iter->searchRef, &itemRef);
    if (ret == errSecItemNotFound)
	return 0;
    else if (ret != 0)
	return EINVAL;

    /*
     * Pick out certificate and matching "keyid"
     */

    item[0] = kSecPublicKeyHashItemAttr;

    attrInfo.count = 1;
    attrInfo.tag = item;
    attrInfo.format = attrFormat;

    ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL,
					       &attrs, &len, &ptr);
    if (ret)
	return EINVAL;

    *cert = hx509_cert_init_data(context, ptr, len, &error);
    if (*cert == NULL) {
	ret = heim_error_get_code(error);
	heim_release(error);
	goto out;
    }

    /*
     * Find related private key if there is one by looking at
     * kSecPublicKeyHashItemAttr == kSecKeyLabel
     */
    {
	SecKeychainSearchRef search;
	SecKeychainAttribute attrKeyid;
	SecKeychainAttributeList attrList;

	attrKeyid.tag = kSecKeyLabel;
	attrKeyid.length = attrs->attr[0].length;
	attrKeyid.data = attrs->attr[0].data;

	attrList.count = 1;
	attrList.attr = &attrKeyid;

	ret = SecKeychainSearchCreateFromAttributes(NULL,
						    CSSM_DL_DB_RECORD_PRIVATE_KEY,
						    &attrList,
						    &search);
	if (ret) {
	    ret = 0;
	    goto out;
	}

	ret = SecKeychainSearchCopyNext(search, &itemRef);
	CFRelease(search);
	if (ret == errSecItemNotFound) {
	    ret = 0;
	    goto out;
	} else if (ret) {
	    ret = EINVAL;
	    goto out;
	}
	set_private_key(context, itemRef, *cert);
    }

out:
    SecKeychainItemFreeAttributesAndData(attrs, ptr);

    return ret;
}
コード例 #9
0
ファイル: ca.c プロジェクト: cg2v/heimdal
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_error_t error = NULL;
    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 = tbs->sigalg;
    if (sigalg == NULL)
	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 */
    if (tbs->issuerUniqueID.length) {
	tbsc->issuerUniqueID = calloc(1, sizeof(*tbsc->issuerUniqueID));
	if (tbsc->issuerUniqueID == NULL) {
	    ret = ENOMEM;
	    hx509_set_error_string(context, 0, ret, "Out of memory");
	    goto out;
	}
	ret = der_copy_bit_string(&tbs->issuerUniqueID, tbsc->issuerUniqueID);
	if (ret) {
	    hx509_set_error_string(context, 0, ret, "Out of memory");
	    goto out;
	}
    }
    /* subjectUniqueID [2]  IMPLICIT BIT STRING OPTIONAL */
    if (tbs->subjectUniqueID.length) {
	tbsc->subjectUniqueID = calloc(1, sizeof(*tbsc->subjectUniqueID));
	if (tbsc->subjectUniqueID == NULL) {
	    ret = ENOMEM;
	    hx509_set_error_string(context, 0, ret, "Out of memory");
	    goto out;
	}

	ret = der_copy_bit_string(&tbs->subjectUniqueID, tbsc->subjectUniqueID);
	if (ret) {
	    hx509_set_error_string(context, 0, ret, "Out of memory");
	    goto out;
	}
    }

    /* 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,
			    &asn1_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,
			    &asn1_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,
			    &asn1_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,
			    &asn1_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,
			    &asn1_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];

	{
	    EVP_MD_CTX *ctx;

	    ctx = EVP_MD_CTX_create();
	    EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
	    EVP_DigestUpdate(ctx, tbs->spki.subjectPublicKey.data,
			     tbs->spki.subjectPublicKey.length / 8);
	    EVP_DigestFinal_ex(ctx, hash, NULL);
	    EVP_MD_CTX_destroy(ctx);
	}

	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,
			    &asn1_oid_id_x509_ce_subjectKeyIdentifier,
			    &data);
	free(data.data);
	if (ret)
	    goto out;
    }

    /* Add BasicConstraints */
    {
	BasicConstraints bc;
	int aCA = 1;
	unsigned int 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,
			    &asn1_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(&asn1_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,
			    &asn1_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,
			    &asn1_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;

    *certificate = hx509_cert_init(context, &c, &error);
    if (*certificate == NULL) {
	ret = heim_error_get_code(error);
	heim_release(error);
	goto out;
    }

    free_Certificate(&c);

    return 0;

out:
    free_Certificate(&c);
    return ret;
}