summaryrefslogtreecommitdiff
path: root/source4/heimdal/lib/hx509
diff options
context:
space:
mode:
Diffstat (limited to 'source4/heimdal/lib/hx509')
-rw-r--r--source4/heimdal/lib/hx509/asn1_id_pkix_ocsp.x22
-rw-r--r--source4/heimdal/lib/hx509/asn1_id_pkix_ocsp_basic.x22
-rw-r--r--source4/heimdal/lib/hx509/asn1_id_pkix_ocsp_nonce.x22
-rw-r--r--source4/heimdal/lib/hx509/ca.c893
-rw-r--r--source4/heimdal/lib/hx509/cert.c2214
-rw-r--r--source4/heimdal/lib/hx509/cms.c1279
-rw-r--r--source4/heimdal/lib/hx509/collector.c324
-rw-r--r--source4/heimdal/lib/hx509/crmf.asn1113
-rw-r--r--source4/heimdal/lib/hx509/crypto.c2438
-rw-r--r--source4/heimdal/lib/hx509/error.c141
-rw-r--r--source4/heimdal/lib/hx509/file.c115
-rw-r--r--source4/heimdal/lib/hx509/hx509-protos.h824
-rw-r--r--source4/heimdal/lib/hx509/hx509.h111
-rw-r--r--source4/heimdal/lib/hx509/hx509_err.c157
-rw-r--r--source4/heimdal/lib/hx509/hx509_err.et100
-rw-r--r--source4/heimdal/lib/hx509/hx_locl.h184
-rw-r--r--source4/heimdal/lib/hx509/keyset.c439
-rw-r--r--source4/heimdal/lib/hx509/ks_dir.c223
-rw-r--r--source4/heimdal/lib/hx509/ks_file.c794
-rw-r--r--source4/heimdal/lib/hx509/ks_mem.c223
-rw-r--r--source4/heimdal/lib/hx509/ks_null.c92
-rw-r--r--source4/heimdal/lib/hx509/ks_p11.c1183
-rw-r--r--source4/heimdal/lib/hx509/ks_p12.c697
-rw-r--r--source4/heimdal/lib/hx509/lock.c242
-rw-r--r--source4/heimdal/lib/hx509/name.c550
-rw-r--r--source4/heimdal/lib/hx509/ocsp.asn1113
-rw-r--r--source4/heimdal/lib/hx509/peer.c148
-rw-r--r--source4/heimdal/lib/hx509/pkcs10.asn125
-rw-r--r--source4/heimdal/lib/hx509/print.c538
-rw-r--r--source4/heimdal/lib/hx509/req.c217
-rw-r--r--source4/heimdal/lib/hx509/revoke.c1020
-rw-r--r--source4/heimdal/lib/hx509/test_name.c92
32 files changed, 15555 insertions, 0 deletions
diff --git a/source4/heimdal/lib/hx509/asn1_id_pkix_ocsp.x b/source4/heimdal/lib/hx509/asn1_id_pkix_ocsp.x
new file mode 100644
index 0000000000..e17bad6ed8
--- /dev/null
+++ b/source4/heimdal/lib/hx509/asn1_id_pkix_ocsp.x
@@ -0,0 +1,22 @@
+/* Generated from /home/data/samba/samba4/svn/source/heimdal/lib/hx509/ocsp.asn1 */
+/* Do not edit */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <krb5-types.h>
+#include <ocsp_asn1.h>
+#include <asn1_err.h>
+#include <der.h>
+#include <parse_units.h>
+
+static unsigned oid_id_pkix_ocsp_variable_num[9] = {1, 3, 6, 1, 5, 5, 7, 48, 1 };
+static const heim_oid oid_id_pkix_ocsp_variable = { 9, oid_id_pkix_ocsp_variable_num };
+
+const heim_oid *oid_id_pkix_ocsp(void)
+{
+return &oid_id_pkix_ocsp_variable;
+}
+
diff --git a/source4/heimdal/lib/hx509/asn1_id_pkix_ocsp_basic.x b/source4/heimdal/lib/hx509/asn1_id_pkix_ocsp_basic.x
new file mode 100644
index 0000000000..6f030f1713
--- /dev/null
+++ b/source4/heimdal/lib/hx509/asn1_id_pkix_ocsp_basic.x
@@ -0,0 +1,22 @@
+/* Generated from /home/data/samba/samba4/svn/source/heimdal/lib/hx509/ocsp.asn1 */
+/* Do not edit */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <krb5-types.h>
+#include <ocsp_asn1.h>
+#include <asn1_err.h>
+#include <der.h>
+#include <parse_units.h>
+
+static unsigned oid_id_pkix_ocsp_basic_variable_num[10] = {1, 3, 6, 1, 5, 5, 7, 48, 1, 1 };
+static const heim_oid oid_id_pkix_ocsp_basic_variable = { 10, oid_id_pkix_ocsp_basic_variable_num };
+
+const heim_oid *oid_id_pkix_ocsp_basic(void)
+{
+return &oid_id_pkix_ocsp_basic_variable;
+}
+
diff --git a/source4/heimdal/lib/hx509/asn1_id_pkix_ocsp_nonce.x b/source4/heimdal/lib/hx509/asn1_id_pkix_ocsp_nonce.x
new file mode 100644
index 0000000000..36d7422a0d
--- /dev/null
+++ b/source4/heimdal/lib/hx509/asn1_id_pkix_ocsp_nonce.x
@@ -0,0 +1,22 @@
+/* Generated from /home/data/samba/samba4/svn/source/heimdal/lib/hx509/ocsp.asn1 */
+/* Do not edit */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <krb5-types.h>
+#include <ocsp_asn1.h>
+#include <asn1_err.h>
+#include <der.h>
+#include <parse_units.h>
+
+static unsigned oid_id_pkix_ocsp_nonce_variable_num[10] = {1, 3, 6, 1, 5, 5, 7, 48, 1, 2 };
+static const heim_oid oid_id_pkix_ocsp_nonce_variable = { 10, oid_id_pkix_ocsp_nonce_variable_num };
+
+const heim_oid *oid_id_pkix_ocsp_nonce(void)
+{
+return &oid_id_pkix_ocsp_nonce_variable;
+}
+
diff --git a/source4/heimdal/lib/hx509/ca.c b/source4/heimdal/lib/hx509/ca.c
new file mode 100644
index 0000000000..1a5b4947be
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ca.c
@@ -0,0 +1,893 @@
+/*
+ * Copyright (c) 2006 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: ca.c,v 1.12 2007/01/05 18:40:46 lha Exp $");
+
+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;
+ } flags;
+ time_t notBefore;
+ time_t notAfter;
+ int pathLenConstraint; /* both for CA and Proxy */
+};
+
+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;
+
+ 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);
+
+ 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);
+}
+
+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_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 contex,
+ hx509_ca_tbs tbs,
+ const heim_oid *oid)
+{
+ void *ptr;
+ int ret;
+
+ ptr = realloc(tbs->eku.val, sizeof(tbs->eku.val[0]) * (tbs->eku.len + 1));
+ if (ptr == NULL)
+ return ENOMEM;
+ tbs->eku.val = ptr;
+ ret = der_copy_oid(oid, &tbs->eku.val[tbs->eku.len]);
+ if (ret)
+ return ret;
+ tbs->eku.len += 1;
+ return 0;
+}
+
+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;
+}
+
+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);
+}
+
+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_signature_rsa_with_sha1();
+
+ 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;
+ 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;
+ }
+ if (tbs->subject == NULL && !tbs->flags.proxy) {
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret, "No subject name set");
+ return ret;
+ }
+ if (tbs->flags.ca && tbs->flags.proxy) {
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret, "Can't be proxy and CA "
+ "at the same time");
+ return ret;
+ }
+ 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 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");
+ ret = add_extension(context, tbsc, 0,
+ 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_pe_proxyCertInfo(),
+ &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) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ ret = der_copy_octet_string(&si, ai->keyIdentifier);
+ 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;
+ }
+
+ 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);
+}
diff --git a/source4/heimdal/lib/hx509/cert.c b/source4/heimdal/lib/hx509/cert.c
new file mode 100644
index 0000000000..f84c61a798
--- /dev/null
+++ b/source4/heimdal/lib/hx509/cert.c
@@ -0,0 +1,2214 @@
+/*
+ * 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,v 1.82 2007/01/09 10:52:03 lha Exp $");
+#include "crypto-headers.h"
+
+struct hx509_verify_ctx_data {
+ hx509_certs trust_anchors;
+ int flags;
+#define HX509_VERIFY_CTX_F_TIME_SET 1
+#define HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE 2
+#define HX509_VERIFY_CTX_F_REQUIRE_RFC3280 4
+#define HX509_VERIFY_CTX_F_CHECK_TRUST_ANCHORS 8
+ time_t time_now;
+ unsigned int max_depth;
+#define HX509_VERIFY_MAX_DEPTH 30
+ hx509_revoke_ctx revoke_ctx;
+};
+
+#define REQUIRE_RFC3280(ctx) ((ctx)->flags & HX509_VERIFY_CTX_F_REQUIRE_RFC3280)
+#define CHECK_TA(ctx) ((ctx)->flags & HX509_VERIFY_CTX_F_CHECK_TRUST_ANCHORS)
+
+struct _hx509_cert_attrs {
+ size_t len;
+ hx509_cert_attribute *val;
+};
+
+struct hx509_cert_data {
+ unsigned int ref;
+ char *friendlyname;
+ Certificate *data;
+ hx509_private_key private_key;
+ struct _hx509_cert_attrs attrs;
+ hx509_name basename;
+ _hx509_cert_release_func release;
+ void *ctx;
+};
+
+typedef struct hx509_name_constraints {
+ NameConstraints *val;
+ size_t len;
+} hx509_name_constraints;
+
+#define GeneralSubtrees_SET(g,var) \
+ (g)->len = (var)->len, (g)->val = (var)->val;
+
+/*
+ *
+ */
+
+void
+_hx509_abort(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+ fflush(stdout);
+ abort();
+}
+
+/*
+ *
+ */
+
+int
+hx509_context_init(hx509_context *context)
+{
+ *context = calloc(1, sizeof(**context));
+ if (*context == NULL)
+ return ENOMEM;
+
+ _hx509_ks_mem_register(*context);
+ _hx509_ks_file_register(*context);
+ _hx509_ks_pkcs12_register(*context);
+ _hx509_ks_pkcs11_register(*context);
+ _hx509_ks_dir_register(*context);
+
+ ENGINE_add_conf_module();
+ OpenSSL_add_all_algorithms();
+
+ (*context)->ocsp_time_diff = HX509_DEFAULT_OCSP_TIME_DIFF;
+
+ initialize_hx_error_table_r(&(*context)->et_list);
+ initialize_asn1_error_table_r(&(*context)->et_list);
+
+ return 0;
+}
+
+void
+hx509_context_set_missing_revoke(hx509_context context, int flag)
+{
+ if (flag)
+ context->flags |= HX509_CTX_VERIFY_MISSING_OK;
+ else
+ context->flags &= ~HX509_CTX_VERIFY_MISSING_OK;
+}
+
+void
+hx509_context_free(hx509_context *context)
+{
+ hx509_clear_error_string(*context);
+ if ((*context)->ks_ops) {
+ free((*context)->ks_ops);
+ (*context)->ks_ops = NULL;
+ }
+ (*context)->ks_num_ops = 0;
+ free_error_table ((*context)->et_list);
+ free(*context);
+ *context = NULL;
+}
+
+
+/*
+ *
+ */
+
+Certificate *
+_hx509_get_cert(hx509_cert cert)
+{
+ return cert->data;
+}
+
+/*
+ *
+ */
+
+#if 0
+void
+_hx509_print_cert_subject(hx509_cert cert)
+{
+ char *subject_name;
+ hx509_name name;
+ int ret;
+
+ ret = hx509_cert_get_subject(cert, &name);
+ if (ret)
+ abort();
+
+ ret = hx509_name_to_string(name, &subject_name);
+ hx509_name_free(&name);
+ if (ret)
+ abort();
+
+ printf("name: %s\n", subject_name);
+
+ free(subject_name);
+}
+#endif
+
+/*
+ *
+ */
+
+int
+_hx509_cert_get_version(const Certificate *t)
+{
+ return t->tbsCertificate.version ? *t->tbsCertificate.version + 1 : 1;
+}
+
+int
+hx509_cert_init(hx509_context context, const Certificate *c, hx509_cert *cert)
+{
+ int ret;
+
+ *cert = malloc(sizeof(**cert));
+ if (*cert == NULL)
+ return ENOMEM;
+ (*cert)->ref = 1;
+ (*cert)->friendlyname = NULL;
+ (*cert)->attrs.len = 0;
+ (*cert)->attrs.val = NULL;
+ (*cert)->private_key = NULL;
+ (*cert)->basename = NULL;
+ (*cert)->release = NULL;
+ (*cert)->ctx = NULL;
+
+ (*cert)->data = calloc(1, sizeof(*(*cert)->data));
+ if ((*cert)->data == NULL) {
+ free(*cert);
+ return ENOMEM;
+ }
+ ret = copy_Certificate(c, (*cert)->data);
+ if (ret) {
+ free((*cert)->data);
+ free(*cert);
+ }
+ return ret;
+}
+
+void
+_hx509_cert_set_release(hx509_cert cert,
+ _hx509_cert_release_func release,
+ void *ctx)
+{
+ cert->release = release;
+ cert->ctx = ctx;
+}
+
+
+/* Doesn't make a copy of `private_key'. */
+
+int
+_hx509_cert_assign_key(hx509_cert cert, hx509_private_key private_key)
+{
+ if (cert->private_key)
+ _hx509_private_key_free(&cert->private_key);
+ cert->private_key = _hx509_private_key_ref(private_key);
+ return 0;
+}
+
+void
+hx509_cert_free(hx509_cert cert)
+{
+ int i;
+
+ if (cert == NULL)
+ return;
+
+ if (cert->ref <= 0)
+ _hx509_abort("refcount <= 0");
+ if (--cert->ref > 0)
+ return;
+
+ if (cert->release)
+ (cert->release)(cert, cert->ctx);
+
+ if (cert->private_key)
+ _hx509_private_key_free(&cert->private_key);
+
+ free_Certificate(cert->data);
+ free(cert->data);
+
+ for (i = 0; i < cert->attrs.len; i++) {
+ der_free_octet_string(&cert->attrs.val[i]->data);
+ der_free_oid(&cert->attrs.val[i]->oid);
+ free(cert->attrs.val[i]);
+ }
+ free(cert->attrs.val);
+ free(cert->friendlyname);
+ if (cert->basename)
+ hx509_name_free(&cert->basename);
+ memset(cert, 0, sizeof(cert));
+ free(cert);
+}
+
+hx509_cert
+hx509_cert_ref(hx509_cert cert)
+{
+ if (cert->ref <= 0)
+ _hx509_abort("refcount <= 0");
+ cert->ref++;
+ if (cert->ref == 0)
+ _hx509_abort("refcount == 0");
+ return cert;
+}
+
+int
+hx509_verify_init_ctx(hx509_context context, hx509_verify_ctx *ctx)
+{
+ hx509_verify_ctx c;
+
+ c = calloc(1, sizeof(*c));
+ if (c == NULL)
+ return ENOMEM;
+
+ c->max_depth = HX509_VERIFY_MAX_DEPTH;
+
+ *ctx = c;
+
+ return 0;
+}
+
+void
+hx509_verify_destroy_ctx(hx509_verify_ctx ctx)
+{
+ if (ctx)
+ memset(ctx, 0, sizeof(*ctx));
+ free(ctx);
+}
+
+void
+hx509_verify_attach_anchors(hx509_verify_ctx ctx, hx509_certs set)
+{
+ ctx->trust_anchors = set;
+}
+
+void
+hx509_verify_attach_revoke(hx509_verify_ctx ctx, hx509_revoke_ctx revoke_ctx)
+{
+ ctx->revoke_ctx = revoke_ctx;
+}
+
+void
+hx509_verify_set_time(hx509_verify_ctx ctx, time_t t)
+{
+ ctx->flags |= HX509_VERIFY_CTX_F_TIME_SET;
+ ctx->time_now = t;
+}
+
+void
+hx509_verify_set_proxy_certificate(hx509_verify_ctx ctx, int boolean)
+{
+ if (boolean)
+ ctx->flags |= HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE;
+ else
+ ctx->flags &= ~HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE;
+}
+
+void
+hx509_verify_set_strict_rfc3280_verification(hx509_verify_ctx ctx, int boolean)
+{
+ if (boolean)
+ ctx->flags |= HX509_VERIFY_CTX_F_REQUIRE_RFC3280;
+ else
+ ctx->flags &= ~HX509_VERIFY_CTX_F_REQUIRE_RFC3280;
+}
+
+static const Extension *
+find_extension(const Certificate *cert, const heim_oid *oid, int *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)++];
+ }
+ return NULL;
+}
+
+static int
+find_extension_auth_key_id(const Certificate *subject,
+ AuthorityKeyIdentifier *ai)
+{
+ const Extension *e;
+ size_t size;
+ int i = 0;
+
+ memset(ai, 0, sizeof(*ai));
+
+ e = find_extension(subject, oid_id_x509_ce_authorityKeyIdentifier(), &i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_AuthorityKeyIdentifier(e->extnValue.data,
+ e->extnValue.length,
+ ai, &size);
+}
+
+int
+_hx509_find_extension_subject_key_id(const Certificate *issuer,
+ SubjectKeyIdentifier *si)
+{
+ const Extension *e;
+ size_t size;
+ int i = 0;
+
+ memset(si, 0, sizeof(*si));
+
+ e = find_extension(issuer, oid_id_x509_ce_subjectKeyIdentifier(), &i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_SubjectKeyIdentifier(e->extnValue.data,
+ e->extnValue.length,
+ si, &size);
+}
+
+static int
+find_extension_name_constraints(const Certificate *subject,
+ NameConstraints *nc)
+{
+ const Extension *e;
+ size_t size;
+ int i = 0;
+
+ memset(nc, 0, sizeof(*nc));
+
+ e = find_extension(subject, oid_id_x509_ce_nameConstraints(), &i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_NameConstraints(e->extnValue.data,
+ e->extnValue.length,
+ nc, &size);
+}
+
+static int
+find_extension_subject_alt_name(const Certificate *cert, int *i,
+ GeneralNames *sa)
+{
+ const Extension *e;
+ size_t size;
+
+ memset(sa, 0, sizeof(*sa));
+
+ e = find_extension(cert, oid_id_x509_ce_subjectAltName(), i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_GeneralNames(e->extnValue.data,
+ e->extnValue.length,
+ sa, &size);
+}
+
+static int
+find_extension_eku(const Certificate *cert, ExtKeyUsage *eku)
+{
+ const Extension *e;
+ size_t size;
+ int i = 0;
+
+ memset(eku, 0, sizeof(*eku));
+
+ e = find_extension(cert, oid_id_x509_ce_extKeyUsage(), &i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_ExtKeyUsage(e->extnValue.data,
+ e->extnValue.length,
+ eku, &size);
+}
+
+static int
+add_to_list(hx509_octet_string_list *list, const heim_octet_string *entry)
+{
+ void *p;
+ int ret;
+
+ p = realloc(list->val, (list->len + 1) * sizeof(list->val[0]));
+ if (p == NULL)
+ return ENOMEM;
+ list->val = p;
+ ret = der_copy_octet_string(entry, &list->val[list->len]);
+ if (ret)
+ return ret;
+ list->len++;
+ return 0;
+}
+
+void
+hx509_free_octet_string_list(hx509_octet_string_list *list)
+{
+ int i;
+ for (i = 0; i < list->len; i++)
+ der_free_octet_string(&list->val[i]);
+ free(list->val);
+ list->val = NULL;
+ list->len = 0;
+}
+
+int
+hx509_cert_find_subjectAltName_otherName(hx509_cert cert,
+ const heim_oid *oid,
+ hx509_octet_string_list *list)
+{
+ GeneralNames sa;
+ int ret, i, j;
+
+ list->val = NULL;
+ list->len = 0;
+
+ i = 0;
+ while (1) {
+ ret = find_extension_subject_alt_name(_hx509_get_cert(cert), &i, &sa);
+ i++;
+ if (ret == HX509_EXTENSION_NOT_FOUND) {
+ ret = 0;
+ break;
+ } else if (ret != 0)
+ break;
+
+
+ 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)
+ {
+ ret = add_to_list(list, &sa.val[j].u.otherName.value);
+ if (ret) {
+ free_GeneralNames(&sa);
+ return ret;
+ }
+ }
+ }
+ free_GeneralNames(&sa);
+ }
+ return ret;
+}
+
+
+static int
+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;
+ unsigned ku_flags;
+
+ if (_hx509_cert_get_version(cert) < 3)
+ return 0;
+
+ e = find_extension(cert, oid_id_x509_ce_keyUsage(), &i);
+ if (e == NULL) {
+ if (req_present) {
+ hx509_set_error_string(context, 0, HX509_KU_CERT_MISSING,
+ "Required extension key "
+ "usage missing from certifiate");
+ return HX509_KU_CERT_MISSING;
+ }
+ return 0;
+ }
+
+ ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length, &ku, &size);
+ if (ret)
+ return ret;
+ ku_flags = KeyUsage2int(ku);
+ if ((ku_flags & flags) != flags) {
+ unsigned missing = (~ku_flags) & flags;
+ char buf[256], *name;
+
+ unparse_flags(missing, asn1_KeyUsage_units(), buf, sizeof(buf));
+ _hx509_unparse_Name(&cert->tbsCertificate.subject, &name);
+ hx509_set_error_string(context, 0, HX509_KU_CERT_MISSING,
+ "Key usage %s required but missing "
+ "from certifiate %s", buf, name);
+ free(name);
+ return HX509_KU_CERT_MISSING;
+ }
+ return 0;
+}
+
+int
+_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);
+}
+
+enum certtype { PROXY_CERT, EE_CERT, CA_CERT };
+
+static int
+check_basic_constraints(hx509_context context, const Certificate *cert,
+ enum certtype type, int depth)
+{
+ BasicConstraints bc;
+ const Extension *e;
+ size_t size;
+ int ret, i = 0;
+
+ if (_hx509_cert_get_version(cert) < 3)
+ return 0;
+
+ e = find_extension(cert, oid_id_x509_ce_basicConstraints(), &i);
+ if (e == NULL) {
+ switch(type) {
+ case PROXY_CERT:
+ case EE_CERT:
+ return 0;
+ case CA_CERT: {
+ char *name;
+ ret = _hx509_unparse_Name(&cert->tbsCertificate.subject, &name);
+ assert(ret == 0);
+ hx509_set_error_string(context, 0, HX509_EXTENSION_NOT_FOUND,
+ "basicConstraints missing from "
+ "CA certifiacte %s", name);
+ free(name);
+ return HX509_EXTENSION_NOT_FOUND;
+ }
+ }
+ }
+
+ ret = decode_BasicConstraints(e->extnValue.data,
+ e->extnValue.length, &bc,
+ &size);
+ if (ret)
+ return ret;
+ switch(type) {
+ case PROXY_CERT:
+ if (bc.cA != NULL && *bc.cA)
+ ret = HX509_PARENT_IS_CA;
+ break;
+ case EE_CERT:
+ ret = 0;
+ break;
+ case CA_CERT:
+ if (bc.cA == NULL || !*bc.cA)
+ ret = HX509_PARENT_NOT_CA;
+ else if (bc.pathLenConstraint)
+ if (depth - 1 > *bc.pathLenConstraint)
+ ret = HX509_CA_PATH_TOO_DEEP;
+ break;
+ }
+ free_BasicConstraints(&bc);
+ return ret;
+}
+
+int
+_hx509_cert_is_parent_cmp(const Certificate *subject,
+ const Certificate *issuer,
+ int allow_self_signed)
+{
+ int diff;
+ AuthorityKeyIdentifier ai;
+ SubjectKeyIdentifier si;
+ int ret_ai, ret_si;
+
+ diff = _hx509_name_cmp(&issuer->tbsCertificate.subject,
+ &subject->tbsCertificate.issuer);
+ if (diff)
+ return diff;
+
+ memset(&ai, 0, sizeof(ai));
+ memset(&si, 0, sizeof(si));
+
+ /*
+ * Try to find AuthorityKeyIdentifier, if its not present in the
+ * subject certificate nor the parent.
+ */
+
+ ret_ai = find_extension_auth_key_id(subject, &ai);
+ if (ret_ai && ret_ai != HX509_EXTENSION_NOT_FOUND)
+ return 1;
+ ret_si = _hx509_find_extension_subject_key_id(issuer, &si);
+ if (ret_si && ret_si != HX509_EXTENSION_NOT_FOUND)
+ return -1;
+
+ if (ret_si && ret_ai)
+ goto out;
+ if (ret_ai)
+ goto out;
+ if (ret_si) {
+ if (allow_self_signed) {
+ diff = 0;
+ goto out;
+ } else if (ai.keyIdentifier) {
+ diff = -1;
+ goto out;
+ }
+ }
+
+ if (ai.keyIdentifier == NULL) {
+ Name name;
+
+ if (ai.authorityCertIssuer == NULL)
+ return -1;
+ if (ai.authorityCertSerialNumber == NULL)
+ return -1;
+
+ diff = der_heim_integer_cmp(ai.authorityCertSerialNumber,
+ &issuer->tbsCertificate.serialNumber);
+ if (diff)
+ return diff;
+ if (ai.authorityCertIssuer->len != 1)
+ return -1;
+ if (ai.authorityCertIssuer->val[0].element != choice_GeneralName_directoryName)
+ return -1;
+
+ name.element =
+ ai.authorityCertIssuer->val[0].u.directoryName.element;
+ name.u.rdnSequence =
+ ai.authorityCertIssuer->val[0].u.directoryName.u.rdnSequence;
+
+ diff = _hx509_name_cmp(&issuer->tbsCertificate.subject,
+ &name);
+ if (diff)
+ return diff;
+ diff = 0;
+ } else
+ diff = der_heim_octet_string_cmp(ai.keyIdentifier, &si);
+ if (diff)
+ goto out;
+
+ out:
+ free_AuthorityKeyIdentifier(&ai);
+ free_SubjectKeyIdentifier(&si);
+ return diff;
+}
+
+static int
+certificate_is_anchor(hx509_context context,
+ hx509_certs trust_anchors,
+ const hx509_cert cert)
+{
+ hx509_query q;
+ hx509_cert c;
+ int ret;
+
+ if (trust_anchors == NULL)
+ return 0;
+
+ _hx509_query_clear(&q);
+
+ q.match = HX509_QUERY_MATCH_CERTIFICATE;
+ q.certificate = _hx509_get_cert(cert);
+
+ ret = hx509_certs_find(context, trust_anchors, &q, &c);
+ if (ret == 0)
+ hx509_cert_free(c);
+ return ret == 0;
+}
+
+static int
+certificate_is_self_signed(const Certificate *cert)
+{
+ return _hx509_cert_is_parent_cmp(cert, cert, 1) == 0;
+}
+
+/*
+ * The subjectName is "null" when its empty set of relative DBs.
+ */
+
+static int
+subject_null_p(const Certificate *c)
+{
+ return c->tbsCertificate.subject.u.rdnSequence.len == 0;
+}
+
+
+static int
+find_parent(hx509_context context,
+ time_t time_now,
+ hx509_certs trust_anchors,
+ hx509_path *path,
+ hx509_certs pool,
+ hx509_cert current,
+ hx509_cert *parent)
+{
+ AuthorityKeyIdentifier ai;
+ hx509_query q;
+ int ret;
+
+ *parent = NULL;
+ memset(&ai, 0, sizeof(ai));
+
+ _hx509_query_clear(&q);
+
+ if (!subject_null_p(current->data)) {
+ q.match |= HX509_QUERY_FIND_ISSUER_CERT;
+ q.subject = _hx509_get_cert(current);
+ } else {
+ ret = find_extension_auth_key_id(current->data, &ai);
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_CERTIFICATE_MALFORMED,
+ "Subjectless certificate missing AuthKeyID");
+ return HX509_CERTIFICATE_MALFORMED;
+ }
+
+ if (ai.keyIdentifier == NULL) {
+ free_AuthorityKeyIdentifier(&ai);
+ hx509_set_error_string(context, 0, HX509_CERTIFICATE_MALFORMED,
+ "Subjectless certificate missing keyIdentifier "
+ "inside AuthKeyID");
+ return HX509_CERTIFICATE_MALFORMED;
+ }
+
+ q.subject_id = ai.keyIdentifier;
+ q.match = HX509_QUERY_MATCH_SUBJECT_KEY_ID;
+ }
+
+ q.path = path;
+ q.match |= HX509_QUERY_NO_MATCH_PATH;
+
+ if (pool) {
+ q.timenow = time_now;
+ q.match |= HX509_QUERY_MATCH_TIME;
+
+ ret = hx509_certs_find(context, pool, &q, parent);
+ if (ret == 0) {
+ free_AuthorityKeyIdentifier(&ai);
+ return 0;
+ }
+ q.match &= ~HX509_QUERY_MATCH_TIME;
+ }
+
+ if (trust_anchors) {
+ ret = hx509_certs_find(context, trust_anchors, &q, parent);
+ if (ret == 0) {
+ free_AuthorityKeyIdentifier(&ai);
+ return ret;
+ }
+ }
+ free_AuthorityKeyIdentifier(&ai);
+
+ {
+ hx509_name name;
+ char *str;
+
+ ret = hx509_cert_get_subject(current, &name);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return HX509_ISSUER_NOT_FOUND;
+ }
+ ret = hx509_name_to_string(name, &str);
+ hx509_name_free(&name);
+ if (ret) {
+ 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);
+ free(str);
+ }
+ return HX509_ISSUER_NOT_FOUND;
+}
+
+/*
+ *
+ */
+
+static int
+is_proxy_cert(hx509_context context, const Certificate *cert, ProxyCertInfo *rinfo)
+{
+ ProxyCertInfo info;
+ const Extension *e;
+ size_t size;
+ int ret, i = 0;
+
+ if (rinfo)
+ memset(rinfo, 0, sizeof(*rinfo));
+
+ e = find_extension(cert, oid_id_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,
+ &info,
+ &size);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ if (size != e->extnValue.length) {
+ free_ProxyCertInfo(&info);
+ hx509_clear_error_string(context);
+ return HX509_EXTRA_DATA_AFTER_STRUCTURE;
+ }
+ if (rinfo)
+ *rinfo = info;
+
+ return 0;
+}
+
+/*
+ * Path operations are like MEMORY based keyset, but with exposed
+ * internal so we can do easy searches.
+ */
+
+int
+_hx509_path_append(hx509_context context, hx509_path *path, hx509_cert cert)
+{
+ hx509_cert *val;
+ val = realloc(path->val, (path->len + 1) * sizeof(path->val[0]));
+ if (val == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ path->val = val;
+ path->val[path->len] = hx509_cert_ref(cert);
+ path->len++;
+
+ return 0;
+}
+
+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);
+ path->val = NULL;
+ path->len = 0;
+}
+
+/*
+ * Find path by looking up issuer for the top certificate and continue
+ * until an anchor certificate is found or max limit is found. A
+ * certificate never included twice in the path.
+ *
+ * If the trust anchors are not given, calculate optimistic path, just
+ * follow the chain upward until we no longer find a parent or we hit
+ * the max path limit. In this case, a failure will always be returned
+ * depending on what error condition is hit first.
+ *
+ * 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
+ * failure.
+ */
+
+int
+_hx509_calculate_path(hx509_context context,
+ int flags,
+ time_t time_now,
+ hx509_certs anchors,
+ unsigned int max_depth,
+ hx509_cert cert,
+ hx509_certs pool,
+ hx509_path *path)
+{
+ hx509_cert parent, current;
+ int ret;
+
+ if (max_depth == 0)
+ max_depth = HX509_VERIFY_MAX_DEPTH;
+
+ ret = _hx509_path_append(context, path, cert);
+ if (ret)
+ return ret;
+
+ current = hx509_cert_ref(cert);
+
+ while (!certificate_is_anchor(context, anchors, current)) {
+
+ ret = find_parent(context, time_now, anchors, path,
+ pool, current, &parent);
+ hx509_cert_free(current);
+ if (ret)
+ return ret;
+
+ ret = _hx509_path_append(context, path, parent);
+ if (ret)
+ return ret;
+ current = parent;
+
+ if (path->len > max_depth) {
+ hx509_set_error_string(context, 0, HX509_PATH_TOO_LONG,
+ "Path too long while bulding certificate chain");
+ return HX509_PATH_TOO_LONG;
+ }
+ }
+
+ 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]);
+ path->len--;
+ }
+
+ hx509_cert_free(current);
+ return 0;
+}
+
+static int
+AlgorithmIdentifier_cmp(const AlgorithmIdentifier *p,
+ const AlgorithmIdentifier *q)
+{
+ int diff;
+ diff = der_heim_oid_cmp(&p->algorithm, &q->algorithm);
+ if (diff)
+ return diff;
+ if (p->parameters) {
+ if (q->parameters)
+ return heim_any_cmp(p->parameters,
+ q->parameters);
+ else
+ return 1;
+ } else {
+ if (q->parameters)
+ return -1;
+ else
+ return 0;
+ }
+}
+
+int
+_hx509_Certificate_cmp(const Certificate *p, const Certificate *q)
+{
+ int diff;
+ diff = der_heim_bit_string_cmp(&p->signatureValue, &q->signatureValue);
+ if (diff)
+ return diff;
+ diff = AlgorithmIdentifier_cmp(&p->signatureAlgorithm,
+ &q->signatureAlgorithm);
+ if (diff)
+ return diff;
+ diff = der_heim_octet_string_cmp(&p->tbsCertificate._save,
+ &q->tbsCertificate._save);
+ return diff;
+}
+
+int
+hx509_cert_cmp(hx509_cert p, hx509_cert q)
+{
+ return _hx509_Certificate_cmp(p->data, q->data);
+}
+
+int
+hx509_cert_get_issuer(hx509_cert p, hx509_name *name)
+{
+ return _hx509_name_from_Name(&p->data->tbsCertificate.issuer, name);
+}
+
+int
+hx509_cert_get_subject(hx509_cert p, hx509_name *name)
+{
+ return _hx509_name_from_Name(&p->data->tbsCertificate.subject, name);
+}
+
+int
+hx509_cert_get_base_subject(hx509_context context, hx509_cert c,
+ hx509_name *name)
+{
+ if (c->basename)
+ return hx509_name_copy(context, c->basename, name);
+ if (is_proxy_cert(context, c->data, NULL) == 0) {
+ int ret = HX509_PROXY_CERTIFICATE_NOT_CANONICALIZED;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy certificate have not been "
+ "canonicalize yet, no base name");
+ return ret;
+ }
+ return _hx509_name_from_Name(&c->data->tbsCertificate.subject, name);
+}
+
+int
+hx509_cert_get_serialnumber(hx509_cert p, heim_integer *i)
+{
+ return der_copy_heim_integer(&p->data->tbsCertificate.serialNumber, i);
+}
+
+hx509_private_key
+_hx509_cert_private_key(hx509_cert p)
+{
+ return p->private_key;
+}
+
+int
+_hx509_cert_private_key_exportable(hx509_cert p)
+{
+ if (p->private_key == NULL)
+ return 0;
+ return _hx509_private_key_exportable(p->private_key);
+}
+
+int
+_hx509_cert_private_decrypt(hx509_context context,
+ const heim_octet_string *ciphertext,
+ const heim_oid *encryption_oid,
+ hx509_cert p,
+ heim_octet_string *cleartext)
+{
+ cleartext->data = NULL;
+ cleartext->length = 0;
+
+ if (p->private_key == NULL) {
+ hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
+ "Private key missing");
+ return HX509_PRIVATE_KEY_MISSING;
+ }
+
+ return _hx509_private_key_private_decrypt(context,
+ ciphertext,
+ encryption_oid,
+ p->private_key,
+ cleartext);
+}
+
+int
+_hx509_cert_public_encrypt(hx509_context context,
+ const heim_octet_string *cleartext,
+ const hx509_cert p,
+ heim_oid *encryption_oid,
+ heim_octet_string *ciphertext)
+{
+ return _hx509_public_encrypt(context,
+ cleartext, p->data,
+ encryption_oid, ciphertext);
+}
+
+/*
+ *
+ */
+
+time_t
+_hx509_Time2time_t(const Time *t)
+{
+ switch(t->element) {
+ case choice_Time_utcTime:
+ return t->u.utcTime;
+ case choice_Time_generalTime:
+ return t->u.generalTime;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+init_name_constraints(hx509_name_constraints *nc)
+{
+ memset(nc, 0, sizeof(*nc));
+ return 0;
+}
+
+static int
+add_name_constraints(hx509_context context, const Certificate *c, int not_ca,
+ hx509_name_constraints *nc)
+{
+ NameConstraints tnc;
+ int ret;
+
+ ret = find_extension_name_constraints(c, &tnc);
+ if (ret == HX509_EXTENSION_NOT_FOUND)
+ return 0;
+ else if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed getting NameConstraints");
+ return ret;
+ } else if (not_ca) {
+ ret = HX509_VERIFY_CONSTRAINTS;
+ hx509_set_error_string(context, 0, ret, "Not a CA and "
+ "have NameConstraints");
+ } else {
+ NameConstraints *val;
+ val = realloc(nc->val, sizeof(nc->val[0]) * (nc->len + 1));
+ if (val == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ nc->val = val;
+ ret = copy_NameConstraints(&tnc, &nc->val[nc->len]);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ nc->len += 1;
+ }
+out:
+ free_NameConstraints(&tnc);
+ return ret;
+}
+
+static int
+match_RDN(const RelativeDistinguishedName *c,
+ const RelativeDistinguishedName *n)
+{
+ int i;
+
+ if (c->len != n->len)
+ return HX509_NAME_CONSTRAINT_ERROR;
+
+ for (i = 0; i < n->len; i++) {
+ 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)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ }
+ return 0;
+}
+
+static int
+match_X501Name(const Name *c, const Name *n)
+{
+ int i, ret;
+
+ if (c->element != choice_Name_rdnSequence
+ || n->element != choice_Name_rdnSequence)
+ return 0;
+ if (c->u.rdnSequence.len > n->u.rdnSequence.len)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ for (i = 0; i < c->u.rdnSequence.len; i++) {
+ ret = match_RDN(&c->u.rdnSequence.val[i], &n->u.rdnSequence.val[i]);
+ if (ret)
+ 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.
+ */
+ assert(c->element == n->element);
+
+ switch(c->element) {
+ case choice_GeneralName_otherName:
+ if (der_heim_oid_cmp(&c->u.otherName.type_id,
+ &n->u.otherName.type_id) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (heim_any_cmp(&c->u.otherName.value,
+ &n->u.otherName.value) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ *match = 1;
+ return 0;
+ case choice_GeneralName_rfc822Name: {
+ const char *s;
+ size_t len1, len2;
+ s = strchr(c->u.rfc822Name, '@');
+ if (s) {
+ if (strcasecmp(c->u.rfc822Name, n->u.rfc822Name) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ } else {
+ s = strchr(n->u.rfc822Name, '@');
+ if (s == NULL)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ len1 = strlen(c->u.rfc822Name);
+ len2 = strlen(s + 1);
+ if (len1 > len2)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (strcasecmp(s + 1 + len2 - len1, c->u.rfc822Name) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (len1 < len2 && s[len2 - len1] != '.')
+ return HX509_NAME_CONSTRAINT_ERROR;
+ }
+ *match = 1;
+ return 0;
+ }
+ case choice_GeneralName_dNSName: {
+ size_t len1, len2;
+
+ len1 = strlen(c->u.dNSName);
+ len2 = strlen(n->u.dNSName);
+ if (len1 > len2)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (strcasecmp(&n->u.dNSName[len2 - len1], c->u.dNSName) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ *match = 1;
+ return 0;
+ }
+ case choice_GeneralName_directoryName: {
+ Name c_name, n_name;
+ int ret;
+
+ c_name._save.data = NULL;
+ c_name._save.length = 0;
+ c_name.element = c->u.directoryName.element;
+ c_name.u.rdnSequence = c->u.directoryName.u.rdnSequence;
+
+ n_name._save.data = NULL;
+ n_name._save.length = 0;
+ n_name.element = n->u.directoryName.element;
+ n_name.u.rdnSequence = n->u.directoryName.u.rdnSequence;
+
+ ret = match_X501Name(&c_name, &n_name);
+ if (ret == 0)
+ *match = 1;
+ return ret;
+ }
+ case choice_GeneralName_uniformResourceIdentifier:
+ case choice_GeneralName_iPAddress:
+ case choice_GeneralName_registeredID:
+ default:
+ return HX509_NAME_CONSTRAINT_ERROR;
+ }
+}
+
+static int
+match_alt_name(const GeneralName *n, const Certificate *c,
+ int *same, int *match)
+{
+ GeneralNames sa;
+ int ret, i, j;
+
+ i = 0;
+ do {
+ ret = find_extension_subject_alt_name(c, &i, &sa);
+ if (ret == HX509_EXTENSION_NOT_FOUND) {
+ ret = 0;
+ break;
+ } else if (ret != 0)
+ break;
+
+ for (j = 0; j < sa.len; j++) {
+ if (n->element == sa.val[j].element) {
+ *same = 1;
+ ret = match_general_name(n, &sa.val[j], match);
+ }
+ }
+ free_GeneralNames(&sa);
+ } while (1);
+
+ return ret;
+}
+
+
+static int
+match_tree(const GeneralSubtrees *t, const Certificate *c, int *match)
+{
+ int name, alt_name, same;
+ unsigned int i;
+ int ret = 0;
+
+ name = alt_name = same = *match = 0;
+ for (i = 0; i < t->len; i++) {
+ if (t->val[i].minimum && t->val[i].maximum)
+ return HX509_RANGE;
+
+ /*
+ * If the constraint apply to directoryNames, test is with
+ * subjectName of the certificate if the certificate have a
+ * non-null (empty) subjectName.
+ */
+
+ if (t->val[i].base.element == choice_GeneralName_directoryName
+ && !subject_null_p(c))
+ {
+ GeneralName certname;
+
+
+ certname.element = choice_GeneralName_directoryName;
+ certname.u.directoryName.element =
+ c->tbsCertificate.subject.element;
+ certname.u.directoryName.u.rdnSequence =
+ c->tbsCertificate.subject.u.rdnSequence;
+
+ ret = match_general_name(&t->val[i].base, &certname, &name);
+ }
+
+ /* Handle subjectAltNames, this is icky since they
+ * restrictions only apply if the subjectAltName is of the
+ * same type. So if there have been a match of type, require
+ * altname to be set.
+ */
+ ret = match_alt_name(&t->val[i].base, c, &same, &alt_name);
+ }
+ if (name && (!same || alt_name))
+ *match = 1;
+ return ret;
+}
+
+static int
+check_name_constraints(hx509_context context,
+ const hx509_name_constraints *nc,
+ const Certificate *c)
+{
+ int match, ret;
+ int i;
+
+ for (i = 0 ; i < nc->len; i++) {
+ GeneralSubtrees gs;
+
+ if (nc->val[i].permittedSubtrees) {
+ GeneralSubtrees_SET(&gs, nc->val[i].permittedSubtrees);
+ ret = match_tree(&gs, c, &match);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ /* allow null subjectNames, they wont matches anything */
+ if (match == 0 && !subject_null_p(c)) {
+ hx509_clear_error_string(context);
+ return HX509_VERIFY_CONSTRAINTS;
+ }
+ }
+ if (nc->val[i].excludedSubtrees) {
+ GeneralSubtrees_SET(&gs, nc->val[i].excludedSubtrees);
+ ret = match_tree(&gs, c, &match);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ if (match) {
+ hx509_clear_error_string(context);
+ return HX509_VERIFY_CONSTRAINTS;
+ }
+ }
+ }
+ return 0;
+}
+
+static void
+free_name_constraints(hx509_name_constraints *nc)
+{
+ int i;
+
+ for (i = 0 ; i < nc->len; i++)
+ free_NameConstraints(&nc->val[i]);
+ free(nc->val);
+}
+
+int
+hx509_verify_path(hx509_context context,
+ hx509_verify_ctx ctx,
+ hx509_cert cert,
+ hx509_certs pool)
+{
+ hx509_name_constraints nc;
+ hx509_path path;
+#if 0
+ const AlgorithmIdentifier *alg_id;
+#endif
+ int ret, i, proxy_cert_depth;
+ enum certtype type;
+ Name proxy_issuer;
+
+ memset(&proxy_issuer, 0, sizeof(proxy_issuer));
+
+ ret = init_name_constraints(&nc);
+ if (ret)
+ return ret;
+
+ path.val = NULL;
+ path.len = 0;
+
+ if ((ctx->flags & HX509_VERIFY_CTX_F_TIME_SET) == 0)
+ ctx->time_now = time(NULL);
+
+ /*
+ * Calculate the path from the certificate user presented to the
+ * to an anchor.
+ */
+ ret = _hx509_calculate_path(context, 0, ctx->time_now,
+ ctx->trust_anchors, ctx->max_depth,
+ cert, pool, &path);
+ 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
+ * to the current time.
+ *
+ */
+
+ proxy_cert_depth = 0;
+
+ if (ctx->flags & HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE)
+ type = PROXY_CERT;
+ else
+ type = EE_CERT;
+
+ for (i = 0; i < path.len; i++) {
+ Certificate *c;
+ 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
+ * on what type of certificate this is.
+ */
+
+ switch (type) {
+ case CA_CERT:
+ /* XXX make constants for keyusage */
+ ret = check_key_usage(context, c, 1 << 5,
+ REQUIRE_RFC3280(ctx) ? TRUE : FALSE);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Key usage missing from CA certificate");
+ goto out;
+ }
+ break;
+ case PROXY_CERT: {
+ ProxyCertInfo info;
+
+ if (is_proxy_cert(context, c, &info) == 0) {
+ int j;
+
+ if (info.pCPathLenConstraint != NULL &&
+ *info.pCPathLenConstraint < i)
+ {
+ free_ProxyCertInfo(&info);
+ ret = HX509_PATH_TOO_LONG;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy certificate chain "
+ "longer then allowed");
+ goto out;
+ }
+ /* XXX MUST check info.proxyPolicy */
+ free_ProxyCertInfo(&info);
+
+ j = 0;
+ if (find_extension(c, oid_id_x509_ce_subjectAltName(), &j)) {
+ ret = HX509_PROXY_CERT_INVALID;
+ 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)) {
+ ret = HX509_PROXY_CERT_INVALID;
+ 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
+ * then check with the EE cert when we get to it.
+ */
+
+ if (proxy_cert_depth) {
+ ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.subject);
+ if (ret) {
+ ret = HX509_PROXY_CERT_NAME_WRONG;
+ hx509_set_error_string(context, 0, ret,
+ "Base proxy name not right");
+ goto out;
+ }
+ }
+
+ free_Name(&proxy_issuer);
+
+ ret = copy_Name(&c->tbsCertificate.subject, &proxy_issuer);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ j = proxy_issuer.u.rdnSequence.len;
+ 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()))
+ {
+ ret = HX509_PROXY_CERT_NAME_WRONG;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy name too short or "
+ "does not have Common name "
+ "at the top");
+ goto out;
+ }
+
+ 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_PROXY_CERT_NAME_WRONG;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy issuer name not as expected");
+ goto out;
+ }
+
+ 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.
+ */
+ type = EE_CERT;
+ /* FALLTHOUGH */
+ }
+ }
+ case EE_CERT:
+ /*
+ * If there where any proxy certificates in the chain
+ * (proxy_cert_depth > 0), check that the proxy issuer
+ * matched proxy certificates "base" subject.
+ */
+ if (proxy_cert_depth) {
+
+ ret = _hx509_name_cmp(&proxy_issuer,
+ &c->tbsCertificate.subject);
+ if (ret) {
+ 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);
+ goto out;
+ }
+ }
+
+ break;
+ }
+
+ ret = check_basic_constraints(context, c, type, i - proxy_cert_depth);
+ if (ret)
+ goto out;
+
+ /*
+ * Don't check the trust anchors expiration time since they
+ * are transported out of band, from RFC3820.
+ */
+ if (i + 1 != path.len || CHECK_TA(ctx)) {
+
+ t = _hx509_Time2time_t(&c->tbsCertificate.validity.notBefore);
+ if (t > ctx->time_now) {
+ ret = HX509_CERT_USED_BEFORE_TIME;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ t = _hx509_Time2time_t(&c->tbsCertificate.validity.notAfter);
+ if (t < ctx->time_now) {
+ ret = HX509_CERT_USED_AFTER_TIME;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ if (type == EE_CERT)
+ type = CA_CERT;
+ else if (type == PROXY_CERT)
+ proxy_cert_depth++;
+ }
+
+ /*
+ * Verify constraints, do this backward so path constraints are
+ * checked in the right order.
+ */
+
+ for (ret = 0, i = path.len - 1; i >= 0; i--) {
+ Certificate *c;
+
+ c = _hx509_get_cert(path.val[i]);
+
+#if 0
+ /* check that algorithm and parameters is the same */
+ /* XXX this is wrong */
+ ret = alg_cmp(&c->tbsCertificate.signature, alg_id);
+ if (ret) {
+ hx509_clear_error_string(context);
+ ret = HX509_PATH_ALGORITHM_CHANGED;
+ goto out;
+ }
+#endif
+
+ /* verify name constraints, not for selfsigned and anchor */
+ if (!certificate_is_self_signed(c) || i == path.len - 1) {
+ ret = check_name_constraints(context, &nc, c);
+ if (ret) {
+ goto out;
+ }
+ }
+ ret = add_name_constraints(context, c, i == 0, &nc);
+ if (ret)
+ goto out;
+
+ /* XXX verify all other silly constraints */
+
+ }
+
+ /*
+ * Verify that no certificates has been revoked.
+ */
+
+ if (ctx->revoke_ctx) {
+ hx509_certs certs;
+
+ ret = hx509_certs_init(context, "MEMORY:revoke-certs", 0,
+ NULL, &certs);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < path.len; i++) {
+ ret = hx509_certs_add(context, certs, path.val[i]);
+ if (ret) {
+ hx509_certs_free(&certs);
+ goto out;
+ }
+ }
+ ret = hx509_certs_merge(context, certs, pool);
+ if (ret) {
+ hx509_certs_free(&certs);
+ goto out;
+ }
+
+ for (i = 0; i < path.len - 1; i++) {
+ int parent = (i < path.len - 1) ? i + 1 : i;
+
+ ret = hx509_revoke_verify(context,
+ ctx->revoke_ctx,
+ certs,
+ ctx->time_now,
+ path.val[i],
+ path.val[parent]);
+ if (ret) {
+ hx509_certs_free(&certs);
+ goto out;
+ }
+ }
+ hx509_certs_free(&certs);
+ }
+
+#if 0
+ for (i = path.len - 1; i >= 0; i--) {
+ _hx509_print_cert_subject(path.val[i]);
+ }
+#endif
+
+ /*
+ * Verify signatures, do this backward so public key working
+ * parameter is passed up from the anchor up though the chain.
+ */
+
+ for (i = path.len - 1; i >= 0; i--) {
+ Certificate *signer, *c;
+
+ c = _hx509_get_cert(path.val[i]);
+
+ /* is last in chain (trust anchor) */
+ if (i == path.len - 1) {
+ signer = path.val[i]->data;
+
+ /* if trust anchor is not self signed, don't check sig */
+ if (!certificate_is_self_signed(signer))
+ continue;
+ } else {
+ /* take next certificate in chain */
+ signer = path.val[i + 1]->data;
+ }
+
+ /* verify signatureValue */
+ ret = _hx509_verify_signature_bitstring(context,
+ signer,
+ &c->signatureAlgorithm,
+ &c->tbsCertificate._save,
+ &c->signatureValue);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to verify signature of certificate");
+ goto out;
+ }
+ }
+
+out:
+ free_Name(&proxy_issuer);
+ free_name_constraints(&nc);
+ _hx509_path_free(&path);
+
+ return ret;
+}
+
+int
+hx509_verify_signature(hx509_context context,
+ const hx509_cert signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ return _hx509_verify_signature(context, signer->data, alg, data, sig);
+}
+
+int
+hx509_verify_hostname(hx509_context context,
+ const hx509_cert cert,
+ int require_match,
+ const char *hostname,
+ const struct sockaddr *sa,
+ /* XXX krb5_socklen_t */ int sa_size)
+{
+ if (sa && sa_size <= 0)
+ return EINVAL;
+ return 0;
+}
+
+int
+_hx509_set_cert_attribute(hx509_context context,
+ hx509_cert cert,
+ const heim_oid *oid,
+ const heim_octet_string *attr)
+{
+ hx509_cert_attribute a;
+ void *d;
+
+ if (hx509_cert_get_attribute(cert, oid) != NULL)
+ return 0;
+
+ d = realloc(cert->attrs.val,
+ sizeof(cert->attrs.val[0]) * (cert->attrs.len + 1));
+ if (d == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ cert->attrs.val = d;
+
+ a = malloc(sizeof(*a));
+ if (a == NULL)
+ return ENOMEM;
+
+ der_copy_octet_string(attr, &a->data);
+ der_copy_oid(oid, &a->oid);
+
+ cert->attrs.val[cert->attrs.len] = a;
+ cert->attrs.len++;
+
+ return 0;
+}
+
+hx509_cert_attribute
+hx509_cert_get_attribute(hx509_cert cert, const heim_oid *oid)
+{
+ int 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];
+ return NULL;
+}
+
+int
+hx509_cert_set_friendly_name(hx509_cert cert, const char *name)
+{
+ if (cert->friendlyname)
+ free(cert->friendlyname);
+ cert->friendlyname = strdup(name);
+ if (cert->friendlyname == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+
+const char *
+hx509_cert_get_friendly_name(hx509_cert cert)
+{
+ hx509_cert_attribute a;
+ PKCS9_friendlyName n;
+ size_t sz;
+ int ret, i;
+
+ if (cert->friendlyname)
+ return cert->friendlyname;
+
+ a = hx509_cert_get_attribute(cert, oid_id_pkcs_9_at_friendlyName());
+ if (a == NULL) {
+ /* XXX use subject name ? */
+ return NULL;
+ }
+
+ 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;
+ else
+ cert->friendlyname[i] = 'X';
+ }
+ cert->friendlyname[i] = '\0';
+ free_PKCS9_friendlyName(&n);
+
+ return cert->friendlyname;
+}
+
+void
+_hx509_query_clear(hx509_query *q)
+{
+ memset(q, 0, sizeof(*q));
+}
+
+int
+hx509_query_alloc(hx509_context context, hx509_query **q)
+{
+ *q = calloc(1, sizeof(**q));
+ if (*q == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+void
+hx509_query_match_option(hx509_query *q, hx509_query_option option)
+{
+ switch(option) {
+ case HX509_QUERY_OPTION_PRIVATE_KEY:
+ q->match |= HX509_QUERY_PRIVATE_KEY;
+ break;
+ case HX509_QUERY_OPTION_KU_ENCIPHERMENT:
+ q->match |= HX509_QUERY_KU_ENCIPHERMENT;
+ break;
+ case HX509_QUERY_OPTION_KU_DIGITALSIGNATURE:
+ q->match |= HX509_QUERY_KU_DIGITALSIGNATURE;
+ break;
+ case HX509_QUERY_OPTION_KU_KEYCERTSIGN:
+ q->match |= HX509_QUERY_KU_KEYCERTSIGN;
+ break;
+ case HX509_QUERY_OPTION_END:
+ default:
+ break;
+ }
+}
+
+int
+hx509_query_match_issuer_serial(hx509_query *q,
+ const Name *issuer,
+ const heim_integer *serialNumber)
+{
+ int ret;
+ if (q->serial) {
+ der_free_heim_integer(q->serial);
+ free(q->serial);
+ }
+ q->serial = malloc(sizeof(*q->serial));
+ if (q->serial == NULL)
+ return ENOMEM;
+ ret = der_copy_heim_integer(serialNumber, q->serial);
+ if (ret) {
+ free(q->serial);
+ q->serial = NULL;
+ return ret;
+ }
+ if (q->issuer_name) {
+ free_Name(q->issuer_name);
+ free(q->issuer_name);
+ }
+ q->issuer_name = malloc(sizeof(*q->issuer_name));
+ if (q->issuer_name == NULL)
+ return ENOMEM;
+ ret = copy_Name(issuer, q->issuer_name);
+ if (ret) {
+ free(q->issuer_name);
+ q->issuer_name = NULL;
+ return ret;
+ }
+ q->match |= HX509_QUERY_MATCH_SERIALNUMBER|HX509_QUERY_MATCH_ISSUER_NAME;
+ return 0;
+}
+
+
+int
+hx509_query_match_friendly_name(hx509_query *q, const char *name)
+{
+ if (q->friendlyname)
+ free(q->friendlyname);
+ q->friendlyname = strdup(name);
+ if (q->friendlyname == NULL)
+ return ENOMEM;
+ q->match |= HX509_QUERY_MATCH_FRIENDLY_NAME;
+ return 0;
+}
+
+int
+hx509_query_match_cmp_func(hx509_query *q,
+ int (*func)(void *, hx509_cert),
+ void *ctx)
+{
+ if (func)
+ q->match |= HX509_QUERY_MATCH_FUNCTION;
+ else
+ q->match &= ~HX509_QUERY_MATCH_FUNCTION;
+ q->cmp_func = func;
+ q->cmp_func_ctx = ctx;
+ return 0;
+}
+
+
+void
+hx509_query_free(hx509_context context, hx509_query *q)
+{
+ 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));
+ }
+ free(q);
+}
+
+int
+_hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert cert)
+{
+ Certificate *c = _hx509_get_cert(cert);
+
+ if ((q->match & HX509_QUERY_FIND_ISSUER_CERT) &&
+ _hx509_cert_is_parent_cmp(q->subject, c, 0) != 0)
+ return 0;
+
+ if ((q->match & HX509_QUERY_MATCH_CERTIFICATE) &&
+ _hx509_Certificate_cmp(q->certificate, c) != 0)
+ return 0;
+
+ if ((q->match & HX509_QUERY_MATCH_SERIALNUMBER)
+ && 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_SUBJECT_NAME)
+ && _hx509_name_cmp(&c->tbsCertificate.subject, q->subject_name) != 0)
+ 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) {
+ if (der_heim_octet_string_cmp(&si, q->subject_id) != 0)
+ ret = 1;
+ free_SubjectKeyIdentifier(&si);
+ }
+ if (ret)
+ return 0;
+ }
+ if ((q->match & HX509_QUERY_MATCH_ISSUER_ID))
+ return 0;
+ if ((q->match & HX509_QUERY_PRIVATE_KEY) &&
+ _hx509_cert_private_key(cert) == NULL)
+ return 0;
+
+ {
+ unsigned ku = 0;
+ if (q->match & HX509_QUERY_KU_DIGITALSIGNATURE)
+ ku |= (1 << 0);
+ if (q->match & HX509_QUERY_KU_NONREPUDIATION)
+ ku |= (1 << 1);
+ if (q->match & HX509_QUERY_KU_ENCIPHERMENT)
+ ku |= (1 << 2);
+ if (q->match & HX509_QUERY_KU_DATAENCIPHERMENT)
+ ku |= (1 << 3);
+ if (q->match & HX509_QUERY_KU_KEYAGREEMENT)
+ ku |= (1 << 4);
+ if (q->match & HX509_QUERY_KU_KEYCERTSIGN)
+ ku |= (1 << 5);
+ if (q->match & HX509_QUERY_KU_CRLSIGN)
+ ku |= (1 << 6);
+ if (ku && check_key_usage(context, c, ku, TRUE))
+ return 0;
+ }
+ if ((q->match & HX509_QUERY_ANCHOR))
+ return 0;
+
+ 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());
+ if (a == NULL)
+ return 0;
+ if (der_heim_octet_string_cmp(&a->data, q->local_key_id) != 0)
+ return 0;
+ }
+
+ if (q->match & HX509_QUERY_NO_MATCH_PATH) {
+ size_t i;
+
+ for (i = 0; i < q->path->len; i++)
+ if (hx509_cert_cmp(q->path->val[i], cert) == 0)
+ return 0;
+ }
+ if (q->match & HX509_QUERY_MATCH_FRIENDLY_NAME) {
+ const char *name = hx509_cert_get_friendly_name(cert);
+ if (name == NULL)
+ return 0;
+ if (strcasecmp(q->friendlyname, name) != 0)
+ return 0;
+ }
+ if (q->match & HX509_QUERY_MATCH_FUNCTION) {
+ int ret = (*q->cmp_func)(q->cmp_func_ctx, cert);
+ 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 =
+ c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
+
+ ret = _hx509_verify_signature(context,
+ NULL,
+ hx509_signature_sha1(),
+ &os,
+ q->keyhash_sha1);
+ if (ret != 0)
+ return 0;
+ }
+
+ if (q->match & HX509_QUERY_MATCH_TIME) {
+ time_t t;
+ t = _hx509_Time2time_t(&c->tbsCertificate.validity.notBefore);
+ if (t > q->timenow)
+ return 0;
+ t = _hx509_Time2time_t(&c->tbsCertificate.validity.notAfter);
+ if (t < q->timenow)
+ return 0;
+ }
+
+ if (q->match & ~HX509_QUERY_MASK)
+ return 0;
+
+ return 1;
+}
+
+int
+hx509_cert_check_eku(hx509_context context, hx509_cert cert,
+ const heim_oid *eku, int allow_any_eku)
+{
+ ExtKeyUsage e;
+ int ret, i;
+
+ ret = find_extension_eku(_hx509_get_cert(cert), &e);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+
+ for (i = 0; i < e.len; i++) {
+ if (der_heim_oid_cmp(eku, &e.val[i]) == 0) {
+ free_ExtKeyUsage(&e);
+ return 0;
+ }
+ if (allow_any_eku) {
+#if 0
+ if (der_heim_oid_cmp(id_any_eku, &e.val[i]) == 0) {
+ free_ExtKeyUsage(&e);
+ return 0;
+ }
+#endif
+ }
+ }
+ free_ExtKeyUsage(&e);
+ hx509_clear_error_string(context);
+ return HX509_CERTIFICATE_MISSING_EKU;
+}
+
+int
+_hx509_cert_get_keyusage(hx509_context context,
+ hx509_cert c,
+ KeyUsage *ku)
+{
+ Certificate *cert;
+ const Extension *e;
+ size_t size;
+ int ret, i = 0;
+
+ memset(ku, 0, sizeof(*ku));
+
+ cert = _hx509_get_cert(c);
+
+ if (_hx509_cert_get_version(cert) < 3)
+ return 0;
+
+ e = find_extension(cert, 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;
+ return 0;
+}
diff --git a/source4/heimdal/lib/hx509/cms.c b/source4/heimdal/lib/hx509/cms.c
new file mode 100644
index 0000000000..4ed70b8f84
--- /dev/null
+++ b/source4/heimdal/lib/hx509/cms.c
@@ -0,0 +1,1279 @@
+/*
+ * Copyright (c) 2003 - 2006 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: cms.c,v 1.48 2007/01/08 18:45:03 lha Exp $");
+
+#define ALLOC(X, N) (X) = calloc((N), sizeof(*(X)))
+#define ALLOC_SEQ(X, N) do { (X)->len = (N); ALLOC((X)->val, (N)); } while(0)
+
+int
+hx509_cms_wrap_ContentInfo(const heim_oid *oid,
+ const heim_octet_string *buf,
+ heim_octet_string *res)
+{
+ ContentInfo ci;
+ size_t size;
+ int ret;
+
+ memset(res, 0, sizeof(*res));
+ memset(&ci, 0, sizeof(ci));
+
+ ret = der_copy_oid(oid, &ci.contentType);
+ if (ret)
+ return ret;
+ ALLOC(ci.content, 1);
+ if (ci.content == NULL) {
+ free_ContentInfo(&ci);
+ return ENOMEM;
+ }
+ ci.content->data = malloc(buf->length);
+ if (ci.content->data == NULL) {
+ free_ContentInfo(&ci);
+ return ENOMEM;
+ }
+ memcpy(ci.content->data, buf->data, buf->length);
+ ci.content->length = buf->length;
+
+ ASN1_MALLOC_ENCODE(ContentInfo, res->data, res->length, &ci, &size, ret);
+ free_ContentInfo(&ci);
+ if (ret)
+ return ret;
+ if (res->length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ return 0;
+}
+
+int
+hx509_cms_unwrap_ContentInfo(const heim_octet_string *in,
+ heim_oid *oid,
+ heim_octet_string *out,
+ int *have_data)
+{
+ ContentInfo ci;
+ size_t size;
+ int ret;
+
+ memset(oid, 0, sizeof(*oid));
+ memset(out, 0, sizeof(*out));
+
+ ret = decode_ContentInfo(in->data, in->length, &ci, &size);
+ if (ret)
+ return ret;
+
+ ret = der_copy_oid(&ci.contentType, oid);
+ if (ret) {
+ free_ContentInfo(&ci);
+ return ret;
+ }
+ if (ci.content) {
+ ret = der_copy_octet_string(ci.content, out);
+ if (ret) {
+ der_free_oid(oid);
+ free_ContentInfo(&ci);
+ return ret;
+ }
+ } else
+ memset(out, 0, sizeof(*out));
+
+ if (have_data)
+ *have_data = (ci.content != NULL) ? 1 : 0;
+
+ free_ContentInfo(&ci);
+
+ return 0;
+}
+
+static int
+fill_CMSIdentifier(const hx509_cert cert, CMSIdentifier *id)
+{
+ hx509_name name;
+ int ret;
+
+ id->element = choice_CMSIdentifier_issuerAndSerialNumber;
+ ret = hx509_cert_get_issuer(cert, &name);
+ if (ret)
+ return ret;
+ ret = copy_Name(&name->der_name,
+ &id->u.issuerAndSerialNumber.issuer);
+ hx509_name_free(&name);
+ if (ret)
+ return ret;
+
+ ret = hx509_cert_get_serialnumber(cert,
+ &id->u.issuerAndSerialNumber.serialNumber);
+ return ret;
+}
+
+static int
+unparse_CMSIdentifier(hx509_context context,
+ CMSIdentifier *id,
+ char **str)
+{
+ int ret;
+
+ *str = NULL;
+ switch (id->element) {
+ case choice_CMSIdentifier_issuerAndSerialNumber: {
+ IssuerAndSerialNumber *iasn;
+ char *serial, *name;
+
+ iasn = &id->u.issuerAndSerialNumber;
+
+ ret = _hx509_Name_to_string(&iasn->issuer, &name);
+ if(ret)
+ return ret;
+ ret = der_print_hex_heim_integer(&iasn->serialNumber, &serial);
+ if (ret) {
+ free(name);
+ return ret;
+ }
+ asprintf(str, "certificate issued by %s with serial number %s",
+ name, serial);
+ free(name);
+ free(serial);
+ break;
+ }
+ case choice_CMSIdentifier_subjectKeyIdentifier: {
+ KeyIdentifier *ki = &id->u.subjectKeyIdentifier;
+ char *keyid;
+ ssize_t len;
+
+ len = hex_encode(ki->data, ki->length, &keyid);
+ if (len < 0)
+ return ENOMEM;
+
+ asprintf(str, "certificate with id %s", keyid);
+ free(keyid);
+ break;
+ }
+ default:
+ asprintf(str, "certificate have unknown CMSidentifier type");
+ break;
+ }
+ if (*str == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+static int
+find_CMSIdentifier(hx509_context context,
+ CMSIdentifier *client,
+ hx509_certs certs,
+ hx509_cert *signer_cert,
+ int match)
+{
+ hx509_query q;
+ hx509_cert cert;
+ Certificate c;
+ int ret;
+
+ memset(&c, 0, sizeof(c));
+ _hx509_query_clear(&q);
+
+ *signer_cert = NULL;
+
+ switch (client->element) {
+ case choice_CMSIdentifier_issuerAndSerialNumber:
+ q.serial = &client->u.issuerAndSerialNumber.serialNumber;
+ q.issuer_name = &client->u.issuerAndSerialNumber.issuer;
+ q.match = HX509_QUERY_MATCH_SERIALNUMBER|HX509_QUERY_MATCH_ISSUER_NAME;
+ break;
+ case choice_CMSIdentifier_subjectKeyIdentifier:
+ q.subject_id = &client->u.subjectKeyIdentifier;
+ q.match = HX509_QUERY_MATCH_SUBJECT_KEY_ID;
+ break;
+ default:
+ hx509_set_error_string(context, 0, HX509_CMS_NO_RECIPIENT_CERTIFICATE,
+ "unknown CMS identifier element");
+ return HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ }
+
+ q.match |= match;
+
+ q.match |= HX509_QUERY_MATCH_TIME;
+ q.timenow = time(NULL);
+
+ ret = hx509_certs_find(context, certs, &q, &cert);
+ if (ret == HX509_CERT_NOT_FOUND) {
+ char *str;
+
+ ret = unparse_CMSIdentifier(context, client, &str);
+ if (ret == 0) {
+ hx509_set_error_string(context, 0,
+ HX509_CMS_NO_RECIPIENT_CERTIFICATE,
+ "Failed to find %s", str);
+ } else
+ hx509_clear_error_string(context);
+ return HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ } else if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND,
+ HX509_CMS_NO_RECIPIENT_CERTIFICATE,
+ "Failed to find CMS id in cert store");
+ return HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ }
+
+ *signer_cert = cert;
+
+ return 0;
+}
+
+int
+hx509_cms_unenvelope(hx509_context context,
+ hx509_certs certs,
+ int flags,
+ const void *data,
+ size_t length,
+ const heim_octet_string *encryptedContent,
+ heim_oid *contentType,
+ heim_octet_string *content)
+{
+ heim_octet_string key;
+ EnvelopedData ed;
+ hx509_cert cert;
+ AlgorithmIdentifier *ai;
+ const heim_octet_string *enccontent;
+ heim_octet_string *params, params_data;
+ heim_octet_string ivec;
+ size_t size;
+ int ret, i, matched = 0, findflags = 0;
+
+
+ memset(&key, 0, sizeof(key));
+ memset(&ed, 0, sizeof(ed));
+ memset(&ivec, 0, sizeof(ivec));
+ memset(content, 0, sizeof(*content));
+ memset(contentType, 0, sizeof(*contentType));
+
+ if ((flags & HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT) == 0)
+ findflags |= HX509_QUERY_KU_ENCIPHERMENT;
+
+ ret = decode_EnvelopedData(data, length, &ed, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode EnvelopedData");
+ return ret;
+ }
+
+ if (ed.recipientInfos.len == 0) {
+ ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ hx509_set_error_string(context, 0, ret,
+ "No recipient info in enveloped data");
+ goto out;
+ }
+
+ enccontent = ed.encryptedContentInfo.encryptedContent;
+ if (enccontent == NULL) {
+ if (encryptedContent == NULL) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "Content missing from encrypted data");
+ goto out;
+ }
+ enccontent = encryptedContent;
+ }
+
+ cert = NULL;
+ for (i = 0; i < ed.recipientInfos.len; i++) {
+ KeyTransRecipientInfo *ri;
+ char *str;
+ int ret2;
+
+ ri = &ed.recipientInfos.val[i];
+
+ /* ret = search_keyset(ri,
+ * PRIVATE_KEY,
+ * ki->keyEncryptionAlgorithm.algorithm);
+ */
+
+ ret = find_CMSIdentifier(context, &ri->rid, certs, &cert,
+ HX509_QUERY_PRIVATE_KEY|findflags);
+ if (ret)
+ continue;
+
+ matched = 1; /* found a matching certificate, let decrypt */
+
+ ret = _hx509_cert_private_decrypt(context,
+ &ri->encryptedKey,
+ &ri->keyEncryptionAlgorithm.algorithm,
+ cert, &key);
+
+ hx509_cert_free(cert);
+ if (ret == 0)
+ break; /* succuessfully decrypted cert */
+ cert = NULL;
+ ret2 = unparse_CMSIdentifier(context, &ri->rid, &str);
+ if (ret2 == 0) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to decrypt with %s", str);
+ free(str);
+ }
+ }
+
+ if (!matched) {
+ ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ hx509_set_error_string(context, 0, ret,
+ "No private key matched any certificate");
+ goto out;
+ }
+
+ if (cert == NULL) {
+ ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "No private key decrypted the transfer key");
+ goto out;
+ }
+
+ ret = der_copy_oid(&ed.encryptedContentInfo.contentType, contentType);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy EnvelopedData content oid");
+ goto out;
+ }
+
+ ai = &ed.encryptedContentInfo.contentEncryptionAlgorithm;
+ if (ai->parameters) {
+ params_data.data = ai->parameters->data;
+ params_data.length = ai->parameters->length;
+ params = &params_data;
+ } else
+ params = NULL;
+
+ {
+ hx509_crypto crypto;
+
+ ret = hx509_crypto_init(context, NULL, &ai->algorithm, &crypto);
+ if (ret)
+ goto out;
+
+ if (params) {
+ ret = hx509_crypto_set_params(context, crypto, params, &ivec);
+ if (ret) {
+ hx509_crypto_destroy(crypto);
+ goto out;
+ }
+ }
+
+ ret = hx509_crypto_set_key_data(crypto, key.data, key.length);
+ if (ret) {
+ hx509_crypto_destroy(crypto);
+ hx509_set_error_string(context, 0, ret,
+ "Failed to set key for decryption "
+ "of EnvelopedData");
+ goto out;
+ }
+
+ ret = hx509_crypto_decrypt(crypto,
+ enccontent->data,
+ enccontent->length,
+ ivec.length ? &ivec : NULL,
+ content);
+ hx509_crypto_destroy(crypto);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decrypt EnvelopedData");
+ goto out;
+ }
+ }
+
+out:
+
+ free_EnvelopedData(&ed);
+ der_free_octet_string(&key);
+ if (ivec.length)
+ der_free_octet_string(&ivec);
+ if (ret) {
+ der_free_oid(contentType);
+ der_free_octet_string(content);
+ }
+
+ return ret;
+}
+
+int
+hx509_cms_envelope_1(hx509_context context,
+ hx509_cert cert,
+ const void *data,
+ size_t length,
+ const heim_oid *encryption_type,
+ const heim_oid *contentType,
+ heim_octet_string *content)
+{
+ KeyTransRecipientInfo *ri;
+ heim_octet_string ivec;
+ heim_octet_string key;
+ hx509_crypto crypto = NULL;
+ EnvelopedData ed;
+ size_t size;
+ int ret;
+
+ memset(&ivec, 0, sizeof(ivec));
+ memset(&key, 0, sizeof(key));
+ memset(&ed, 0, sizeof(ed));
+ memset(content, 0, sizeof(*content));
+
+ if (encryption_type == NULL)
+ encryption_type = oid_id_aes_256_cbc();
+
+ ret = _hx509_check_key_usage(context, cert, 1 << 2, TRUE);
+ if (ret)
+ goto out;
+
+ ret = hx509_crypto_init(context, NULL, encryption_type, &crypto);
+ if (ret)
+ goto out;
+
+ ret = hx509_crypto_set_random_key(crypto, &key);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Create random key for EnvelopedData content");
+ goto out;
+ }
+
+ ret = hx509_crypto_encrypt(crypto,
+ data,
+ length,
+ &ivec,
+ &ed.encryptedContentInfo.encryptedContent);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to encrypt EnvelopedData content");
+ goto out;
+ }
+
+ {
+ AlgorithmIdentifier *enc_alg;
+ enc_alg = &ed.encryptedContentInfo.contentEncryptionAlgorithm;
+ ret = der_copy_oid(encryption_type, &enc_alg->algorithm);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to set crypto oid "
+ "for EnvelopedData");
+ goto out;
+ }
+ ALLOC(enc_alg->parameters, 1);
+ if (enc_alg->parameters == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to allocate crypto paramaters "
+ "for EnvelopedData");
+ goto out;
+ }
+
+ ret = hx509_crypto_get_params(context,
+ crypto,
+ &ivec,
+ enc_alg->parameters);
+ if (ret) {
+ goto out;
+ }
+ }
+
+ ALLOC_SEQ(&ed.recipientInfos, 1);
+ if (ed.recipientInfos.val == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to allocate recipients info "
+ "for EnvelopedData");
+ goto out;
+ }
+
+ ri = &ed.recipientInfos.val[0];
+
+ ri->version = 0;
+ ret = fill_CMSIdentifier(cert, &ri->rid);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to set CMS identifier info "
+ "for EnvelopedData");
+ goto out;
+ }
+
+ ret = _hx509_cert_public_encrypt(context,
+ &key, cert,
+ &ri->keyEncryptionAlgorithm.algorithm,
+ &ri->encryptedKey);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to encrypt transport key for "
+ "EnvelopedData");
+ goto out;
+ }
+
+ /*
+ *
+ */
+
+ ed.version = 0;
+ ed.originatorInfo = NULL;
+
+ ret = der_copy_oid(contentType, &ed.encryptedContentInfo.contentType);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy content oid for "
+ "EnvelopedData");
+ goto out;
+ }
+
+ ed.unprotectedAttrs = NULL;
+
+ ASN1_MALLOC_ENCODE(EnvelopedData, content->data, content->length,
+ &ed, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to encode EnvelopedData");
+ goto out;
+ }
+ if (size != content->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+out:
+ if (crypto)
+ hx509_crypto_destroy(crypto);
+ if (ret)
+ der_free_octet_string(content);
+ der_free_octet_string(&key);
+ der_free_octet_string(&ivec);
+ free_EnvelopedData(&ed);
+
+ return ret;
+}
+
+static int
+any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs)
+{
+ int ret, i;
+
+ if (sd->certificates == NULL)
+ return 0;
+
+ for (i = 0; i < sd->certificates->len; i++) {
+ Certificate cert;
+ hx509_cert c;
+
+ const void *p = sd->certificates->val[i].data;
+ size_t size, length = sd->certificates->val[i].length;
+
+ ret = decode_Certificate(p, length, &cert, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode certificate %d "
+ "in SignedData.certificates", i);
+ return ret;
+ }
+
+ ret = hx509_cert_init(context, &cert, &c);
+ free_Certificate(&cert);
+ if (ret)
+ return ret;
+ ret = hx509_certs_add(context, certs, c);
+ hx509_cert_free(c);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const Attribute *
+find_attribute(const CMSAttributes *attr, const heim_oid *oid)
+{
+ int i;
+ for (i = 0; i < attr->len; i++)
+ if (der_heim_oid_cmp(&attr->val[i].type, oid) == 0)
+ return &attr->val[i];
+ return NULL;
+}
+
+int
+hx509_cms_verify_signed(hx509_context context,
+ hx509_verify_ctx ctx,
+ const void *data,
+ size_t length,
+ hx509_certs store,
+ heim_oid *contentType,
+ heim_octet_string *content,
+ hx509_certs *signer_certs)
+{
+ SignerInfo *signer_info;
+ hx509_cert cert = NULL;
+ hx509_certs certs = NULL;
+ SignedData sd;
+ size_t size;
+ int ret, i, found_valid_sig;
+
+ *signer_certs = NULL;
+ content->data = NULL;
+ content->length = 0;
+ contentType->length = 0;
+ contentType->components = NULL;
+
+ memset(&sd, 0, sizeof(sd));
+
+ ret = decode_SignedData(data, length, &sd, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode SignedData");
+ goto out;
+ }
+
+ if (sd.encapContentInfo.eContent == NULL) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "No content data in SignedData");
+ goto out;
+ }
+
+ ret = hx509_certs_init(context, "MEMORY:cms-cert-buffer",
+ 0, NULL, &certs);
+ if (ret)
+ goto out;
+
+ ret = hx509_certs_init(context, "MEMORY:cms-signer-certs",
+ 0, NULL, signer_certs);
+ if (ret)
+ goto out;
+
+ /* XXX Check CMS version */
+
+ ret = any_to_certs(context, &sd, certs);
+ if (ret)
+ goto out;
+
+ if (store) {
+ ret = hx509_certs_merge(context, certs, store);
+ if (ret)
+ goto out;
+ }
+
+ for (found_valid_sig = 0, i = 0; i < sd.signerInfos.len; i++) {
+ heim_octet_string *signed_data;
+ const heim_oid *match_oid;
+ heim_oid decode_oid;
+
+ signer_info = &sd.signerInfos.val[i];
+ match_oid = NULL;
+
+ if (signer_info->signature.length == 0) {
+ ret = HX509_CMS_MISSING_SIGNER_DATA;
+ hx509_set_error_string(context, 0, ret,
+ "SignerInfo %d in SignedData "
+ "missing sigature", i);
+ continue;
+ }
+
+ ret = find_CMSIdentifier(context, &signer_info->sid, certs, &cert,
+ HX509_QUERY_KU_DIGITALSIGNATURE);
+ if (ret)
+ continue;
+
+ if (signer_info->signedAttrs) {
+ const Attribute *attr;
+
+ CMSAttributes sa;
+ heim_octet_string os;
+
+ sa.val = signer_info->signedAttrs->val;
+ sa.len = signer_info->signedAttrs->len;
+
+ /* verify that sigature exists */
+ attr = find_attribute(&sa, oid_id_pkcs9_messageDigest());
+ if (attr == NULL) {
+ ret = HX509_CRYPTO_SIGNATURE_MISSING;
+ hx509_set_error_string(context, 0, ret,
+ "SignerInfo have signed attributes "
+ "but messageDigest (signature) "
+ "is missing");
+ goto next_sigature;
+ }
+ if (attr->value.len != 1) {
+ ret = HX509_CRYPTO_SIGNATURE_MISSING;
+ hx509_set_error_string(context, 0, ret,
+ "SignerInfo have more then one "
+ "messageDigest (signature)");
+ goto next_sigature;
+ }
+
+ ret = decode_MessageDigest(attr->value.val[0].data,
+ attr->value.val[0].length,
+ &os,
+ &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode "
+ "messageDigest (signature)");
+ goto next_sigature;
+ }
+
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &signer_info->digestAlgorithm,
+ sd.encapContentInfo.eContent,
+ &os);
+ der_free_octet_string(&os);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to verify messageDigest");
+ goto next_sigature;
+ }
+
+ /*
+ * Fetch content oid inside signedAttrs or set it to
+ * id-pkcs7-data.
+ */
+ attr = find_attribute(&sa, oid_id_pkcs9_contentType());
+ if (attr == NULL) {
+ match_oid = oid_id_pkcs7_data();
+ } else {
+ if (attr->value.len != 1) {
+ ret = HX509_CMS_DATA_OID_MISMATCH;
+ hx509_set_error_string(context, 0, ret,
+ "More then one oid in signedAttrs");
+ goto next_sigature;
+
+ }
+ ret = decode_ContentType(attr->value.val[0].data,
+ attr->value.val[0].length,
+ &decode_oid,
+ &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode "
+ "oid in signedAttrs");
+ goto next_sigature;
+ }
+ match_oid = &decode_oid;
+ }
+
+ ALLOC(signed_data, 1);
+ if (signed_data == NULL) {
+ if (match_oid == &decode_oid)
+ der_free_oid(&decode_oid);
+ ret = ENOMEM;
+ hx509_clear_error_string(context);
+ goto next_sigature;
+ }
+
+ ASN1_MALLOC_ENCODE(CMSAttributes,
+ signed_data->data,
+ signed_data->length,
+ &sa,
+ &size, ret);
+ if (ret) {
+ if (match_oid == &decode_oid)
+ der_free_oid(&decode_oid);
+ free(signed_data);
+ hx509_clear_error_string(context);
+ goto next_sigature;
+ }
+ if (size != signed_data->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ } else {
+ signed_data = sd.encapContentInfo.eContent;
+ match_oid = oid_id_pkcs7_data();
+ }
+
+ if (der_heim_oid_cmp(match_oid, &sd.encapContentInfo.eContentType)) {
+ ret = HX509_CMS_DATA_OID_MISMATCH;
+ hx509_set_error_string(context, 0, ret,
+ "Oid in message mismatch from the expected");
+ }
+ if (match_oid == &decode_oid)
+ der_free_oid(&decode_oid);
+
+ if (ret == 0) {
+ ret = hx509_verify_signature(context,
+ cert,
+ &signer_info->signatureAlgorithm,
+ signed_data,
+ &signer_info->signature);
+ if (ret)
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to verify sigature in "
+ "CMS SignedData");
+ }
+ if (signed_data != sd.encapContentInfo.eContent) {
+ der_free_octet_string(signed_data);
+ free(signed_data);
+ }
+ if (ret)
+ goto next_sigature;
+
+ ret = hx509_verify_path(context, ctx, cert, certs);
+ if (ret)
+ goto next_sigature;
+
+ ret = hx509_certs_add(context, *signer_certs, cert);
+ if (ret)
+ goto next_sigature;
+
+ found_valid_sig++;
+
+ next_sigature:
+ if (cert)
+ hx509_cert_free(cert);
+ cert = NULL;
+ }
+ if (found_valid_sig == 0) {
+ if (ret == 0) {
+ ret = HX509_CMS_SIGNER_NOT_FOUND;
+ hx509_set_error_string(context, 0, ret,
+ "No signers where found");
+ }
+ goto out;
+ }
+
+ ret = der_copy_oid(&sd.encapContentInfo.eContentType, contentType);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ content->data = malloc(sd.encapContentInfo.eContent->length);
+ if (content->data == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ content->length = sd.encapContentInfo.eContent->length;
+ memcpy(content->data,sd.encapContentInfo.eContent->data,content->length);
+
+out:
+ free_SignedData(&sd);
+ if (certs)
+ hx509_certs_free(&certs);
+ if (ret) {
+ if (*signer_certs)
+ hx509_certs_free(signer_certs);
+ der_free_oid(contentType);
+ der_free_octet_string(content);
+ }
+
+ return ret;
+}
+
+int
+_hx509_set_digest_alg(DigestAlgorithmIdentifier *id,
+ const heim_oid *oid,
+ void *param, size_t length)
+{
+ int ret;
+ if (param) {
+ id->parameters = malloc(sizeof(*id->parameters));
+ if (id->parameters == NULL)
+ return ENOMEM;
+ id->parameters->data = malloc(length);
+ if (id->parameters->data == NULL) {
+ free(id->parameters);
+ id->parameters = NULL;
+ return ENOMEM;
+ }
+ memcpy(id->parameters->data, param, length);
+ id->parameters->length = length;
+ } else
+ id->parameters = NULL;
+ ret = der_copy_oid(oid, &id->algorithm);
+ if (ret) {
+ if (id->parameters) {
+ free(id->parameters->data);
+ free(id->parameters);
+ id->parameters = NULL;
+ }
+ return ret;
+ }
+ return 0;
+}
+
+static int
+add_one_attribute(Attribute **attr,
+ unsigned int *len,
+ const heim_oid *oid,
+ heim_octet_string *data)
+{
+ void *d;
+ int ret;
+
+ d = realloc(*attr, sizeof((*attr)[0]) * (*len + 1));
+ if (d == NULL)
+ return ENOMEM;
+ (*attr) = d;
+
+ ret = der_copy_oid(oid, &(*attr)[*len].type);
+ if (ret)
+ return ret;
+
+ ALLOC_SEQ(&(*attr)[*len].value, 1);
+ if ((*attr)[*len].value.val == NULL) {
+ der_free_oid(&(*attr)[*len].type);
+ return ENOMEM;
+ }
+
+ (*attr)[*len].value.val[0].data = data->data;
+ (*attr)[*len].value.val[0].length = data->length;
+
+ *len += 1;
+
+ return 0;
+}
+
+int
+hx509_cms_create_signed_1(hx509_context context,
+ const heim_oid *eContentType,
+ const void *data, size_t length,
+ const AlgorithmIdentifier *digest_alg,
+ hx509_cert cert,
+ hx509_peer_info peer,
+ hx509_certs anchors,
+ hx509_certs pool,
+ heim_octet_string *signed_data)
+{
+ AlgorithmIdentifier digest;
+ hx509_name name;
+ SignerInfo *signer_info;
+ heim_octet_string buf;
+ SignedData sd;
+ int ret;
+ size_t size;
+ hx509_path path;
+
+ memset(&sd, 0, sizeof(sd));
+ memset(&name, 0, sizeof(name));
+ memset(&path, 0, sizeof(path));
+ memset(&digest, 0, sizeof(digest));
+
+ if (_hx509_cert_private_key(cert) == NULL) {
+ hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
+ "Private key missing for signing");
+ return HX509_PRIVATE_KEY_MISSING;
+ }
+
+ if (digest_alg == NULL) {
+ ret = hx509_crypto_select(context, HX509_SELECT_DIGEST,
+ _hx509_cert_private_key(cert), peer, &digest);
+ } else {
+ ret = copy_AlgorithmIdentifier(digest_alg, &digest);
+ if (ret)
+ hx509_clear_error_string(context);
+ }
+ if (ret)
+ goto out;
+
+ sd.version = CMSVersion_v3;
+
+ der_copy_oid(eContentType, &sd.encapContentInfo.eContentType);
+ ALLOC(sd.encapContentInfo.eContent, 1);
+ if (sd.encapContentInfo.eContent == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ sd.encapContentInfo.eContent->data = malloc(length);
+ if (sd.encapContentInfo.eContent->data == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ memcpy(sd.encapContentInfo.eContent->data, data, length);
+ sd.encapContentInfo.eContent->length = length;
+
+ ALLOC_SEQ(&sd.signerInfos, 1);
+ if (sd.signerInfos.val == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ signer_info = &sd.signerInfos.val[0];
+
+ signer_info->version = 1;
+
+ ret = fill_CMSIdentifier(cert, &signer_info->sid);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ signer_info->signedAttrs = NULL;
+ signer_info->unsignedAttrs = NULL;
+
+ ALLOC(signer_info->signedAttrs, 1);
+ if (signer_info->signedAttrs == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ {
+ heim_octet_string data;
+
+ ret = copy_AlgorithmIdentifier(&digest, &signer_info->digestAlgorithm);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ &digest,
+ sd.encapContentInfo.eContent,
+ NULL,
+ &data);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(MessageDigest,
+ buf.data,
+ buf.length,
+ &data,
+ &size,
+ ret);
+ der_free_octet_string(&data);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ if (size != buf.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = add_one_attribute(&signer_info->signedAttrs->val,
+ &signer_info->signedAttrs->len,
+ oid_id_pkcs9_messageDigest(),
+ &buf);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ }
+
+ if (der_heim_oid_cmp(eContentType, oid_id_pkcs7_data()) != 0) {
+
+ ASN1_MALLOC_ENCODE(ContentType,
+ buf.data,
+ buf.length,
+ eContentType,
+ &size,
+ ret);
+ if (ret)
+ goto out;
+ if (size != buf.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = add_one_attribute(&signer_info->signedAttrs->val,
+ &signer_info->signedAttrs->len,
+ oid_id_pkcs9_contentType(),
+ &buf);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+
+
+ {
+ CMSAttributes sa;
+ heim_octet_string os;
+
+ sa.val = signer_info->signedAttrs->val;
+ sa.len = signer_info->signedAttrs->len;
+
+ ASN1_MALLOC_ENCODE(CMSAttributes,
+ os.data,
+ os.length,
+ &sa,
+ &size,
+ ret);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ if (size != os.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = _hx509_create_signature(context,
+ _hx509_cert_private_key(cert),
+ hx509_signature_rsa_with_sha1(),
+ &os,
+ &signer_info->signatureAlgorithm,
+ &signer_info->signature);
+
+ der_free_octet_string(&os);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ ALLOC_SEQ(&sd.digestAlgorithms, 1);
+ if (sd.digestAlgorithms.val == NULL) {
+ ret = ENOMEM;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ret = copy_AlgorithmIdentifier(&digest, &sd.digestAlgorithms.val[0]);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ /*
+ * Provide best effort path
+ */
+ if (pool) {
+ _hx509_calculate_path(context,
+ HX509_CALCULATE_PATH_NO_ANCHOR,
+ time(NULL),
+ anchors,
+ 0,
+ cert,
+ pool,
+ &path);
+ } else
+ _hx509_path_append(context, &path, cert);
+
+
+ if (path.len) {
+ int i;
+
+ ALLOC(sd.certificates, 1);
+ if (sd.certificates == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ ALLOC_SEQ(sd.certificates, path.len);
+ if (sd.certificates->val == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < path.len; i++) {
+ ASN1_MALLOC_ENCODE(Certificate,
+ sd.certificates->val[i].data,
+ sd.certificates->val[i].length,
+ _hx509_get_cert(path.val[i]),
+ &size, ret);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ if (sd.certificates->val[i].length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(SignedData,
+ signed_data->data, signed_data->length,
+ &sd, &size, ret);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ if (signed_data->length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+out:
+ free_AlgorithmIdentifier(&digest);
+ _hx509_path_free(&path);
+ free_SignedData(&sd);
+
+ return ret;
+}
+
+int
+hx509_cms_decrypt_encrypted(hx509_context context,
+ hx509_lock lock,
+ const void *data,
+ size_t length,
+ heim_oid *contentType,
+ heim_octet_string *content)
+{
+ heim_octet_string cont;
+ CMSEncryptedData ed;
+ AlgorithmIdentifier *ai;
+ int ret;
+
+ memset(content, 0, sizeof(*content));
+ memset(&cont, 0, sizeof(cont));
+
+ ret = decode_CMSEncryptedData(data, length, &ed, NULL);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode CMSEncryptedData");
+ return ret;
+ }
+
+ if (ed.encryptedContentInfo.encryptedContent == NULL) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "No content in EncryptedData");
+ goto out;
+ }
+
+ ret = der_copy_oid(&ed.encryptedContentInfo.contentType, contentType);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ai = &ed.encryptedContentInfo.contentEncryptionAlgorithm;
+ if (ai->parameters == NULL) {
+ ret = HX509_ALG_NOT_SUPP;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ret = _hx509_pbe_decrypt(context,
+ lock,
+ ai,
+ ed.encryptedContentInfo.encryptedContent,
+ &cont);
+ if (ret)
+ goto out;
+
+ *content = cont;
+
+out:
+ if (ret) {
+ if (cont.data)
+ free(cont.data);
+ }
+ free_CMSEncryptedData(&ed);
+ return ret;
+}
diff --git a/source4/heimdal/lib/hx509/collector.c b/source4/heimdal/lib/hx509/collector.c
new file mode 100644
index 0000000000..ec172f46f4
--- /dev/null
+++ b/source4/heimdal/lib/hx509/collector.c
@@ -0,0 +1,324 @@
+/*
+ * 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: collector.c,v 1.16 2007/01/09 10:52:04 lha Exp $");
+
+struct private_key {
+ AlgorithmIdentifier alg;
+ hx509_private_key private_key;
+ heim_octet_string localKeyId;
+};
+
+struct hx509_collector {
+ hx509_lock lock;
+ hx509_certs unenvelop_certs;
+ hx509_certs certs;
+ struct {
+ struct private_key **data;
+ size_t len;
+ } val;
+};
+
+
+struct hx509_collector *
+_hx509_collector_alloc(hx509_context context, hx509_lock lock)
+{
+ struct hx509_collector *c;
+ int ret;
+
+ c = calloc(1, sizeof(*c));
+ if (c == NULL)
+ return NULL;
+ c->lock = lock;
+
+ ret = hx509_certs_init(context, "MEMORY:collector-unenvelop-cert",
+ 0,NULL, &c->unenvelop_certs);
+ if (ret) {
+ free(c);
+ return NULL;
+ }
+ c->val.data = NULL;
+ c->val.len = 0;
+ ret = hx509_certs_init(context, "MEMORY:collector-tmp-store",
+ 0, NULL, &c->certs);
+ if (ret) {
+ hx509_certs_free(&c->unenvelop_certs);
+ free(c);
+ return NULL;
+ }
+
+ return c;
+}
+
+hx509_lock
+_hx509_collector_get_lock(struct hx509_collector *c)
+{
+ return c->lock;
+}
+
+
+int
+_hx509_collector_certs_add(hx509_context context,
+ struct hx509_collector *c,
+ hx509_cert cert)
+{
+ return hx509_certs_add(context, c->certs, cert);
+}
+
+static void
+free_private_key(struct private_key *key)
+{
+ free_AlgorithmIdentifier(&key->alg);
+ if (key->private_key)
+ _hx509_private_key_free(&key->private_key);
+ der_free_octet_string(&key->localKeyId);
+ free(key);
+}
+
+int
+_hx509_collector_private_key_add(hx509_context context,
+ struct hx509_collector *c,
+ const AlgorithmIdentifier *alg,
+ hx509_private_key private_key,
+ const heim_octet_string *key_data,
+ const heim_octet_string *localKeyId)
+{
+ struct private_key *key;
+ void *d;
+ int ret;
+
+ key = calloc(1, sizeof(*key));
+ if (key == NULL)
+ return ENOMEM;
+
+ d = realloc(c->val.data, (c->val.len + 1) * sizeof(c->val.data[0]));
+ if (d == NULL) {
+ free(key);
+ hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
+ return ENOMEM;
+ }
+ c->val.data = d;
+
+ ret = copy_AlgorithmIdentifier(alg, &key->alg);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to copy "
+ "AlgorithmIdentifier");
+ goto out;
+ }
+ if (private_key) {
+ key->private_key = private_key;
+ } else {
+ ret = _hx509_parse_private_key(context, &alg->algorithm,
+ key_data->data, key_data->length,
+ &key->private_key);
+ if (ret)
+ goto out;
+ }
+ if (localKeyId) {
+ ret = der_copy_octet_string(localKeyId, &key->localKeyId);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy localKeyId");
+ goto out;
+ }
+ } else
+ memset(&key->localKeyId, 0, sizeof(key->localKeyId));
+
+ c->val.data[c->val.len] = key;
+ c->val.len++;
+
+out:
+ if (ret)
+ free_private_key(key);
+
+ return ret;
+}
+
+static int
+match_localkeyid(hx509_context context,
+ struct private_key *value,
+ hx509_certs certs)
+{
+ hx509_cert cert;
+ hx509_query q;
+ int ret;
+
+ if (value->localKeyId.length == 0) {
+ hx509_set_error_string(context, 0, HX509_LOCAL_ATTRIBUTE_MISSING,
+ "No local key attribute on private key");
+ return HX509_LOCAL_ATTRIBUTE_MISSING;
+ }
+
+ _hx509_query_clear(&q);
+ q.match |= HX509_QUERY_MATCH_LOCAL_KEY_ID;
+
+ q.local_key_id = &value->localKeyId;
+
+ ret = hx509_certs_find(context, certs, &q, &cert);
+ if (ret == 0) {
+
+ if (value->private_key)
+ _hx509_cert_assign_key(cert, value->private_key);
+ hx509_cert_free(cert);
+ }
+ return ret;
+}
+
+static int
+match_keys(hx509_context context, struct private_key *value, hx509_certs certs)
+{
+ hx509_cursor cursor;
+ hx509_cert c;
+ int ret, found = HX509_CERT_NOT_FOUND;
+
+ if (value->private_key == NULL) {
+ hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
+ "No private key to compare with");
+ return HX509_PRIVATE_KEY_MISSING;
+ }
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret)
+ return ret;
+
+ c = NULL;
+ while (1) {
+ ret = hx509_certs_next_cert(context, certs, cursor, &c);
+ if (ret)
+ break;
+ if (c == NULL)
+ break;
+ if (_hx509_cert_private_key(c)) {
+ hx509_cert_free(c);
+ continue;
+ }
+
+ ret = _hx509_match_keys(c, value->private_key);
+ if (ret) {
+ _hx509_cert_assign_key(c, value->private_key);
+ hx509_cert_free(c);
+ found = 0;
+ break;
+ }
+ hx509_cert_free(c);
+ }
+
+ hx509_certs_end_seq(context, certs, cursor);
+
+ if (found)
+ hx509_clear_error_string(context);
+
+ return found;
+}
+
+int
+_hx509_collector_collect_certs(hx509_context context,
+ struct hx509_collector *c,
+ hx509_certs *ret_certs)
+{
+ hx509_certs certs;
+ int ret, i;
+
+ *ret_certs = NULL;
+
+ ret = hx509_certs_init(context, "MEMORY:collector-store", 0, NULL, &certs);
+ if (ret)
+ return ret;
+
+ ret = hx509_certs_merge(context, certs, c->certs);
+ if (ret) {
+ hx509_certs_free(&certs);
+ return ret;
+ }
+
+ for (i = 0; i < c->val.len; i++) {
+ ret = match_localkeyid(context, c->val.data[i], certs);
+ if (ret == 0)
+ continue;
+ ret = match_keys(context, c->val.data[i], certs);
+ if (ret == 0)
+ continue;
+ }
+
+ *ret_certs = certs;
+
+ return 0;
+}
+
+int
+_hx509_collector_collect_private_keys(hx509_context context,
+ struct hx509_collector *c,
+ hx509_private_key **keys)
+{
+ int i, nkeys;
+
+ *keys = NULL;
+
+ for (i = 0, nkeys = 0; i < c->val.len; i++)
+ if (c->val.data[i]->private_key)
+ nkeys++;
+
+ *keys = calloc(nkeys + 1, sizeof(**keys));
+ if (*keys == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "malloc - out of memory");
+ return ENOMEM;
+ }
+
+ for (i = 0, nkeys = 0; i < c->val.len; i++) {
+ if (c->val.data[i]->private_key) {
+ (*keys)[nkeys++] = c->val.data[i]->private_key;
+ c->val.data[i]->private_key = NULL;
+ }
+ }
+ (*keys)[nkeys++] = NULL;
+
+ return 0;
+}
+
+
+void
+_hx509_collector_free(struct hx509_collector *c)
+{
+ int i;
+
+ if (c->unenvelop_certs)
+ hx509_certs_free(&c->unenvelop_certs);
+ if (c->certs)
+ hx509_certs_free(&c->certs);
+ for (i = 0; i < c->val.len; i++)
+ free_private_key(c->val.data[i]);
+ if (c->val.data)
+ free(c->val.data);
+ free(c);
+}
diff --git a/source4/heimdal/lib/hx509/crmf.asn1 b/source4/heimdal/lib/hx509/crmf.asn1
new file mode 100644
index 0000000000..4f02b26872
--- /dev/null
+++ b/source4/heimdal/lib/hx509/crmf.asn1
@@ -0,0 +1,113 @@
+-- $Id: crmf.asn1,v 1.1 2006/04/18 13:05:21 lha Exp $
+PKCS10 DEFINITIONS ::=
+
+BEGIN
+
+IMPORTS
+ Time,
+ GeneralName,
+ SubjectPublicKeyInfo,
+ RelativeDistinguishedName,
+ AttributeTypeAndValue,
+ Extension,
+ AlgorithmIdentifier
+ FROM rfc2459
+ heim_any
+ FROM heim;
+
+CRMFRDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+
+Controls ::= SEQUENCE -- SIZE(1..MAX) -- OF AttributeTypeAndValue
+
+-- XXX IMPLICIT brokenness
+POPOSigningKey ::= SEQUENCE {
+ poposkInput [0] IMPLICIT POPOSigningKeyInput OPTIONAL,
+ algorithmIdentifier AlgorithmIdentifier,
+ signature BIT STRING }
+
+PKMACValue ::= SEQUENCE {
+ algId AlgorithmIdentifier,
+ value BIT STRING
+}
+
+-- XXX IMPLICIT brokenness
+POPOSigningKeyInput ::= SEQUENCE {
+ authInfo CHOICE {
+ sender [0] IMPLICIT GeneralName,
+ publicKeyMAC PKMACValue
+ },
+ publicKey SubjectPublicKeyInfo
+} -- from CertTemplate
+
+
+PBMParameter ::= SEQUENCE {
+ salt OCTET STRING,
+ owf AlgorithmIdentifier,
+ iterationCount INTEGER,
+ mac AlgorithmIdentifier
+}
+
+SubsequentMessage ::= INTEGER {
+ encrCert (0),
+ challengeResp (1)
+}
+
+-- XXX IMPLICIT brokenness
+POPOPrivKey ::= CHOICE {
+ thisMessage [0] BIT STRING, -- Deprecated
+ subsequentMessage [1] IMPLICIT SubsequentMessage,
+ dhMAC [2] BIT STRING, -- Deprecated
+ agreeMAC [3] IMPLICIT PKMACValue,
+ encryptedKey [4] heim_any
+}
+
+-- XXX IMPLICIT brokenness
+ProofOfPossession ::= CHOICE {
+ raVerified [0] NULL,
+ signature [1] POPOSigningKey,
+ keyEncipherment [2] POPOPrivKey,
+ keyAgreement [3] POPOPrivKey
+}
+
+CertTemplate ::= SEQUENCE {
+ version [0] INTEGER OPTIONAL,
+ serialNumber [1] INTEGER OPTIONAL,
+ signingAlg [2] SEQUENCE {
+ algorithm OBJECT IDENTIFIER,
+ parameters heim_any OPTIONAL
+ } -- AlgorithmIdentifier -- OPTIONAL,
+ issuer [3] IMPLICIT CHOICE {
+ rdnSequence CRMFRDNSequence
+ } -- Name -- OPTIONAL,
+ validity [4] SEQUENCE {
+ notBefore [0] Time OPTIONAL,
+ notAfter [1] Time OPTIONAL
+ } -- OptionalValidity -- OPTIONAL,
+ subject [5] IMPLICIT CHOICE {
+ rdnSequence CRMFRDNSequence
+ } -- Name -- OPTIONAL,
+ publicKey [6] IMPLICIT SEQUENCE {
+ algorithm AlgorithmIdentifier,
+ subjectPublicKey BIT STRING OPTIONAL
+ } -- SubjectPublicKeyInfo -- OPTIONAL,
+ issuerUID [7] IMPLICIT BIT STRING OPTIONAL,
+ subjectUID [8] IMPLICIT BIT STRING OPTIONAL,
+ extensions [9] IMPLICIT SEQUENCE OF Extension OPTIONAL
+}
+
+CertRequest ::= SEQUENCE {
+ certReqId INTEGER,
+ certTemplate CertTemplate,
+ controls Controls OPTIONAL
+}
+
+CertReqMsg ::= SEQUENCE {
+ certReq CertRequest,
+ popo ProofOfPossession OPTIONAL,
+ regInfo SEQUENCE OF AttributeTypeAndValue OPTIONAL }
+
+CertReqMessages ::= SEQUENCE OF CertReqMsg
+
+
+END
+
diff --git a/source4/heimdal/lib/hx509/crypto.c b/source4/heimdal/lib/hx509/crypto.c
new file mode 100644
index 0000000000..dac0a8160b
--- /dev/null
+++ b/source4/heimdal/lib/hx509/crypto.c
@@ -0,0 +1,2438 @@
+/*
+ * 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: crypto.c,v 1.63 2007/01/09 10:52:05 lha Exp $");
+
+struct hx509_crypto;
+
+struct signature_alg;
+
+enum crypto_op_type {
+ COT_SIGN
+};
+
+
+struct hx509_private_key_ops {
+ const char *pemtype;
+ const heim_oid *(*key_oid)(void);
+ int (*get_spki)(hx509_context,
+ const hx509_private_key,
+ SubjectPublicKeyInfo *);
+ int (*export)(hx509_context context,
+ const hx509_private_key,
+ heim_octet_string *);
+ int (*import)(hx509_context,
+ const void *data,
+ size_t len,
+ hx509_private_key private_key);
+ int (*generate_private_key)(hx509_context context,
+ hx509_private_key private_key);
+ int (*handle_alg)(const hx509_private_key,
+ const AlgorithmIdentifier *,
+ enum crypto_op_type);
+ int (*sign)(hx509_context context,
+ const hx509_private_key,
+ const AlgorithmIdentifier *,
+ const heim_octet_string *,
+ AlgorithmIdentifier *,
+ heim_octet_string *);
+#if 0
+ const AlgorithmIdentifier *(*preferred_sig_alg)
+ (const hx509_private_key,
+ const hx509_peer_info);
+ int (*unwrap)(hx509_context context,
+ const hx509_private_key,
+ const AlgorithmIdentifier *,
+ const heim_octet_string *,
+ heim_octet_string *);
+#endif
+};
+
+struct hx509_private_key {
+ unsigned int ref;
+ const struct signature_alg *md;
+ const heim_oid *signature_alg;
+ union {
+ RSA *rsa;
+ void *keydata;
+ } private_key;
+ /* new crypto layer */
+ hx509_private_key_ops *ops;
+};
+
+/*
+ *
+ */
+
+struct signature_alg {
+ char *name;
+ const heim_oid *(*sig_oid)(void);
+ const AlgorithmIdentifier *(*sig_alg)(void);
+ const heim_oid *(*key_oid)(void);
+ const heim_oid *(*digest_oid)(void);
+ int flags;
+#define PROVIDE_CONF 1
+#define REQUIRE_SIGNER 2
+
+#define SIG_DIGEST 0x100
+#define SIG_PUBLIC_SIG 0x200
+#define SIG_PUBLIC_ENC 0x400
+#define SIG_SECRET 0x800
+
+ int (*verify_signature)(hx509_context context,
+ const struct signature_alg *,
+ const Certificate *,
+ const AlgorithmIdentifier *,
+ const heim_octet_string *,
+ const heim_octet_string *);
+ int (*create_signature)(hx509_context,
+ const struct signature_alg *,
+ const hx509_private_key,
+ const AlgorithmIdentifier *,
+ const heim_octet_string *,
+ AlgorithmIdentifier *,
+ heim_octet_string *);
+ int (*private_key2SPKI)(hx509_context,
+ hx509_private_key,
+ SubjectPublicKeyInfo *);
+};
+
+/*
+ *
+ */
+
+static BIGNUM *
+heim_int2BN(const heim_integer *i)
+{
+ BIGNUM *bn;
+
+ bn = BN_bin2bn(i->data, i->length, NULL);
+ BN_set_negative(bn, i->negative);
+ return bn;
+}
+
+static int
+rsa_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ const SubjectPublicKeyInfo *spi;
+ DigestInfo di;
+ unsigned char *to;
+ int tosize, retsize;
+ int ret;
+ RSA *rsa;
+ RSAPublicKey pk;
+ size_t size;
+
+ memset(&di, 0, sizeof(di));
+
+ spi = &signer->tbsCertificate.subjectPublicKeyInfo;
+
+ rsa = RSA_new();
+ if (rsa == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ ret = decode_RSAPublicKey(spi->subjectPublicKey.data,
+ spi->subjectPublicKey.length / 8,
+ &pk, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to decode RSAPublicKey");
+ goto out;
+ }
+
+ rsa->n = heim_int2BN(&pk.modulus);
+ rsa->e = heim_int2BN(&pk.publicExponent);
+
+ free_RSAPublicKey(&pk);
+
+ if (rsa->n == NULL || rsa->e == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ tosize = RSA_size(rsa);
+ to = malloc(tosize);
+ if (to == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ retsize = RSA_public_decrypt(sig->length, (unsigned char *)sig->data,
+ to, rsa, RSA_PKCS1_PADDING);
+ if (retsize <= 0) {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret,
+ "RSA public decrypt failed: %d", retsize);
+ free(to);
+ goto out;
+ }
+ if (retsize > tosize)
+ _hx509_abort("internal rsa decryption failure: ret > tosize");
+ ret = decode_DigestInfo(to, retsize, &di, &size);
+ free(to);
+ if (ret) {
+ goto out;
+ }
+
+ /* Check for extra data inside the sigature */
+ if (size != retsize) {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "size from decryption mismatch");
+ goto out;
+ }
+
+ if (sig_alg->digest_oid &&
+ der_heim_oid_cmp(&di.digestAlgorithm.algorithm,
+ (*sig_alg->digest_oid)()) != 0)
+ {
+ ret = HX509_CRYPTO_OID_MISMATCH;
+ hx509_set_error_string(context, 0, ret, "object identifier in RSA sig mismatch");
+ goto out;
+ }
+
+ /* verify that the parameters are NULL or the NULL-type */
+ if (di.digestAlgorithm.parameters != NULL &&
+ (di.digestAlgorithm.parameters->length != 2 ||
+ memcmp(di.digestAlgorithm.parameters->data, "\x05\x00", 2) != 0))
+ {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "Extra parameters inside RSA signature");
+ goto out;
+ }
+
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &di.digestAlgorithm,
+ data,
+ &di.digest);
+ out:
+ free_DigestInfo(&di);
+ RSA_free(rsa);
+ return ret;
+}
+
+static int
+rsa_create_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ const AlgorithmIdentifier *digest_alg;
+ heim_octet_string indata;
+ const heim_oid *sig_oid;
+ DigestInfo di;
+ size_t size;
+ int ret;
+
+ if (alg)
+ sig_oid = &alg->algorithm;
+ else
+ sig_oid = signer->signature_alg;
+
+ if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_sha256WithRSAEncryption()) == 0) {
+ digest_alg = hx509_signature_sha256();
+ } else if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_sha1WithRSAEncryption()) == 0) {
+ digest_alg = hx509_signature_sha1();
+ } else if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_md5WithRSAEncryption()) == 0) {
+ digest_alg = hx509_signature_md5();
+ } else if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_md5WithRSAEncryption()) == 0) {
+ digest_alg = hx509_signature_md5();
+ } else if (der_heim_oid_cmp(sig_oid, oid_id_dsa_with_sha1()) == 0) {
+ digest_alg = hx509_signature_sha1();
+ } else
+ return HX509_ALG_NOT_SUPP;
+
+ if (signatureAlgorithm) {
+ ret = _hx509_set_digest_alg(signatureAlgorithm,
+ sig_oid, "\x05\x00", 2);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ }
+
+ memset(&di, 0, sizeof(di));
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ digest_alg,
+ data,
+ &di.digestAlgorithm,
+ &di.digest);
+ if (ret)
+ return ret;
+ ASN1_MALLOC_ENCODE(DigestInfo,
+ indata.data,
+ indata.length,
+ &di,
+ &size,
+ ret);
+ free_DigestInfo(&di);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+ if (indata.length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ sig->length = RSA_size(signer->private_key.rsa);
+ sig->data = malloc(sig->length);
+ if (sig->data == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = RSA_private_encrypt(indata.length, indata.data,
+ sig->data,
+ signer->private_key.rsa,
+ RSA_PKCS1_PADDING);
+ der_free_octet_string(&indata);
+ if (ret <= 0) {
+ ret = HX509_CMS_FAILED_CREATE_SIGATURE;
+ hx509_set_error_string(context, 0, ret,
+ "RSA private decrypt failed: %d", ret);
+ return ret;
+ }
+ if (ret > sig->length)
+ _hx509_abort("RSA signature prelen longer the output len");
+
+ sig->length = ret;
+
+ return 0;
+}
+
+static int
+rsa_private_key_import(hx509_context context,
+ const void *data,
+ size_t len,
+ hx509_private_key private_key)
+{
+ const unsigned char *p = data;
+
+ private_key->private_key.rsa =
+ d2i_RSAPrivateKey(NULL, &p, len);
+ if (private_key->private_key.rsa == NULL) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to parse RSA key");
+ return HX509_PARSING_KEY_FAILED;
+ }
+ private_key->signature_alg = oid_id_pkcs1_sha1WithRSAEncryption();
+
+ return 0;
+}
+
+static int
+rsa_private_key2SPKI(hx509_context context,
+ hx509_private_key private_key,
+ SubjectPublicKeyInfo *spki)
+{
+ int len, ret;
+
+ memset(spki, 0, sizeof(*spki));
+
+ len = i2d_RSAPublicKey(private_key->private_key.rsa, NULL);
+
+ spki->subjectPublicKey.data = malloc(len);
+ if (spki->subjectPublicKey.data == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "malloc - out of memory");
+ return ENOMEM;
+ }
+ spki->subjectPublicKey.length = len * 8;
+
+ ret = _hx509_set_digest_alg(&spki->algorithm,
+ oid_id_pkcs1_rsaEncryption(),
+ "\x05\x00", 2);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "malloc - out of memory");
+ free(spki->subjectPublicKey.data);
+ spki->subjectPublicKey.data = NULL;
+ spki->subjectPublicKey.length = 0;
+ return ret;
+ }
+
+ {
+ unsigned char *pp = spki->subjectPublicKey.data;
+ i2d_RSAPublicKey(private_key->private_key.rsa, &pp);
+ }
+
+ return 0;
+}
+
+static int
+cb_func(int a, int b, BN_GENCB *c)
+{
+ return 1;
+}
+
+static int
+rsa_generate_private_key(hx509_context context, hx509_private_key private_key)
+{
+ BN_GENCB cb;
+ BIGNUM *e;
+ int ret;
+
+ static const int default_rsa_e = 65537;
+ static const int default_rsa_bits = 1024;
+
+ private_key->private_key.rsa = RSA_new();
+ if (private_key->private_key.rsa == NULL) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to generate RSA key");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ e = BN_new();
+ BN_set_word(e, default_rsa_e);
+
+ BN_GENCB_set(&cb, cb_func, NULL);
+ ret = RSA_generate_key_ex(private_key->private_key.rsa,
+ default_rsa_bits, e, &cb);
+ BN_free(e);
+ if (ret != 1) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to generate RSA key");
+ return HX509_PARSING_KEY_FAILED;
+ }
+ private_key->signature_alg = oid_id_pkcs1_sha1WithRSAEncryption();
+
+ return 0;
+}
+
+static int
+rsa_private_key_export(hx509_context context,
+ const hx509_private_key key,
+ heim_octet_string *data)
+{
+ int ret;
+
+ data->data = NULL;
+ data->length = 0;
+
+ ret = i2d_RSAPrivateKey(key->private_key.rsa, NULL);
+ if (ret <= 0) {
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret,
+ "Private key is not exportable");
+ return ret;
+ }
+
+ data->data = malloc(ret);
+ if (data->data == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "malloc out of memory");
+ return ret;
+ }
+ data->length = ret;
+
+ {
+ unsigned char *p = data->data;
+ i2d_RSAPrivateKey(key->private_key.rsa, &p);
+ }
+
+ return 0;
+}
+
+
+static hx509_private_key_ops rsa_private_key_ops = {
+ "RSA PRIVATE KEY",
+ oid_id_pkcs1_rsaEncryption,
+ rsa_private_key2SPKI,
+ rsa_private_key_export,
+ rsa_private_key_import,
+ rsa_generate_private_key
+};
+
+
+/*
+ *
+ */
+
+static int
+dsa_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ const SubjectPublicKeyInfo *spi;
+ DSAPublicKey pk;
+ DSAParams param;
+ size_t size;
+ DSA *dsa;
+ int ret;
+
+ spi = &signer->tbsCertificate.subjectPublicKeyInfo;
+
+ dsa = DSA_new();
+ if (dsa == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = decode_DSAPublicKey(spi->subjectPublicKey.data,
+ spi->subjectPublicKey.length / 8,
+ &pk, &size);
+ if (ret)
+ goto out;
+
+ dsa->pub_key = heim_int2BN(&pk);
+
+ free_DSAPublicKey(&pk);
+
+ if (dsa->pub_key == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ if (spi->algorithm.parameters == NULL) {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "DSA parameters missing");
+ goto out;
+ }
+
+ ret = decode_DSAParams(spi->algorithm.parameters->data,
+ spi->algorithm.parameters->length,
+ &param,
+ &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "DSA parameters failed to decode");
+ goto out;
+ }
+
+ dsa->p = heim_int2BN(&param.p);
+ dsa->q = heim_int2BN(&param.q);
+ dsa->g = heim_int2BN(&param.g);
+
+ free_DSAParams(&param);
+
+ if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ ret = DSA_verify(-1, data->data, data->length,
+ (unsigned char*)sig->data, sig->length,
+ dsa);
+ if (ret == 1)
+ ret = 0;
+ else if (ret == 0 || ret == -1) {
+ ret = HX509_CRYPTO_BAD_SIGNATURE;
+ hx509_set_error_string(context, 0, ret, "BAD DSA sigature");
+ } else {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "Invalid format of DSA sigature");
+ }
+
+ out:
+ DSA_free(dsa);
+
+ return ret;
+}
+
+#if 0
+static int
+dsa_parse_private_key(hx509_context context,
+ const void *data,
+ size_t len,
+ hx509_private_key private_key)
+{
+ const unsigned char *p = data;
+
+ private_key->private_key.dsa =
+ d2i_DSAPrivateKey(NULL, &p, len);
+ if (private_key->private_key.dsa == NULL)
+ return EINVAL;
+ private_key->signature_alg = oid_id_dsa_with_sha1();
+
+ return 0;
+/* else */
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "No support to parse DSA keys");
+ return HX509_PARSING_KEY_FAILED;
+}
+#endif
+
+
+static int
+sha1_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ SHA_CTX m;
+
+ if (sig->length != SHA_DIGEST_LENGTH) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT,
+ "SHA1 sigature have wrong length");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ SHA1_Init(&m);
+ SHA1_Update(&m, data->data, data->length);
+ SHA1_Final (digest, &m);
+
+ if (memcmp(digest, sig->data, SHA_DIGEST_LENGTH) != 0) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE,
+ "Bad SHA1 sigature");
+ return HX509_CRYPTO_BAD_SIGNATURE;
+ }
+
+ return 0;
+}
+
+static int
+sha256_create_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ SHA256_CTX m;
+
+ memset(sig, 0, sizeof(*sig));
+
+ if (signatureAlgorithm) {
+ int ret;
+ ret = _hx509_set_digest_alg(signatureAlgorithm,
+ (*sig_alg->sig_oid)(), "\x05\x00", 2);
+ if (ret)
+ return ret;
+ }
+
+
+ sig->data = malloc(SHA256_DIGEST_LENGTH);
+ if (sig->data == NULL) {
+ sig->length = 0;
+ return ENOMEM;
+ }
+ sig->length = SHA256_DIGEST_LENGTH;
+
+ SHA256_Init(&m);
+ SHA256_Update(&m, data->data, data->length);
+ SHA256_Final (sig->data, &m);
+
+ return 0;
+}
+
+static int
+sha256_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+ SHA256_CTX m;
+
+ if (sig->length != SHA256_DIGEST_LENGTH) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT,
+ "SHA256 sigature have wrong length");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ SHA256_Init(&m);
+ SHA256_Update(&m, data->data, data->length);
+ SHA256_Final (digest, &m);
+
+ if (memcmp(digest, sig->data, SHA256_DIGEST_LENGTH) != 0) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE,
+ "Bad SHA256 sigature");
+ return HX509_CRYPTO_BAD_SIGNATURE;
+ }
+
+ return 0;
+}
+
+static int
+sha1_create_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ SHA_CTX m;
+
+ memset(sig, 0, sizeof(*sig));
+
+ if (signatureAlgorithm) {
+ int ret;
+ ret = _hx509_set_digest_alg(signatureAlgorithm,
+ (*sig_alg->sig_oid)(), "\x05\x00", 2);
+ if (ret)
+ return ret;
+ }
+
+
+ sig->data = malloc(SHA_DIGEST_LENGTH);
+ if (sig->data == NULL) {
+ sig->length = 0;
+ return ENOMEM;
+ }
+ sig->length = SHA_DIGEST_LENGTH;
+
+ SHA1_Init(&m);
+ SHA1_Update(&m, data->data, data->length);
+ SHA1_Final (sig->data, &m);
+
+ return 0;
+}
+
+static int
+md5_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ unsigned char digest[MD5_DIGEST_LENGTH];
+ MD5_CTX m;
+
+ if (sig->length != MD5_DIGEST_LENGTH) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT,
+ "MD5 sigature have wrong length");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ MD5_Init(&m);
+ MD5_Update(&m, data->data, data->length);
+ MD5_Final (digest, &m);
+
+ if (memcmp(digest, sig->data, MD5_DIGEST_LENGTH) != 0) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE,
+ "Bad MD5 sigature");
+ return HX509_CRYPTO_BAD_SIGNATURE;
+ }
+
+ return 0;
+}
+
+static int
+md2_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ unsigned char digest[MD2_DIGEST_LENGTH];
+ MD2_CTX m;
+
+ if (sig->length != MD2_DIGEST_LENGTH) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT,
+ "MD2 sigature have wrong length");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ MD2_Init(&m);
+ MD2_Update(&m, data->data, data->length);
+ MD2_Final (digest, &m);
+
+ if (memcmp(digest, sig->data, MD2_DIGEST_LENGTH) != 0) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE,
+ "Bad MD2 sigature");
+ return HX509_CRYPTO_BAD_SIGNATURE;
+ }
+
+ return 0;
+}
+
+static struct signature_alg pkcs1_rsa_sha1_alg = {
+ "rsa",
+ oid_id_pkcs1_rsaEncryption,
+ hx509_signature_rsa_with_sha1,
+ oid_id_pkcs1_rsaEncryption,
+ NULL,
+ PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG,
+ rsa_verify_signature,
+ rsa_create_signature,
+ rsa_private_key2SPKI
+};
+
+static struct signature_alg rsa_with_sha256_alg = {
+ "rsa-with-sha256",
+ oid_id_pkcs1_sha256WithRSAEncryption,
+ hx509_signature_rsa_with_sha256,
+ oid_id_pkcs1_rsaEncryption,
+ oid_id_sha256,
+ PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG,
+ rsa_verify_signature,
+ rsa_create_signature,
+ rsa_private_key2SPKI
+};
+
+static struct signature_alg rsa_with_sha1_alg = {
+ "rsa-with-sha1",
+ oid_id_pkcs1_sha1WithRSAEncryption,
+ hx509_signature_rsa_with_sha1,
+ oid_id_pkcs1_rsaEncryption,
+ oid_id_secsig_sha_1,
+ PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG,
+ rsa_verify_signature,
+ rsa_create_signature,
+ rsa_private_key2SPKI
+};
+
+static struct signature_alg rsa_with_md5_alg = {
+ "rsa-with-md5",
+ oid_id_pkcs1_md5WithRSAEncryption,
+ hx509_signature_rsa_with_md5,
+ oid_id_pkcs1_rsaEncryption,
+ oid_id_rsa_digest_md5,
+ PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG,
+ rsa_verify_signature,
+ rsa_create_signature,
+ rsa_private_key2SPKI
+};
+
+static struct signature_alg rsa_with_md2_alg = {
+ "rsa-with-md2",
+ oid_id_pkcs1_md2WithRSAEncryption,
+ hx509_signature_rsa_with_md2,
+ oid_id_pkcs1_rsaEncryption,
+ oid_id_rsa_digest_md2,
+ PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG,
+ rsa_verify_signature,
+ rsa_create_signature,
+ rsa_private_key2SPKI
+};
+
+static struct signature_alg dsa_sha1_alg = {
+ "dsa-with-sha1",
+ oid_id_dsa_with_sha1,
+ NULL,
+ oid_id_dsa,
+ oid_id_secsig_sha_1,
+ PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG,
+ dsa_verify_signature,
+ /* create_signature */ NULL,
+};
+
+static struct signature_alg sha256_alg = {
+ "sha-256",
+ oid_id_sha256,
+ hx509_signature_sha256,
+ NULL,
+ NULL,
+ SIG_DIGEST,
+ sha256_verify_signature,
+ sha256_create_signature
+};
+
+static struct signature_alg sha1_alg = {
+ "sha1",
+ oid_id_secsig_sha_1,
+ hx509_signature_sha1,
+ NULL,
+ NULL,
+ SIG_DIGEST,
+ sha1_verify_signature,
+ sha1_create_signature
+};
+
+static struct signature_alg md5_alg = {
+ "rsa-md5",
+ oid_id_rsa_digest_md5,
+ hx509_signature_md5,
+ NULL,
+ NULL,
+ SIG_DIGEST,
+ md5_verify_signature
+};
+
+static struct signature_alg md2_alg = {
+ "rsa-md2",
+ oid_id_rsa_digest_md2,
+ hx509_signature_md2,
+ NULL,
+ NULL,
+ SIG_DIGEST,
+ md2_verify_signature
+};
+
+/*
+ * Order matter in this structure, "best" first for each "key
+ * compatible" type (type is RSA, DSA, none, etc)
+ */
+
+static struct signature_alg *sig_algs[] = {
+ &rsa_with_sha256_alg,
+ &rsa_with_sha1_alg,
+ &pkcs1_rsa_sha1_alg,
+ &rsa_with_md5_alg,
+ &rsa_with_md2_alg,
+ &dsa_sha1_alg,
+ &sha256_alg,
+ &sha1_alg,
+ &md5_alg,
+ &md2_alg,
+ NULL
+};
+
+static const struct signature_alg *
+find_sig_alg(const heim_oid *oid)
+{
+ int i;
+ for (i = 0; sig_algs[i]; i++)
+ if (der_heim_oid_cmp((*sig_algs[i]->sig_oid)(), oid) == 0)
+ return sig_algs[i];
+ return NULL;
+}
+
+/*
+ *
+ */
+
+static struct hx509_private_key_ops *private_algs[] = {
+ &rsa_private_key_ops,
+ NULL
+};
+
+static hx509_private_key_ops *
+find_private_alg(const heim_oid *oid)
+{
+ int i;
+ for (i = 0; private_algs[i]; i++) {
+ if (private_algs[i]->key_oid == NULL)
+ continue;
+ if (der_heim_oid_cmp((*private_algs[i]->key_oid)(), oid) == 0)
+ return private_algs[i];
+ }
+ return NULL;
+}
+
+
+int
+_hx509_verify_signature(hx509_context context,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ const struct signature_alg *md;
+
+ md = find_sig_alg(&alg->algorithm);
+ if (md == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+ if (signer && (md->flags & PROVIDE_CONF) == 0) {
+ hx509_clear_error_string(context);
+ return HX509_CRYPTO_SIG_NO_CONF;
+ }
+ if (signer == NULL && (md->flags & REQUIRE_SIGNER)) {
+ hx509_clear_error_string(context);
+ return HX509_CRYPTO_SIGNATURE_WITHOUT_SIGNER;
+ }
+ if (md->key_oid && signer) {
+ const SubjectPublicKeyInfo *spi;
+ spi = &signer->tbsCertificate.subjectPublicKeyInfo;
+
+ if (der_heim_oid_cmp(&spi->algorithm.algorithm, (*md->key_oid)()) != 0) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_DONT_MATCH_KEY_ALG;
+ }
+ }
+ return (*md->verify_signature)(context, md, signer, alg, data, sig);
+}
+
+int
+_hx509_verify_signature_bitstring(hx509_context context,
+ const Certificate *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);
+}
+
+int
+_hx509_create_signature(hx509_context context,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ const struct signature_alg *md;
+
+ if (signer && signer->ops && signer->ops->handle_alg &&
+ (*signer->ops->handle_alg)(signer, alg, COT_SIGN))
+ {
+ return (*signer->ops->sign)(context, signer, alg, data,
+ signatureAlgorithm, sig);
+ }
+
+ md = find_sig_alg(&alg->algorithm);
+ if (md == NULL) {
+ hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED,
+ "algorithm no supported");
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+
+ if (signer && (md->flags & PROVIDE_CONF) == 0) {
+ hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED,
+ "algorithm provides no conf");
+ return HX509_CRYPTO_SIG_NO_CONF;
+ }
+
+ return (*md->create_signature)(context, md, signer, alg, data,
+ signatureAlgorithm, sig);
+}
+
+int
+_hx509_create_signature_bitstring(hx509_context context,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_bit_string *sig)
+{
+ heim_octet_string os;
+ int ret;
+
+ ret = _hx509_create_signature(context, signer, alg,
+ data, signatureAlgorithm, &os);
+ if (ret)
+ return ret;
+ sig->data = os.data;
+ sig->length = os.length * 8;
+ return 0;
+}
+
+int
+_hx509_public_encrypt(hx509_context context,
+ const heim_octet_string *cleartext,
+ const Certificate *cert,
+ heim_oid *encryption_oid,
+ heim_octet_string *ciphertext)
+{
+ const SubjectPublicKeyInfo *spi;
+ unsigned char *to;
+ int tosize;
+ int ret;
+ RSA *rsa;
+ RSAPublicKey pk;
+ size_t size;
+
+ ciphertext->data = NULL;
+ ciphertext->length = 0;
+
+ spi = &cert->tbsCertificate.subjectPublicKeyInfo;
+
+ rsa = RSA_new();
+ if (rsa == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = decode_RSAPublicKey(spi->subjectPublicKey.data,
+ spi->subjectPublicKey.length / 8,
+ &pk, &size);
+ if (ret) {
+ RSA_free(rsa);
+ hx509_set_error_string(context, 0, ret, "RSAPublicKey decode failure");
+ return ret;
+ }
+ rsa->n = heim_int2BN(&pk.modulus);
+ rsa->e = heim_int2BN(&pk.publicExponent);
+
+ free_RSAPublicKey(&pk);
+
+ if (rsa->n == NULL || rsa->e == NULL) {
+ RSA_free(rsa);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ tosize = RSA_size(rsa);
+ to = malloc(tosize);
+ if (to == NULL) {
+ RSA_free(rsa);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = RSA_public_encrypt(cleartext->length,
+ (unsigned char *)cleartext->data,
+ to, rsa, RSA_PKCS1_PADDING);
+ RSA_free(rsa);
+ if (ret <= 0) {
+ free(to);
+ hx509_set_error_string(context, 0, HX509_CRYPTO_RSA_PUBLIC_ENCRYPT,
+ "RSA public encrypt failed with %d", ret);
+ return HX509_CRYPTO_RSA_PUBLIC_ENCRYPT;
+ }
+ if (ret > tosize)
+ _hx509_abort("internal rsa decryption failure: ret > tosize");
+
+ ciphertext->length = ret;
+ ciphertext->data = to;
+
+ ret = der_copy_oid(oid_id_pkcs1_rsaEncryption(), encryption_oid);
+ if (ret) {
+ der_free_octet_string(ciphertext);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+int
+_hx509_private_key_private_decrypt(hx509_context context,
+ const heim_octet_string *ciphertext,
+ const heim_oid *encryption_oid,
+ hx509_private_key p,
+ heim_octet_string *cleartext)
+{
+ int ret;
+
+ cleartext->data = NULL;
+ cleartext->length = 0;
+
+ if (p->private_key.rsa == NULL) {
+ hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
+ "Private RSA key missing");
+ return HX509_PRIVATE_KEY_MISSING;
+ }
+
+ cleartext->length = RSA_size(p->private_key.rsa);
+ cleartext->data = malloc(cleartext->length);
+ if (cleartext->data == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ ret = RSA_private_decrypt(ciphertext->length, ciphertext->data,
+ cleartext->data,
+ p->private_key.rsa,
+ RSA_PKCS1_PADDING);
+ if (ret <= 0) {
+ der_free_octet_string(cleartext);
+ hx509_set_error_string(context, 0, HX509_CRYPTO_RSA_PRIVATE_DECRYPT,
+ "Failed to decrypt using private key: %d", ret);
+ return HX509_CRYPTO_RSA_PRIVATE_DECRYPT;
+ }
+ if (cleartext->length < ret)
+ _hx509_abort("internal rsa decryption failure: ret > tosize");
+
+ cleartext->length = ret;
+
+ return 0;
+}
+
+
+int
+_hx509_parse_private_key(hx509_context context,
+ const heim_oid *key_oid,
+ const void *data,
+ size_t len,
+ hx509_private_key *private_key)
+{
+ struct hx509_private_key_ops *ops;
+ int ret;
+
+ *private_key = NULL;
+
+ ops = find_private_alg(key_oid);
+ if (ops == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+
+ ret = _hx509_private_key_init(private_key, ops, NULL);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+
+ ret = (*ops->import)(context, data, len, *private_key);
+ if (ret)
+ _hx509_private_key_free(private_key);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+int
+_hx509_private_key2SPKI(hx509_context context,
+ hx509_private_key private_key,
+ SubjectPublicKeyInfo *spki)
+{
+ const struct hx509_private_key_ops *ops = private_key->ops;
+ if (ops == NULL || ops->get_spki == NULL) {
+ hx509_set_error_string(context, 0, HX509_UNIMPLEMENTED_OPERATION,
+ "Private key have no key2SPKI function");
+ return HX509_UNIMPLEMENTED_OPERATION;
+ }
+ return (*ops->get_spki)(context, private_key, spki);
+}
+
+int
+_hx509_generate_private_key(hx509_context context,
+ const heim_oid *key_oid,
+ hx509_private_key *private_key)
+{
+ struct hx509_private_key_ops *ops;
+ int ret;
+
+ *private_key = NULL;
+
+ ops = find_private_alg(key_oid);
+ if (ops == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+
+ ret = _hx509_private_key_init(private_key, ops, NULL);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+
+ ret = (*ops->generate_private_key)(context, *private_key);
+ if (ret)
+ _hx509_private_key_free(private_key);
+
+ return ret;
+}
+
+
+/*
+ *
+ */
+
+static const heim_octet_string null_entry_oid = { 2, "\x05\x00" };
+
+static const unsigned sha512_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 3 };
+const AlgorithmIdentifier _hx509_signature_sha512_data = {
+ { 8, rk_UNCONST(sha512_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned sha384_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2 };
+const AlgorithmIdentifier _hx509_signature_sha384_data = {
+ { 8, rk_UNCONST(sha384_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned sha256_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 1 };
+const AlgorithmIdentifier _hx509_signature_sha256_data = {
+ { 8, rk_UNCONST(sha256_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned sha1_oid_tree[] = { 1, 3, 14, 3, 2, 26 };
+const AlgorithmIdentifier _hx509_signature_sha1_data = {
+ { 6, rk_UNCONST(sha1_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned md5_oid_tree[] = { 1, 2, 840, 113549, 2, 5 };
+const AlgorithmIdentifier _hx509_signature_md5_data = {
+ { 6, rk_UNCONST(md5_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned md2_oid_tree[] = { 1, 2, 840, 113549, 2, 2 };
+const AlgorithmIdentifier _hx509_signature_md2_data = {
+ { 6, rk_UNCONST(md2_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned rsa_with_sha512_oid[] ={ 1, 2, 840, 113549, 1, 1, 13 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_sha512_data = {
+ { 7, rk_UNCONST(rsa_with_sha512_oid) }, NULL
+};
+
+static const unsigned rsa_with_sha384_oid[] ={ 1, 2, 840, 113549, 1, 1, 12 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_sha384_data = {
+ { 7, rk_UNCONST(rsa_with_sha384_oid) }, NULL
+};
+
+static const unsigned rsa_with_sha256_oid[] ={ 1, 2, 840, 113549, 1, 1, 11 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_sha256_data = {
+ { 7, rk_UNCONST(rsa_with_sha256_oid) }, NULL
+};
+
+static const unsigned rsa_with_sha1_oid[] ={ 1, 2, 840, 113549, 1, 1, 5 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_sha1_data = {
+ { 7, rk_UNCONST(rsa_with_sha1_oid) }, NULL
+};
+
+static const unsigned rsa_with_md5_oid[] ={ 1, 2, 840, 113549, 1, 1, 4 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_md5_data = {
+ { 7, rk_UNCONST(rsa_with_md5_oid) }, NULL
+};
+
+static const unsigned rsa_with_md2_oid[] ={ 1, 2, 840, 113549, 1, 1, 2 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_md2_data = {
+ { 7, rk_UNCONST(rsa_with_md2_oid) }, NULL
+};
+
+static const unsigned rsa_oid[] ={ 1, 2, 840, 113549, 1, 1, 1 };
+const AlgorithmIdentifier _hx509_signature_rsa_data = {
+ { 7, rk_UNCONST(rsa_oid) }, NULL
+};
+
+
+const AlgorithmIdentifier *
+hx509_signature_sha512(void)
+{ return &_hx509_signature_sha512_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_sha384(void)
+{ return &_hx509_signature_sha384_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_sha256(void)
+{ return &_hx509_signature_sha256_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_sha1(void)
+{ return &_hx509_signature_sha1_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_md5(void)
+{ return &_hx509_signature_md5_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_md2(void)
+{ return &_hx509_signature_md2_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha512(void)
+{ return &_hx509_signature_rsa_with_sha512_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha384(void)
+{ return &_hx509_signature_rsa_with_sha384_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha256(void)
+{ return &_hx509_signature_rsa_with_sha256_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha1(void)
+{ return &_hx509_signature_rsa_with_sha1_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_md5(void)
+{ return &_hx509_signature_rsa_with_md5_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_md2(void)
+{ return &_hx509_signature_rsa_with_md2_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa(void)
+{ return &_hx509_signature_rsa_data; }
+
+int
+_hx509_private_key_init(hx509_private_key *key,
+ hx509_private_key_ops *ops,
+ void *keydata)
+{
+ *key = calloc(1, sizeof(**key));
+ if (*key == NULL)
+ return ENOMEM;
+ (*key)->ref = 1;
+ (*key)->ops = ops;
+ (*key)->private_key.keydata = keydata;
+ return 0;
+}
+
+hx509_private_key
+_hx509_private_key_ref(hx509_private_key key)
+{
+ if (key->ref <= 0)
+ _hx509_abort("refcount <= 0");
+ key->ref++;
+ if (key->ref == 0)
+ _hx509_abort("refcount == 0");
+ return key;
+}
+
+const char *
+_hx509_private_pem_name(hx509_private_key key)
+{
+ return key->ops->pemtype;
+}
+
+int
+_hx509_private_key_free(hx509_private_key *key)
+{
+ if (key == NULL || *key == NULL)
+ return 0;
+
+ if ((*key)->ref <= 0)
+ _hx509_abort("refcount <= 0");
+ if (--(*key)->ref > 0)
+ return 0;
+
+ if ((*key)->private_key.rsa)
+ RSA_free((*key)->private_key.rsa);
+ (*key)->private_key.rsa = NULL;
+ free(*key);
+ *key = NULL;
+ return 0;
+}
+
+void
+_hx509_private_key_assign_rsa(hx509_private_key key, void *ptr)
+{
+ if (key->private_key.rsa)
+ RSA_free(key->private_key.rsa);
+ key->private_key.rsa = ptr;
+ key->signature_alg = oid_id_pkcs1_sha1WithRSAEncryption();
+ key->md = &pkcs1_rsa_sha1_alg;
+}
+
+int
+_hx509_private_key_oid(hx509_context context,
+ const hx509_private_key key,
+ heim_oid *data)
+{
+ int ret;
+ ret = der_copy_oid((*key->ops->key_oid)(), data);
+ if (ret)
+ hx509_set_error_string(context, 0, ret, "malloc out of memory");
+ return ret;
+}
+
+int
+_hx509_private_key_exportable(hx509_private_key key)
+{
+ if (key->ops->export == NULL)
+ return 0;
+ return 1;
+}
+
+int
+_hx509_private_key_export(hx509_context context,
+ const hx509_private_key key,
+ heim_octet_string *data)
+{
+ if (key->ops->export == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_UNIMPLEMENTED_OPERATION;
+ }
+ return (*key->ops->export)(context, key, data);
+}
+
+/*
+ *
+ */
+
+struct hx509cipher {
+ const char *name;
+ const heim_oid *(*oid_func)(void);
+ const EVP_CIPHER *(*evp_func)(void);
+ int (*get_params)(hx509_context, const hx509_crypto,
+ const heim_octet_string *, heim_octet_string *);
+ int (*set_params)(hx509_context, const heim_octet_string *,
+ hx509_crypto, heim_octet_string *);
+};
+
+struct hx509_crypto_data {
+ char *name;
+ const struct hx509cipher *cipher;
+ const EVP_CIPHER *c;
+ heim_octet_string key;
+ heim_oid oid;
+ void *param;
+};
+
+/*
+ *
+ */
+
+static const heim_oid *
+oid_private_rc2_40(void)
+{
+ static unsigned oid_data[] = { 127, 1 };
+ static const heim_oid oid = { 2, oid_data };
+
+ return &oid;
+}
+
+
+/*
+ *
+ */
+
+static int
+CMSCBCParam_get(hx509_context context, const hx509_crypto crypto,
+ const heim_octet_string *ivec, heim_octet_string *param)
+{
+ size_t size;
+ int ret;
+
+ assert(crypto->param == NULL);
+ if (ivec == NULL)
+ return 0;
+
+ ASN1_MALLOC_ENCODE(CMSCBCParameter, param->data, param->length,
+ ivec, &size, ret);
+ if (ret == 0 && size != param->length)
+ _hx509_abort("Internal asn1 encoder failure");
+ if (ret)
+ hx509_clear_error_string(context);
+ return ret;
+}
+
+static int
+CMSCBCParam_set(hx509_context context, const heim_octet_string *param,
+ hx509_crypto crypto, heim_octet_string *ivec)
+{
+ int ret;
+ if (ivec == NULL)
+ return 0;
+
+ ret = decode_CMSCBCParameter(param->data, param->length, ivec, NULL);
+ if (ret)
+ hx509_clear_error_string(context);
+
+ return ret;
+}
+
+struct _RC2_params {
+ int maximum_effective_key;
+};
+
+static int
+CMSRC2CBCParam_get(hx509_context context, const hx509_crypto crypto,
+ const heim_octet_string *ivec, heim_octet_string *param)
+{
+ CMSRC2CBCParameter rc2params;
+ const struct _RC2_params *p = crypto->param;
+ int maximum_effective_key = 128;
+ size_t size;
+ int ret;
+
+ memset(&rc2params, 0, sizeof(rc2params));
+
+ if (p)
+ maximum_effective_key = p->maximum_effective_key;
+
+ switch(maximum_effective_key) {
+ case 40:
+ rc2params.rc2ParameterVersion = 160;
+ break;
+ case 64:
+ rc2params.rc2ParameterVersion = 120;
+ break;
+ case 128:
+ rc2params.rc2ParameterVersion = 58;
+ break;
+ }
+ rc2params.iv = *ivec;
+
+ ASN1_MALLOC_ENCODE(CMSRC2CBCParameter, param->data, param->length,
+ &rc2params, &size, ret);
+ if (ret == 0 && size != param->length)
+ _hx509_abort("Internal asn1 encoder failure");
+
+ return ret;
+}
+
+static int
+CMSRC2CBCParam_set(hx509_context context, const heim_octet_string *param,
+ hx509_crypto crypto, heim_octet_string *ivec)
+{
+ CMSRC2CBCParameter rc2param;
+ struct _RC2_params *p;
+ size_t size;
+ int ret;
+
+ ret = decode_CMSRC2CBCParameter(param->data, param->length,
+ &rc2param, &size);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ free_CMSRC2CBCParameter(&rc2param);
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ switch(rc2param.rc2ParameterVersion) {
+ case 160:
+ crypto->c = EVP_rc2_40_cbc();
+ p->maximum_effective_key = 40;
+ break;
+ case 120:
+ crypto->c = EVP_rc2_64_cbc();
+ p->maximum_effective_key = 64;
+ break;
+ case 58:
+ crypto->c = EVP_rc2_cbc();
+ p->maximum_effective_key = 128;
+ break;
+ default:
+ free_CMSRC2CBCParameter(&rc2param);
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+ if (ivec)
+ ret = der_copy_octet_string(&rc2param.iv, ivec);
+ free_CMSRC2CBCParameter(&rc2param);
+ if (ret)
+ hx509_clear_error_string(context);
+ else
+ crypto->param = p;
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static const struct hx509cipher ciphers[] = {
+ {
+ "rc2-cbc",
+ oid_id_pkcs3_rc2_cbc,
+ EVP_rc2_cbc,
+ CMSRC2CBCParam_get,
+ CMSRC2CBCParam_set
+ },
+ {
+ "rc2-cbc",
+ oid_id_rsadsi_rc2_cbc,
+ EVP_rc2_cbc,
+ CMSRC2CBCParam_get,
+ CMSRC2CBCParam_set
+ },
+ {
+ "rc2-40-cbc",
+ oid_private_rc2_40,
+ EVP_rc2_40_cbc,
+ CMSRC2CBCParam_get,
+ CMSRC2CBCParam_set
+ },
+ {
+ "des-ede3-cbc",
+ oid_id_pkcs3_des_ede3_cbc,
+ EVP_des_ede3_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ },
+ {
+ "des-ede3-cbc",
+ oid_id_rsadsi_des_ede3_cbc,
+ EVP_des_ede3_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ },
+ {
+ "aes-128-cbc",
+ oid_id_aes_128_cbc,
+ EVP_aes_128_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ },
+ {
+ "aes-192-cbc",
+ oid_id_aes_192_cbc,
+ EVP_aes_192_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ },
+ {
+ "aes-256-cbc",
+ oid_id_aes_256_cbc,
+ EVP_aes_256_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ }
+};
+
+static const struct hx509cipher *
+find_cipher_by_oid(const heim_oid *oid)
+{
+ int i;
+
+ for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++)
+ if (der_heim_oid_cmp(oid, (*ciphers[i].oid_func)()) == 0)
+ return &ciphers[i];
+
+ return NULL;
+}
+
+static const struct hx509cipher *
+find_cipher_by_name(const char *name)
+{
+ int i;
+
+ for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++)
+ if (strcasecmp(name, ciphers[i].name) == 0)
+ return &ciphers[i];
+
+ return NULL;
+}
+
+
+const heim_oid *
+hx509_crypto_enctype_by_name(const char *name)
+{
+ const struct hx509cipher *cipher;
+
+ cipher = find_cipher_by_name(name);
+ if (cipher == NULL)
+ return NULL;
+ return (*cipher->oid_func)();
+}
+
+int
+hx509_crypto_init(hx509_context context,
+ const char *provider,
+ const heim_oid *enctype,
+ hx509_crypto *crypto)
+{
+ const struct hx509cipher *cipher;
+
+ *crypto = NULL;
+
+ cipher = find_cipher_by_oid(enctype);
+ if (cipher == NULL) {
+ hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
+ "Algorithm not supported");
+ return HX509_ALG_NOT_SUPP;
+ }
+
+ *crypto = calloc(1, sizeof(**crypto));
+ if (*crypto == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ (*crypto)->cipher = cipher;
+ (*crypto)->c = (*cipher->evp_func)();
+
+ if (der_copy_oid(enctype, &(*crypto)->oid)) {
+ hx509_crypto_destroy(*crypto);
+ *crypto = NULL;
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+const char *
+hx509_crypto_provider(hx509_crypto crypto)
+{
+ return "unknown";
+}
+
+void
+hx509_crypto_destroy(hx509_crypto crypto)
+{
+ if (crypto->name)
+ free(crypto->name);
+ if (crypto->key.data)
+ free(crypto->key.data);
+ if (crypto->param)
+ free(crypto->param);
+ der_free_oid(&crypto->oid);
+ memset(crypto, 0, sizeof(*crypto));
+ free(crypto);
+}
+
+int
+hx509_crypto_set_key_name(hx509_crypto crypto, const char *name)
+{
+ return 0;
+}
+
+int
+hx509_crypto_set_key_data(hx509_crypto crypto, const void *data, size_t length)
+{
+ if (EVP_CIPHER_key_length(crypto->c) > length)
+ return HX509_CRYPTO_INTERNAL_ERROR;
+
+ if (crypto->key.data) {
+ free(crypto->key.data);
+ crypto->key.data = NULL;
+ crypto->key.length = 0;
+ }
+ crypto->key.data = malloc(length);
+ if (crypto->key.data == NULL)
+ return ENOMEM;
+ memcpy(crypto->key.data, data, length);
+ crypto->key.length = length;
+
+ return 0;
+}
+
+int
+hx509_crypto_set_random_key(hx509_crypto crypto, heim_octet_string *key)
+{
+ if (crypto->key.data) {
+ free(crypto->key.data);
+ crypto->key.length = 0;
+ }
+
+ crypto->key.length = EVP_CIPHER_key_length(crypto->c);
+ crypto->key.data = malloc(crypto->key.length);
+ if (crypto->key.data == NULL) {
+ crypto->key.length = 0;
+ return ENOMEM;
+ }
+ if (RAND_bytes(crypto->key.data, crypto->key.length) <= 0) {
+ free(crypto->key.data);
+ crypto->key.data = NULL;
+ crypto->key.length = 0;
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+ if (key)
+ return der_copy_octet_string(&crypto->key, key);
+ else
+ return 0;
+}
+
+int
+hx509_crypto_set_params(hx509_context context,
+ hx509_crypto crypto,
+ const heim_octet_string *param,
+ heim_octet_string *ivec)
+{
+ return (*crypto->cipher->set_params)(context, param, crypto, ivec);
+}
+
+int
+hx509_crypto_get_params(hx509_context context,
+ hx509_crypto crypto,
+ const heim_octet_string *ivec,
+ heim_octet_string *param)
+{
+ return (*crypto->cipher->get_params)(context, crypto, ivec, param);
+}
+
+int
+hx509_crypto_encrypt(hx509_crypto crypto,
+ const void *data,
+ const size_t length,
+ heim_octet_string *ivec,
+ heim_octet_string **ciphertext)
+{
+ EVP_CIPHER_CTX evp;
+ size_t padsize;
+ int ret;
+
+ *ciphertext = NULL;
+
+ EVP_CIPHER_CTX_init(&evp);
+
+ ivec->length = EVP_CIPHER_iv_length(crypto->c);
+ ivec->data = malloc(ivec->length);
+ if (ivec->data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ if (RAND_bytes(ivec->data, ivec->length) <= 0) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+
+ ret = EVP_CipherInit_ex(&evp, crypto->c, NULL,
+ crypto->key.data, ivec->data, 1);
+ if (ret != 1) {
+ EVP_CIPHER_CTX_cleanup(&evp);
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+
+ *ciphertext = calloc(1, sizeof(**ciphertext));
+ if (*ciphertext == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ if (EVP_CIPHER_block_size(crypto->c) == 1) {
+ padsize = 0;
+ } else {
+ int bsize = EVP_CIPHER_block_size(crypto->c);
+ padsize = bsize - (length % bsize);
+ }
+ (*ciphertext)->length = length + padsize;
+ (*ciphertext)->data = malloc(length + padsize);
+ if ((*ciphertext)->data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ memcpy((*ciphertext)->data, data, length);
+ if (padsize) {
+ int i;
+ unsigned char *p = (*ciphertext)->data;
+ p += length;
+ for (i = 0; i < padsize; i++)
+ *p++ = padsize;
+ }
+
+ ret = EVP_Cipher(&evp, (*ciphertext)->data,
+ (*ciphertext)->data,
+ length + padsize);
+ if (ret != 1) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+ ret = 0;
+
+ out:
+ if (ret) {
+ if (ivec->data) {
+ free(ivec->data);
+ memset(ivec, 0, sizeof(*ivec));
+ }
+ if (*ciphertext) {
+ if ((*ciphertext)->data) {
+ free((*ciphertext)->data);
+ }
+ free(*ciphertext);
+ *ciphertext = NULL;
+ }
+ }
+ EVP_CIPHER_CTX_cleanup(&evp);
+
+ return ret;
+}
+
+int
+hx509_crypto_decrypt(hx509_crypto crypto,
+ const void *data,
+ const size_t length,
+ heim_octet_string *ivec,
+ heim_octet_string *clear)
+{
+ EVP_CIPHER_CTX evp;
+ void *idata = NULL;
+ int ret;
+
+ clear->data = NULL;
+ clear->length = 0;
+
+ if (ivec && EVP_CIPHER_iv_length(crypto->c) < ivec->length)
+ return HX509_CRYPTO_INTERNAL_ERROR;
+
+ if (crypto->key.data == NULL)
+ return HX509_CRYPTO_INTERNAL_ERROR;
+
+ if (ivec)
+ idata = ivec->data;
+
+ EVP_CIPHER_CTX_init(&evp);
+
+ ret = EVP_CipherInit_ex(&evp, crypto->c, NULL,
+ crypto->key.data, idata, 0);
+ if (ret != 1) {
+ EVP_CIPHER_CTX_cleanup(&evp);
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+
+ clear->length = length;
+ clear->data = malloc(length);
+ if (clear->data == NULL) {
+ EVP_CIPHER_CTX_cleanup(&evp);
+ clear->length = 0;
+ return ENOMEM;
+ }
+
+ if (EVP_Cipher(&evp, clear->data, data, length) != 1) {
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+ EVP_CIPHER_CTX_cleanup(&evp);
+
+ if (EVP_CIPHER_block_size(crypto->c) > 1) {
+ int padsize;
+ unsigned char *p;
+ int j, bsize = EVP_CIPHER_block_size(crypto->c);
+
+ if (clear->length < bsize) {
+ ret = HX509_CMS_PADDING_ERROR;
+ goto out;
+ }
+
+ p = clear->data;
+ p += clear->length - 1;
+ padsize = *p;
+ if (padsize > bsize) {
+ ret = HX509_CMS_PADDING_ERROR;
+ goto out;
+ }
+ clear->length -= padsize;
+ for (j = 0; j < padsize; j++) {
+ if (*p-- != padsize) {
+ ret = HX509_CMS_PADDING_ERROR;
+ goto out;
+ }
+ }
+ }
+
+ return 0;
+
+ out:
+ if (clear->data)
+ free(clear->data);
+ clear->data = NULL;
+ clear->length = 0;
+ return ret;
+}
+
+typedef int (*PBE_string2key_func)(hx509_context,
+ const char *,
+ const heim_octet_string *,
+ hx509_crypto *, heim_octet_string *,
+ heim_octet_string *,
+ const heim_oid *, const EVP_MD *);
+
+static int
+PBE_string2key(hx509_context context,
+ const char *password,
+ const heim_octet_string *parameters,
+ hx509_crypto *crypto,
+ heim_octet_string *key, heim_octet_string *iv,
+ const heim_oid *enc_oid,
+ const EVP_MD *md)
+{
+ PKCS12_PBEParams p12params;
+ int passwordlen = strlen(password);
+ hx509_crypto c;
+ int iter, saltlen, ret;
+ unsigned char *salt;
+
+ if (parameters == NULL)
+ return HX509_ALG_NOT_SUPP;
+
+ ret = decode_PKCS12_PBEParams(parameters->data,
+ parameters->length,
+ &p12params, NULL);
+ if (ret)
+ goto out;
+
+ if (p12params.iterations)
+ iter = *p12params.iterations;
+ else
+ iter = 1;
+ salt = p12params.salt.data;
+ saltlen = p12params.salt.length;
+
+ /* XXX It needs to be here, but why ? */
+ if (passwordlen == 0)
+ password = NULL;
+
+ if (!PKCS12_key_gen (password, passwordlen, salt, saltlen,
+ PKCS12_KEY_ID, iter, key->length, key->data, md)) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+
+ if (!PKCS12_key_gen (password, passwordlen, salt, saltlen,
+ PKCS12_IV_ID, iter, iv->length, iv->data, md)) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+
+ ret = hx509_crypto_init(context, NULL, enc_oid, &c);
+ if (ret)
+ goto out;
+
+ ret = hx509_crypto_set_key_data(c, key->data, key->length);
+ if (ret) {
+ hx509_crypto_destroy(c);
+ goto out;
+ }
+
+ *crypto = c;
+out:
+ free_PKCS12_PBEParams(&p12params);
+ return ret;
+}
+
+static const heim_oid *
+find_string2key(const heim_oid *oid,
+ const EVP_CIPHER **c,
+ const EVP_MD **md,
+ PBE_string2key_func *s2k)
+{
+ if (der_heim_oid_cmp(oid, oid_id_pbewithSHAAnd40BitRC2_CBC()) == 0) {
+ *c = EVP_rc2_40_cbc();
+ *md = EVP_sha1();
+ *s2k = PBE_string2key;
+ return oid_private_rc2_40();
+ } else if (der_heim_oid_cmp(oid, oid_id_pbeWithSHAAnd128BitRC2_CBC()) == 0) {
+ *c = EVP_rc2_cbc();
+ *md = EVP_sha1();
+ *s2k = PBE_string2key;
+ return oid_id_pkcs3_rc2_cbc();
+#if 0
+ } else if (der_heim_oid_cmp(oid, oid_id_pbeWithSHAAnd40BitRC4()) == 0) {
+ *c = EVP_rc4_40();
+ *md = EVP_sha1();
+ *s2k = PBE_string2key;
+ return NULL;
+ } else if (der_heim_oid_cmp(oid, oid_id_pbeWithSHAAnd128BitRC4()) == 0) {
+ *c = EVP_rc4();
+ *md = EVP_sha1();
+ *s2k = PBE_string2key;
+ return oid_id_pkcs3_rc4();
+#endif
+ } else if (der_heim_oid_cmp(oid, oid_id_pbeWithSHAAnd3_KeyTripleDES_CBC()) == 0) {
+ *c = EVP_des_ede3_cbc();
+ *md = EVP_sha1();
+ *s2k = PBE_string2key;
+ return oid_id_pkcs3_des_ede3_cbc();
+ }
+
+ return NULL;
+}
+
+
+int
+_hx509_pbe_decrypt(hx509_context context,
+ hx509_lock lock,
+ const AlgorithmIdentifier *ai,
+ const heim_octet_string *econtent,
+ heim_octet_string *content)
+{
+ const struct _hx509_password *pw;
+ heim_octet_string key, iv;
+ const heim_oid *enc_oid;
+ const EVP_CIPHER *c;
+ const EVP_MD *md;
+ PBE_string2key_func s2k;
+ int i, ret = 0;
+
+ memset(&key, 0, sizeof(key));
+ memset(&iv, 0, sizeof(iv));
+
+ memset(content, 0, sizeof(*content));
+
+ enc_oid = find_string2key(&ai->algorithm, &c, &md, &s2k);
+ if (enc_oid == NULL) {
+ hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
+ "String to key algorithm not supported");
+ ret = HX509_ALG_NOT_SUPP;
+ goto out;
+ }
+
+ key.length = EVP_CIPHER_key_length(c);
+ key.data = malloc(key.length);
+ if (key.data == NULL) {
+ ret = ENOMEM;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ iv.length = EVP_CIPHER_iv_length(c);
+ iv.data = malloc(iv.length);
+ if (iv.data == NULL) {
+ ret = ENOMEM;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ pw = _hx509_lock_get_passwords(lock);
+
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ for (i = 0; i < pw->len + 1; i++) {
+ hx509_crypto crypto;
+ const char *password;
+
+ if (i < pw->len)
+ password = pw->val[i];
+ else
+ password = "";
+
+ ret = (*s2k)(context, password, ai->parameters, &crypto,
+ &key, &iv, enc_oid, md);
+ if (ret)
+ goto out;
+
+ ret = hx509_crypto_decrypt(crypto,
+ econtent->data,
+ econtent->length,
+ &iv,
+ content);
+ hx509_crypto_destroy(crypto);
+ if (ret == 0)
+ goto out;
+
+ }
+out:
+ if (key.data)
+ der_free_octet_string(&key);
+ if (iv.data)
+ der_free_octet_string(&iv);
+ return ret;
+}
+
+/*
+ *
+ */
+
+
+int
+_hx509_match_keys(hx509_cert c, hx509_private_key private_key)
+{
+ const Certificate *cert;
+ const SubjectPublicKeyInfo *spi;
+ RSAPublicKey pk;
+ RSA *rsa;
+ size_t size;
+ int ret;
+
+ if (private_key->private_key.rsa == NULL)
+ return 0;
+
+ rsa = private_key->private_key.rsa;
+ if (rsa->d == NULL || rsa->p == NULL || rsa->q == NULL)
+ return 0;
+
+ cert = _hx509_get_cert(c);
+ spi = &cert->tbsCertificate.subjectPublicKeyInfo;
+
+ rsa = RSA_new();
+ if (rsa == NULL)
+ return 0;
+
+ ret = decode_RSAPublicKey(spi->subjectPublicKey.data,
+ spi->subjectPublicKey.length / 8,
+ &pk, &size);
+ if (ret) {
+ RSA_free(rsa);
+ return 0;
+ }
+ rsa->n = heim_int2BN(&pk.modulus);
+ rsa->e = heim_int2BN(&pk.publicExponent);
+
+ free_RSAPublicKey(&pk);
+
+ rsa->d = BN_dup(private_key->private_key.rsa->d);
+ rsa->p = BN_dup(private_key->private_key.rsa->p);
+ rsa->q = BN_dup(private_key->private_key.rsa->q);
+ rsa->dmp1 = BN_dup(private_key->private_key.rsa->dmp1);
+ rsa->dmq1 = BN_dup(private_key->private_key.rsa->dmq1);
+ rsa->iqmp = BN_dup(private_key->private_key.rsa->iqmp);
+
+ if (rsa->n == NULL || rsa->e == NULL ||
+ rsa->d == NULL || rsa->p == NULL|| rsa->q == NULL ||
+ rsa->dmp1 == NULL || rsa->dmq1 == NULL) {
+ RSA_free(rsa);
+ return 0;
+ }
+
+ ret = RSA_check_key(rsa);
+ RSA_free(rsa);
+
+ return ret == 1;
+}
+
+static const heim_oid *
+find_keytype(const hx509_private_key key)
+{
+ const struct signature_alg *md;
+
+ if (key == NULL)
+ return NULL;
+
+ md = find_sig_alg(key->signature_alg);
+ if (md == NULL)
+ return NULL;
+ return (*md->key_oid)();
+}
+
+
+int
+hx509_crypto_select(const hx509_context context,
+ int type,
+ const hx509_private_key source,
+ hx509_peer_info peer,
+ AlgorithmIdentifier *selected)
+{
+ const heim_oid *keytype = NULL;
+ const AlgorithmIdentifier *def;
+ size_t i, j;
+ int ret, bits;
+
+ memset(selected, 0, sizeof(*selected));
+
+ if (type == HX509_SELECT_DIGEST) {
+ bits = SIG_DIGEST;
+ def = hx509_signature_sha1();
+ } else if (type == HX509_SELECT_PUBLIC_SIG) {
+ bits = SIG_PUBLIC_SIG;
+ /* XXX depend on `source´ and `peer´ */
+ def = hx509_signature_rsa_with_sha1();
+ } else {
+ hx509_set_error_string(context, 0, EINVAL,
+ "Unknown type %d of selection", type);
+ return EINVAL;
+ }
+
+ keytype = find_keytype(source);
+
+ if (peer) {
+ for (i = 0; i < peer->len; i++) {
+ for (j = 0; sig_algs[j]; j++) {
+ if ((sig_algs[j]->flags & bits) != bits)
+ continue;
+ if (der_heim_oid_cmp((*sig_algs[j]->sig_oid)(),
+ &peer->val[i].algorithm) != 0)
+ continue;
+ if (keytype && sig_algs[j]->key_oid &&
+ der_heim_oid_cmp(keytype, (*sig_algs[j]->key_oid)()))
+ continue;
+
+ /* found one, use that */
+ ret = copy_AlgorithmIdentifier(&peer->val[i], selected);
+ if (ret)
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ }
+ }
+
+ /* use default */
+ ret = copy_AlgorithmIdentifier(def, selected);
+ if (ret)
+ hx509_clear_error_string(context);
+ return ret;
+}
+
+int
+hx509_crypto_available(hx509_context context,
+ int type,
+ hx509_cert source,
+ AlgorithmIdentifier **val,
+ unsigned int *plen)
+{
+ const heim_oid *keytype = NULL;
+ unsigned int len, i;
+ void *ptr;
+ int bits, ret;
+
+ *val = NULL;
+
+ if (type == HX509_SELECT_ALL) {
+ bits = SIG_DIGEST | SIG_PUBLIC_SIG;
+ } else if (type == HX509_SELECT_DIGEST) {
+ bits = SIG_DIGEST;
+ } else if (type == HX509_SELECT_PUBLIC_SIG) {
+ bits = SIG_PUBLIC_SIG;
+ } else {
+ hx509_set_error_string(context, 0, EINVAL,
+ "Unknown type %d of available", type);
+ return EINVAL;
+ }
+
+ if (source)
+ keytype = find_keytype(_hx509_cert_private_key(source));
+
+ len = 0;
+ for (i = 0; sig_algs[i]; i++) {
+ if ((sig_algs[i]->flags & bits) == 0)
+ continue;
+ if (sig_algs[i]->sig_alg == NULL)
+ continue;
+ if (keytype && sig_algs[i]->key_oid &&
+ der_heim_oid_cmp((*sig_algs[i]->key_oid)(), keytype))
+ continue;
+
+ /* found one, add that to the list */
+ ptr = realloc(*val, sizeof(**val) * (len + 1));
+ if (ptr == NULL)
+ goto out;
+ *val = ptr;
+
+ ret = copy_AlgorithmIdentifier((*sig_algs[i]->sig_alg)(), &(*val)[len]);
+ if (ret)
+ goto out;
+ len++;
+ }
+
+ *plen = len;
+ return 0;
+
+out:
+ for (i = 0; i < len; i++)
+ free_AlgorithmIdentifier(&(*val)[i]);
+ free(*val);
+ *val = NULL;
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+}
+
+void
+hx509_crypto_free_algs(AlgorithmIdentifier *val,
+ unsigned int len)
+{
+ unsigned int i;
+ for (i = 0; i < len; i++)
+ free_AlgorithmIdentifier(&val[i]);
+ free(val);
+}
diff --git a/source4/heimdal/lib/hx509/error.c b/source4/heimdal/lib/hx509/error.c
new file mode 100644
index 0000000000..770b71981a
--- /dev/null
+++ b/source4/heimdal/lib/hx509/error.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2006 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: error.c,v 1.4 2006/11/16 15:08:09 lha Exp $");
+
+struct hx509_error_data {
+ hx509_error next;
+ int code;
+ char *msg;
+};
+
+static void
+free_error_string(hx509_error msg)
+{
+ while(msg) {
+ hx509_error m2 = msg->next;
+ free(msg->msg);
+ free(msg);
+ msg = m2;
+ }
+}
+
+void
+hx509_clear_error_string(hx509_context context)
+{
+ free_error_string(context->error);
+ context->error = NULL;
+}
+
+void
+hx509_set_error_stringv(hx509_context context, int flags, int code,
+ const char *fmt, va_list ap)
+{
+ hx509_error msg;
+
+ msg = calloc(1, sizeof(*msg));
+ if (msg == NULL) {
+ hx509_clear_error_string(context);
+ return;
+ }
+
+ if (vasprintf(&msg->msg, fmt, ap) == -1) {
+ hx509_clear_error_string(context);
+ free(msg);
+ return;
+ }
+ msg->code = code;
+
+ if (flags & HX509_ERROR_APPEND) {
+ msg->next = context->error;
+ context->error = msg;
+ } else {
+ free_error_string(context->error);
+ context->error = msg;
+ }
+}
+
+void
+hx509_set_error_string(hx509_context context, int flags, int code, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ hx509_set_error_stringv(context, flags, code, fmt, ap);
+ va_end(ap);
+}
+
+char *
+hx509_get_error_string(hx509_context context, int error_code)
+{
+ struct rk_strpool *p = NULL;
+ hx509_error msg;
+
+ if (context->error == NULL) {
+ const char *cstr;
+ char *str;
+
+ 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;
+ }
+
+ for (msg = context->error; msg; msg = msg->next)
+ p = rk_strpoolprintf(p, "%s%s", msg->msg,
+ msg->next != NULL ? "; " : "");
+
+ return rk_strpoolcollect(p);
+}
+
+void
+hx509_err(hx509_context context, int exit_code, int error_code, char *fmt, ...)
+{
+ va_list ap;
+ char *msg, *str;
+
+ va_start(ap, fmt);
+ vasprintf(&str, fmt, ap);
+ va_end(ap);
+ msg = hx509_get_error_string(context, error_code);
+ if (msg == NULL)
+ msg = "no error";
+
+ errx(exit_code, "%s: %s", str, msg);
+}
diff --git a/source4/heimdal/lib/hx509/file.c b/source4/heimdal/lib/hx509/file.c
new file mode 100644
index 0000000000..39497fc3a9
--- /dev/null
+++ b/source4/heimdal/lib/hx509/file.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2005 - 2006 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$");
+
+int
+_hx509_map_file(const char *fn, void **data, size_t *length, struct stat *rsb)
+{
+ struct stat sb;
+ size_t len;
+ ssize_t l;
+ int ret;
+ void *d;
+ int fd;
+
+ *data = NULL;
+ *length = 0;
+
+ fd = open(fn, O_RDONLY);
+ if (fd < 0)
+ return errno;
+
+ if (fstat(fd, &sb) < 0) {
+ ret = errno;
+ close(fd);
+ return ret;
+ }
+
+ len = sb.st_size;
+
+ d = malloc(len);
+ if (d == NULL) {
+ close(fd);
+ return ENOMEM;
+ }
+
+ l = read(fd, d, len);
+ close(fd);
+ if (l < 0 || l != len) {
+ free(d);
+ return EINVAL;
+ }
+
+ if (rsb)
+ *rsb = sb;
+ *data = d;
+ *length = len;
+ return 0;
+}
+
+void
+_hx509_unmap_file(void *data, size_t len)
+{
+ free(data);
+}
+
+int
+_hx509_write_file(const char *fn, const void *data, size_t length)
+{
+ ssize_t sz;
+ const unsigned char *p = data;
+ int fd;
+
+ fd = open(fn, O_WRONLY|O_TRUNC|O_CREAT, 0644);
+ if (fd < 0)
+ return errno;
+
+ do {
+ sz = write(fd, p, length);
+ if (sz < 0) {
+ int saved_errno = errno;
+ close(fd);
+ return saved_errno;
+ }
+ if (sz == 0)
+ break;
+ length -= sz;
+ } while (length > 0);
+
+ if (close(fd) == -1)
+ return errno;
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/hx509/hx509-protos.h b/source4/heimdal/lib/hx509/hx509-protos.h
new file mode 100644
index 0000000000..4fcab70ff8
--- /dev/null
+++ b/source4/heimdal/lib/hx509/hx509-protos.h
@@ -0,0 +1,824 @@
+/* This is a generated file */
+#ifndef __hx509_protos_h__
+#define __hx509_protos_h__
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+hx509_bitstring_print (
+ const heim_bit_string */*b*/,
+ hx509_vprint_func /*func*/,
+ void */*ctx*/);
+
+int
+hx509_ca_sign (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ hx509_cert /*signer*/,
+ hx509_cert */*certificate*/);
+
+int
+hx509_ca_sign_self (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ hx509_private_key /*signer*/,
+ hx509_cert */*certificate*/);
+
+int
+hx509_ca_tbs_add_eku (
+ hx509_context /*contex*/,
+ hx509_ca_tbs /*tbs*/,
+ const heim_oid */*oid*/);
+
+int
+hx509_ca_tbs_add_san_hostname (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ const char */*dnsname*/);
+
+int
+hx509_ca_tbs_add_san_otherName (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ const heim_oid */*oid*/,
+ const heim_octet_string */*os*/);
+
+int
+hx509_ca_tbs_add_san_pkinit (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ const char */*principal*/);
+
+int
+hx509_ca_tbs_add_san_rfc822name (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ const char */*rfc822Name*/);
+
+void
+hx509_ca_tbs_free (hx509_ca_tbs */*tbs*/);
+
+int
+hx509_ca_tbs_init (
+ hx509_context /*context*/,
+ hx509_ca_tbs */*tbs*/);
+
+int
+hx509_ca_tbs_set_ca (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ int /*pathLenConstraint*/);
+
+int
+hx509_ca_tbs_set_notAfter (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ time_t /*t*/);
+
+int
+hx509_ca_tbs_set_notAfter_lifetime (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ time_t /*delta*/);
+
+int
+hx509_ca_tbs_set_notBefore (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ time_t /*t*/);
+
+int
+hx509_ca_tbs_set_proxy (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ int /*pathLenConstraint*/);
+
+int
+hx509_ca_tbs_set_serialnumber (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ const heim_integer */*serialNumber*/);
+
+int
+hx509_ca_tbs_set_spki (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ const SubjectPublicKeyInfo */*spki*/);
+
+int
+hx509_ca_tbs_set_subject (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ hx509_name /*subject*/);
+
+int
+hx509_cert_check_eku (
+ hx509_context /*context*/,
+ hx509_cert /*cert*/,
+ const heim_oid */*eku*/,
+ int /*allow_any_eku*/);
+
+int
+hx509_cert_cmp (
+ hx509_cert /*p*/,
+ hx509_cert /*q*/);
+
+int
+hx509_cert_find_subjectAltName_otherName (
+ hx509_cert /*cert*/,
+ const heim_oid */*oid*/,
+ hx509_octet_string_list */*list*/);
+
+void
+hx509_cert_free (hx509_cert /*cert*/);
+
+hx509_cert_attribute
+hx509_cert_get_attribute (
+ hx509_cert /*cert*/,
+ const heim_oid */*oid*/);
+
+int
+hx509_cert_get_base_subject (
+ hx509_context /*context*/,
+ hx509_cert /*c*/,
+ hx509_name */*name*/);
+
+const char *
+hx509_cert_get_friendly_name (hx509_cert /*cert*/);
+
+int
+hx509_cert_get_issuer (
+ hx509_cert /*p*/,
+ hx509_name */*name*/);
+
+int
+hx509_cert_get_serialnumber (
+ hx509_cert /*p*/,
+ heim_integer */*i*/);
+
+int
+hx509_cert_get_subject (
+ hx509_cert /*p*/,
+ hx509_name */*name*/);
+
+int
+hx509_cert_init (
+ hx509_context /*context*/,
+ const Certificate */*c*/,
+ hx509_cert */*cert*/);
+
+int
+hx509_cert_keyusage_print (
+ hx509_context /*context*/,
+ hx509_cert /*c*/,
+ char **/*s*/);
+
+hx509_cert
+hx509_cert_ref (hx509_cert /*cert*/);
+
+int
+hx509_cert_set_friendly_name (
+ hx509_cert /*cert*/,
+ const char */*name*/);
+
+int
+hx509_certs_add (
+ hx509_context /*context*/,
+ hx509_certs /*certs*/,
+ hx509_cert /*cert*/);
+
+int
+hx509_certs_append (
+ hx509_context /*context*/,
+ hx509_certs /*to*/,
+ hx509_lock /*lock*/,
+ const char */*name*/);
+
+int
+hx509_certs_end_seq (
+ hx509_context /*context*/,
+ hx509_certs /*certs*/,
+ hx509_cursor /*cursor*/);
+
+int
+hx509_certs_find (
+ hx509_context /*context*/,
+ hx509_certs /*certs*/,
+ const hx509_query */*q*/,
+ hx509_cert */*r*/);
+
+void
+hx509_certs_free (hx509_certs */*certs*/);
+
+int
+hx509_certs_info (
+ hx509_context /*context*/,
+ hx509_certs /*certs*/,
+ int (*/*func*/)(void *, char *),
+ void */*ctx*/);
+
+int
+hx509_certs_init (
+ hx509_context /*context*/,
+ const char */*name*/,
+ int /*flags*/,
+ hx509_lock /*lock*/,
+ hx509_certs */*certs*/);
+
+int
+hx509_certs_iter (
+ hx509_context /*context*/,
+ hx509_certs /*certs*/,
+ int (*/*fn*/)(hx509_context, void *, hx509_cert),
+ void */*ctx*/);
+
+int
+hx509_certs_merge (
+ hx509_context /*context*/,
+ hx509_certs /*to*/,
+ hx509_certs /*from*/);
+
+int
+hx509_certs_next_cert (
+ hx509_context /*context*/,
+ hx509_certs /*certs*/,
+ hx509_cursor /*cursor*/,
+ hx509_cert */*cert*/);
+
+int
+hx509_certs_start_seq (
+ hx509_context /*context*/,
+ hx509_certs /*certs*/,
+ hx509_cursor */*cursor*/);
+
+int
+hx509_certs_store (
+ hx509_context /*context*/,
+ hx509_certs /*certs*/,
+ int /*flags*/,
+ hx509_lock /*lock*/);
+
+int
+hx509_ci_print_names (
+ hx509_context /*context*/,
+ void */*ctx*/,
+ hx509_cert /*c*/);
+
+void
+hx509_clear_error_string (hx509_context /*context*/);
+
+int
+hx509_cms_create_signed_1 (
+ hx509_context /*context*/,
+ const heim_oid */*eContentType*/,
+ const void */*data*/,
+ size_t /*length*/,
+ const AlgorithmIdentifier */*digest_alg*/,
+ hx509_cert /*cert*/,
+ hx509_peer_info /*peer*/,
+ hx509_certs /*anchors*/,
+ hx509_certs /*pool*/,
+ heim_octet_string */*signed_data*/);
+
+int
+hx509_cms_decrypt_encrypted (
+ hx509_context /*context*/,
+ hx509_lock /*lock*/,
+ const void */*data*/,
+ size_t /*length*/,
+ heim_oid */*contentType*/,
+ heim_octet_string */*content*/);
+
+int
+hx509_cms_envelope_1 (
+ hx509_context /*context*/,
+ hx509_cert /*cert*/,
+ const void */*data*/,
+ size_t /*length*/,
+ const heim_oid */*encryption_type*/,
+ const heim_oid */*contentType*/,
+ heim_octet_string */*content*/);
+
+int
+hx509_cms_unenvelope (
+ hx509_context /*context*/,
+ hx509_certs /*certs*/,
+ int /*flags*/,
+ const void */*data*/,
+ size_t /*length*/,
+ const heim_octet_string */*encryptedContent*/,
+ heim_oid */*contentType*/,
+ heim_octet_string */*content*/);
+
+int
+hx509_cms_unwrap_ContentInfo (
+ const heim_octet_string */*in*/,
+ heim_oid */*oid*/,
+ heim_octet_string */*out*/,
+ int */*have_data*/);
+
+int
+hx509_cms_verify_signed (
+ hx509_context /*context*/,
+ hx509_verify_ctx /*ctx*/,
+ const void */*data*/,
+ size_t /*length*/,
+ hx509_certs /*store*/,
+ heim_oid */*contentType*/,
+ heim_octet_string */*content*/,
+ hx509_certs */*signer_certs*/);
+
+int
+hx509_cms_wrap_ContentInfo (
+ const heim_oid */*oid*/,
+ const heim_octet_string */*buf*/,
+ heim_octet_string */*res*/);
+
+void
+hx509_context_free (hx509_context */*context*/);
+
+int
+hx509_context_init (hx509_context */*context*/);
+
+void
+hx509_context_set_missing_revoke (
+ hx509_context /*context*/,
+ int /*flag*/);
+
+int
+hx509_crypto_available (
+ hx509_context /*context*/,
+ int /*type*/,
+ hx509_cert /*source*/,
+ AlgorithmIdentifier **/*val*/,
+ unsigned int */*plen*/);
+
+int
+hx509_crypto_decrypt (
+ hx509_crypto /*crypto*/,
+ const void */*data*/,
+ const size_t /*length*/,
+ heim_octet_string */*ivec*/,
+ heim_octet_string */*clear*/);
+
+void
+hx509_crypto_destroy (hx509_crypto /*crypto*/);
+
+int
+hx509_crypto_encrypt (
+ hx509_crypto /*crypto*/,
+ const void */*data*/,
+ const size_t /*length*/,
+ heim_octet_string */*ivec*/,
+ heim_octet_string **/*ciphertext*/);
+
+const heim_oid *
+hx509_crypto_enctype_by_name (const char */*name*/);
+
+void
+hx509_crypto_free_algs (
+ AlgorithmIdentifier */*val*/,
+ unsigned int /*len*/);
+
+int
+hx509_crypto_get_params (
+ hx509_context /*context*/,
+ hx509_crypto /*crypto*/,
+ const heim_octet_string */*ivec*/,
+ heim_octet_string */*param*/);
+
+int
+hx509_crypto_init (
+ hx509_context /*context*/,
+ const char */*provider*/,
+ const heim_oid */*enctype*/,
+ hx509_crypto */*crypto*/);
+
+const char *
+hx509_crypto_provider (hx509_crypto /*crypto*/);
+
+int
+hx509_crypto_select (
+ const hx509_context /*context*/,
+ int /*type*/,
+ const hx509_private_key /*source*/,
+ hx509_peer_info /*peer*/,
+ AlgorithmIdentifier */*selected*/);
+
+int
+hx509_crypto_set_key_data (
+ hx509_crypto /*crypto*/,
+ const void */*data*/,
+ size_t /*length*/);
+
+int
+hx509_crypto_set_key_name (
+ hx509_crypto /*crypto*/,
+ const char */*name*/);
+
+int
+hx509_crypto_set_params (
+ hx509_context /*context*/,
+ hx509_crypto /*crypto*/,
+ const heim_octet_string */*param*/,
+ heim_octet_string */*ivec*/);
+
+int
+hx509_crypto_set_random_key (
+ hx509_crypto /*crypto*/,
+ heim_octet_string */*key*/);
+
+void
+hx509_err (
+ hx509_context /*context*/,
+ int /*exit_code*/,
+ int /*error_code*/,
+ char */*fmt*/,
+ ...);
+
+void
+hx509_free_octet_string_list (hx509_octet_string_list */*list*/);
+
+char *
+hx509_get_error_string (
+ hx509_context /*context*/,
+ int /*error_code*/);
+
+int
+hx509_get_one_cert (
+ hx509_context /*context*/,
+ hx509_certs /*certs*/,
+ hx509_cert */*c*/);
+
+int
+hx509_lock_add_cert (
+ hx509_context /*context*/,
+ hx509_lock /*lock*/,
+ hx509_cert /*cert*/);
+
+int
+hx509_lock_add_certs (
+ hx509_context /*context*/,
+ hx509_lock /*lock*/,
+ hx509_certs /*certs*/);
+
+int
+hx509_lock_add_password (
+ hx509_lock /*lock*/,
+ const char */*password*/);
+
+int
+hx509_lock_command_string (
+ hx509_lock /*lock*/,
+ const char */*string*/);
+
+void
+hx509_lock_free (hx509_lock /*lock*/);
+
+int
+hx509_lock_init (
+ hx509_context /*context*/,
+ hx509_lock */*lock*/);
+
+int
+hx509_lock_prompt (
+ hx509_lock /*lock*/,
+ hx509_prompt */*prompt*/);
+
+void
+hx509_lock_reset_certs (
+ hx509_context /*context*/,
+ hx509_lock /*lock*/);
+
+void
+hx509_lock_reset_passwords (hx509_lock /*lock*/);
+
+void
+hx509_lock_reset_promper (hx509_lock /*lock*/);
+
+int
+hx509_lock_set_prompter (
+ hx509_lock /*lock*/,
+ hx509_prompter_fct /*prompt*/,
+ void */*data*/);
+
+int
+hx509_name_copy (
+ hx509_context /*context*/,
+ const hx509_name /*from*/,
+ hx509_name */*to*/);
+
+void
+hx509_name_free (hx509_name */*name*/);
+
+int
+hx509_name_is_null_p (const hx509_name /*name*/);
+
+int
+hx509_name_to_Name (
+ const hx509_name /*from*/,
+ Name */*to*/);
+
+int
+hx509_name_to_der_name (
+ const hx509_name /*name*/,
+ void **/*data*/,
+ size_t */*length*/);
+
+int
+hx509_name_to_string (
+ const hx509_name /*name*/,
+ char **/*str*/);
+
+int
+hx509_ocsp_request (
+ hx509_context /*context*/,
+ hx509_certs /*reqcerts*/,
+ hx509_certs /*pool*/,
+ hx509_cert /*signer*/,
+ const AlgorithmIdentifier */*digest*/,
+ heim_octet_string */*request*/,
+ heim_octet_string */*nonce*/);
+
+int
+hx509_ocsp_verify (
+ hx509_context /*context*/,
+ time_t /*now*/,
+ hx509_cert /*cert*/,
+ int /*flags*/,
+ const void */*data*/,
+ size_t /*length*/,
+ time_t */*expiration*/);
+
+void
+hx509_oid_print (
+ const heim_oid */*oid*/,
+ hx509_vprint_func /*func*/,
+ void */*ctx*/);
+
+int
+hx509_oid_sprint (
+ const heim_oid */*oid*/,
+ char **/*str*/);
+
+int
+hx509_parse_name (
+ hx509_context /*context*/,
+ const char */*str*/,
+ hx509_name */*name*/);
+
+int
+hx509_peer_info_alloc (
+ hx509_context /*context*/,
+ hx509_peer_info */*peer*/);
+
+int
+hx509_peer_info_free (hx509_peer_info /*peer*/);
+
+int
+hx509_peer_info_set_cert (
+ hx509_peer_info /*peer*/,
+ hx509_cert /*cert*/);
+
+int
+hx509_peer_info_set_cms_algs (
+ hx509_context /*context*/,
+ hx509_peer_info /*peer*/,
+ const AlgorithmIdentifier */*val*/,
+ size_t /*len*/);
+
+void
+hx509_print_func (
+ hx509_vprint_func /*func*/,
+ void */*ctx*/,
+ const char */*fmt*/,
+ ...);
+
+void
+hx509_print_stdout (
+ void */*ctx*/,
+ const char */*fmt*/,
+ va_list /*va*/);
+
+int
+hx509_prompt_hidden (hx509_prompt_type /*type*/);
+
+int
+hx509_query_alloc (
+ hx509_context /*context*/,
+ hx509_query **/*q*/);
+
+void
+hx509_query_free (
+ hx509_context /*context*/,
+ hx509_query */*q*/);
+
+int
+hx509_query_match_cmp_func (
+ hx509_query */*q*/,
+ int (*/*func*/)(void *, hx509_cert),
+ void */*ctx*/);
+
+int
+hx509_query_match_friendly_name (
+ hx509_query */*q*/,
+ const char */*name*/);
+
+int
+hx509_query_match_issuer_serial (
+ hx509_query */*q*/,
+ const Name */*issuer*/,
+ const heim_integer */*serialNumber*/);
+
+void
+hx509_query_match_option (
+ hx509_query */*q*/,
+ hx509_query_option /*option*/);
+
+int
+hx509_revoke_add_crl (
+ hx509_context /*context*/,
+ hx509_revoke_ctx /*ctx*/,
+ const char */*path*/);
+
+int
+hx509_revoke_add_ocsp (
+ hx509_context /*context*/,
+ hx509_revoke_ctx /*ctx*/,
+ const char */*path*/);
+
+void
+hx509_revoke_free (hx509_revoke_ctx */*ctx*/);
+
+int
+hx509_revoke_init (
+ hx509_context /*context*/,
+ hx509_revoke_ctx */*ctx*/);
+
+int
+hx509_revoke_ocsp_print (
+ hx509_context /*context*/,
+ const char */*path*/,
+ FILE */*out*/);
+
+int
+hx509_revoke_verify (
+ hx509_context /*context*/,
+ hx509_revoke_ctx /*ctx*/,
+ hx509_certs /*certs*/,
+ time_t /*now*/,
+ hx509_cert /*cert*/,
+ hx509_cert /*parent_cert*/);
+
+void
+hx509_set_error_string (
+ hx509_context /*context*/,
+ int /*flags*/,
+ int /*code*/,
+ const char */*fmt*/,
+ ...);
+
+void
+hx509_set_error_stringv (
+ hx509_context /*context*/,
+ int /*flags*/,
+ int /*code*/,
+ const char */*fmt*/,
+ va_list /*ap*/);
+
+const AlgorithmIdentifier *
+hx509_signature_md2 (void);
+
+const AlgorithmIdentifier *
+hx509_signature_md5 (void);
+
+const AlgorithmIdentifier *
+hx509_signature_rsa (void);
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_md2 (void);
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_md5 (void);
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha1 (void);
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha256 (void);
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha384 (void);
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha512 (void);
+
+const AlgorithmIdentifier *
+hx509_signature_sha1 (void);
+
+const AlgorithmIdentifier *
+hx509_signature_sha256 (void);
+
+const AlgorithmIdentifier *
+hx509_signature_sha384 (void);
+
+const AlgorithmIdentifier *
+hx509_signature_sha512 (void);
+
+int
+hx509_unparse_der_name (
+ const void */*data*/,
+ size_t /*length*/,
+ char **/*str*/);
+
+int
+hx509_validate_cert (
+ hx509_context /*context*/,
+ hx509_validate_ctx /*ctx*/,
+ hx509_cert /*cert*/);
+
+void
+hx509_validate_ctx_add_flags (
+ hx509_validate_ctx /*ctx*/,
+ int /*flags*/);
+
+void
+hx509_validate_ctx_free (hx509_validate_ctx /*ctx*/);
+
+int
+hx509_validate_ctx_init (
+ hx509_context /*context*/,
+ hx509_validate_ctx */*ctx*/);
+
+void
+hx509_validate_ctx_set_print (
+ hx509_validate_ctx /*ctx*/,
+ hx509_vprint_func /*func*/,
+ void */*c*/);
+
+void
+hx509_verify_attach_anchors (
+ hx509_verify_ctx /*ctx*/,
+ hx509_certs /*set*/);
+
+void
+hx509_verify_attach_revoke (
+ hx509_verify_ctx /*ctx*/,
+ hx509_revoke_ctx /*revoke_ctx*/);
+
+void
+hx509_verify_destroy_ctx (hx509_verify_ctx /*ctx*/);
+
+int
+hx509_verify_hostname (
+ hx509_context /*context*/,
+ const hx509_cert /*cert*/,
+ int /*require_match*/,
+ const char */*hostname*/,
+ const struct sockaddr */*sa*/,
+ int /*sa_size*/);
+
+int
+hx509_verify_init_ctx (
+ hx509_context /*context*/,
+ hx509_verify_ctx */*ctx*/);
+
+int
+hx509_verify_path (
+ hx509_context /*context*/,
+ hx509_verify_ctx /*ctx*/,
+ hx509_cert /*cert*/,
+ hx509_certs /*pool*/);
+
+void
+hx509_verify_set_proxy_certificate (
+ hx509_verify_ctx /*ctx*/,
+ int /*boolean*/);
+
+void
+hx509_verify_set_strict_rfc3280_verification (
+ hx509_verify_ctx /*ctx*/,
+ int /*boolean*/);
+
+void
+hx509_verify_set_time (
+ hx509_verify_ctx /*ctx*/,
+ time_t /*t*/);
+
+int
+hx509_verify_signature (
+ hx509_context /*context*/,
+ const hx509_cert /*signer*/,
+ const AlgorithmIdentifier */*alg*/,
+ const heim_octet_string */*data*/,
+ const heim_octet_string */*sig*/);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __hx509_protos_h__ */
diff --git a/source4/heimdal/lib/hx509/hx509.h b/source4/heimdal/lib/hx509/hx509.h
new file mode 100644
index 0000000000..70f29ea92d
--- /dev/null
+++ b/source4/heimdal/lib/hx509/hx509.h
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+/* $Id: hx509.h,v 1.16 2007/01/09 10:52:05 lha Exp $ */
+
+typedef struct hx509_cert_attribute_data *hx509_cert_attribute;
+typedef struct hx509_cert_data *hx509_cert;
+typedef struct hx509_certs_data *hx509_certs;
+typedef struct hx509_context_data *hx509_context;
+typedef struct hx509_crypto_data *hx509_crypto;
+typedef struct hx509_lock_data *hx509_lock;
+typedef struct hx509_name_data *hx509_name;
+typedef struct hx509_private_key *hx509_private_key;
+typedef struct hx509_validate_ctx_data *hx509_validate_ctx;
+typedef struct hx509_verify_ctx_data *hx509_verify_ctx;
+typedef struct hx509_revoke_ctx_data *hx509_revoke_ctx;
+typedef struct hx509_query_data hx509_query;
+typedef void * hx509_cursor;
+typedef struct hx509_request_data *hx509_request;
+typedef struct hx509_error_data *hx509_error;
+typedef struct hx509_peer_info *hx509_peer_info;
+typedef struct hx509_ca_tbs *hx509_ca_tbs;
+
+typedef void (*hx509_vprint_func)(void *, const char *, va_list);
+
+enum {
+ HX509_VALIDATE_F_VALIDATE = 1,
+ HX509_VALIDATE_F_VERBOSE = 2
+};
+
+struct hx509_cert_attribute_data {
+ heim_oid oid;
+ heim_octet_string data;
+};
+
+typedef enum {
+ HX509_PROMPT_TYPE_PASSWORD = 0x1, /* password, hidden */
+ HX509_PROMPT_TYPE_QUESTION = 0x2, /* question, not hidden */
+ HX509_PROMPT_TYPE_INFO = 0x4 /* infomation, reply doesn't matter */
+} hx509_prompt_type;
+
+typedef struct hx509_prompt {
+ const char *prompt;
+ hx509_prompt_type type;
+ heim_octet_string reply;
+} hx509_prompt;
+
+typedef int (*hx509_prompter_fct)(void *, const hx509_prompt *);
+
+typedef struct hx509_octet_string_list {
+ size_t len;
+ heim_octet_string *val;
+} hx509_octet_string_list;
+
+/*
+ * Options passed to hx509_query_match_option.
+ */
+typedef enum {
+ HX509_QUERY_OPTION_PRIVATE_KEY = 1,
+ HX509_QUERY_OPTION_KU_ENCIPHERMENT = 2,
+ HX509_QUERY_OPTION_KU_DIGITALSIGNATURE = 3,
+ HX509_QUERY_OPTION_KU_KEYCERTSIGN = 4,
+ HX509_QUERY_OPTION_END = 0xffff
+} hx509_query_option;
+
+/* flags to hx509_certs_init */
+#define HX509_CERTS_CREATE 0x01
+
+/* flags to hx509_set_error_string */
+#define HX509_ERROR_APPEND 0x01
+
+/* flags to hx509_cms_unenvelope */
+#define HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT 0x01
+
+/* selectors passed to hx509_crypto_select and hx509_crypto_available */
+#define HX509_SELECT_ALL 0
+#define HX509_SELECT_DIGEST 1
+#define HX509_SELECT_PUBLIC_SIG 2
+#define HX509_SELECT_PUBLIC_ENC 3
+
+#include <hx509-protos.h>
diff --git a/source4/heimdal/lib/hx509/hx509_err.c b/source4/heimdal/lib/hx509/hx509_err.c
new file mode 100644
index 0000000000..339759d6b7
--- /dev/null
+++ b/source4/heimdal/lib/hx509/hx509_err.c
@@ -0,0 +1,157 @@
+/* Generated from /home/data/samba/samba4/svn/source/heimdal/lib/hx509/hx509_err.et */
+/* $Id: hx509_err.et,v 1.19 2006/12/30 23:05:39 lha Exp $ */
+
+#include <stddef.h>
+#include <com_err.h>
+#include "hx509_err.h"
+
+static const char *hx_error_strings[] = {
+ /* 000 */ "ASN.1 failed call to system time library",
+ /* 001 */ "Extension not found",
+ /* 002 */ "Certification path not found",
+ /* 003 */ "Parent certificate is not a CA",
+ /* 004 */ "CA path too deep",
+ /* 005 */ "Signature algorithm not supported",
+ /* 006 */ "Signature algorithm doesn't match certificate key",
+ /* 007 */ "Certificate used before it became valid",
+ /* 008 */ "Certificate used after it became invalid",
+ /* 009 */ "Private key required for the operation is missing",
+ /* 010 */ "Algorithm not supported",
+ /* 011 */ "Issuer couldn't be found",
+ /* 012 */ "Error verifing constraints",
+ /* 013 */ "Number too large",
+ /* 014 */ "Error while verifing name constraints",
+ /* 015 */ "Path is too long, failed to find valid anchor",
+ /* 016 */ "Required keyusage for this certificate is missing",
+ /* 017 */ "Certificate not found",
+ /* 018 */ "Unknown lock command",
+ /* 019 */ "Parent certificate is a CA",
+ /* 020 */ "Extra data was found after the structure",
+ /* 021 */ "Proxy certificate is invalid",
+ /* 022 */ "Proxy certificate name is wrong",
+ /* 023 */ "Name is malformated",
+ /* 024 */ "Certificate is malformated",
+ /* 025 */ "Certificate is missing a required EKU",
+ /* 026 */ "Proxy certificate not canonicalize",
+ /* 027 */ "Reserved hx error (27)",
+ /* 028 */ "Reserved hx error (28)",
+ /* 029 */ "Reserved hx error (29)",
+ /* 030 */ "Reserved hx error (30)",
+ /* 031 */ "Reserved hx error (31)",
+ /* 032 */ "Failed to create signature",
+ /* 033 */ "Missing signer data",
+ /* 034 */ "Couldn't find signers certificate",
+ /* 035 */ "No data to perform the operation on",
+ /* 036 */ "Data in the message is invalid",
+ /* 037 */ "Padding in the message invalid",
+ /* 038 */ "Couldn't find recipient certificate",
+ /* 039 */ "Mismatch bewteen signed type and unsigned type",
+ /* 040 */ "Reserved hx error (40)",
+ /* 041 */ "Reserved hx error (41)",
+ /* 042 */ "Reserved hx error (42)",
+ /* 043 */ "Reserved hx error (43)",
+ /* 044 */ "Reserved hx error (44)",
+ /* 045 */ "Reserved hx error (45)",
+ /* 046 */ "Reserved hx error (46)",
+ /* 047 */ "Reserved hx error (47)",
+ /* 048 */ "Reserved hx error (48)",
+ /* 049 */ "Reserved hx error (49)",
+ /* 050 */ "Reserved hx error (50)",
+ /* 051 */ "Reserved hx error (51)",
+ /* 052 */ "Reserved hx error (52)",
+ /* 053 */ "Reserved hx error (53)",
+ /* 054 */ "Reserved hx error (54)",
+ /* 055 */ "Reserved hx error (55)",
+ /* 056 */ "Reserved hx error (56)",
+ /* 057 */ "Reserved hx error (57)",
+ /* 058 */ "Reserved hx error (58)",
+ /* 059 */ "Reserved hx error (59)",
+ /* 060 */ "Reserved hx error (60)",
+ /* 061 */ "Reserved hx error (61)",
+ /* 062 */ "Reserved hx error (62)",
+ /* 063 */ "Reserved hx error (63)",
+ /* 064 */ "Internal error in the crypto engine",
+ /* 065 */ "External error in the crypto engine",
+ /* 066 */ "Signature missing for data",
+ /* 067 */ "Signature is not valid",
+ /* 068 */ "Sigature doesn't provide confidentiality",
+ /* 069 */ "Invalid format on signature",
+ /* 070 */ "Mismatch bewteen oids",
+ /* 071 */ "No prompter function defined",
+ /* 072 */ "Signature require signer, but non available",
+ /* 073 */ "RSA public encyption failed",
+ /* 074 */ "RSA public encyption failed",
+ /* 075 */ "RSA private decryption failed",
+ /* 076 */ "RSA private decryption failed",
+ /* 077 */ "Reserved hx error (77)",
+ /* 078 */ "Reserved hx error (78)",
+ /* 079 */ "Reserved hx error (79)",
+ /* 080 */ "Reserved hx error (80)",
+ /* 081 */ "Reserved hx error (81)",
+ /* 082 */ "Reserved hx error (82)",
+ /* 083 */ "Reserved hx error (83)",
+ /* 084 */ "Reserved hx error (84)",
+ /* 085 */ "Reserved hx error (85)",
+ /* 086 */ "Reserved hx error (86)",
+ /* 087 */ "Reserved hx error (87)",
+ /* 088 */ "Reserved hx error (88)",
+ /* 089 */ "Reserved hx error (89)",
+ /* 090 */ "Reserved hx error (90)",
+ /* 091 */ "Reserved hx error (91)",
+ /* 092 */ "Reserved hx error (92)",
+ /* 093 */ "Reserved hx error (93)",
+ /* 094 */ "Reserved hx error (94)",
+ /* 095 */ "Reserved hx error (95)",
+ /* 096 */ "CRL used before it became valid",
+ /* 097 */ "CRL used after it became invalid",
+ /* 098 */ "CRL have invalid format",
+ /* 099 */ "Certificate is included in CRL",
+ /* 100 */ "No revoke status found for certificates",
+ /* 101 */ "Unknown extension",
+ /* 102 */ "Got wrong CRL/OCSP data from server",
+ /* 103 */ "Doesn't have same parent as other certificaes",
+ /* 104 */ "Reserved hx error (104)",
+ /* 105 */ "Reserved hx error (105)",
+ /* 106 */ "Reserved hx error (106)",
+ /* 107 */ "Reserved hx error (107)",
+ /* 108 */ "No local key attribute",
+ /* 109 */ "Failed to parse key",
+ /* 110 */ "Unsupported operation",
+ /* 111 */ "Unimplemented operation",
+ /* 112 */ "Failed to parse name",
+ /* 113 */ "Reserved hx error (113)",
+ /* 114 */ "Reserved hx error (114)",
+ /* 115 */ "Reserved hx error (115)",
+ /* 116 */ "Reserved hx error (116)",
+ /* 117 */ "Reserved hx error (117)",
+ /* 118 */ "Reserved hx error (118)",
+ /* 119 */ "Reserved hx error (119)",
+ /* 120 */ "Reserved hx error (120)",
+ /* 121 */ "Reserved hx error (121)",
+ /* 122 */ "Reserved hx error (122)",
+ /* 123 */ "Reserved hx error (123)",
+ /* 124 */ "Reserved hx error (124)",
+ /* 125 */ "Reserved hx error (125)",
+ /* 126 */ "Reserved hx error (126)",
+ /* 127 */ "Reserved hx error (127)",
+ /* 128 */ "No smartcard reader/device found",
+ /* 129 */ "No smartcard in reader",
+ /* 130 */ "No supported mech(s)",
+ /* 131 */ "Token or slot failed in inconsistent way",
+ /* 132 */ "Failed to open session to slot",
+ /* 133 */ "Failed to login to slot",
+ /* 134 */ "Failed to load PKCS module",
+ NULL
+};
+
+#define num_errors 135
+
+void initialize_hx_error_table_r(struct et_list **list)
+{
+ initialize_error_table_r(list, hx_error_strings, num_errors, ERROR_TABLE_BASE_hx);
+}
+
+void initialize_hx_error_table(void)
+{
+ init_error_table(hx_error_strings, ERROR_TABLE_BASE_hx, num_errors);
+}
diff --git a/source4/heimdal/lib/hx509/hx509_err.et b/source4/heimdal/lib/hx509/hx509_err.et
new file mode 100644
index 0000000000..54ec177e47
--- /dev/null
+++ b/source4/heimdal/lib/hx509/hx509_err.et
@@ -0,0 +1,100 @@
+#
+# Error messages for the hx509 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id: hx509_err.et,v 1.19 2006/12/30 23:05:39 lha Exp $"
+
+error_table hx
+prefix HX509
+
+# path validateion and construction related errors
+error_code BAD_TIMEFORMAT, "ASN.1 failed call to system time library"
+error_code EXTENSION_NOT_FOUND, "Extension not found"
+error_code NO_PATH, "Certification path not found"
+error_code PARENT_NOT_CA, "Parent certificate is not a CA"
+error_code CA_PATH_TOO_DEEP, "CA path too deep"
+error_code SIG_ALG_NO_SUPPORTED, "Signature algorithm not supported"
+error_code SIG_ALG_DONT_MATCH_KEY_ALG, "Signature algorithm doesn't match certificate key"
+error_code CERT_USED_BEFORE_TIME, "Certificate used before it became valid"
+error_code CERT_USED_AFTER_TIME, "Certificate used after it became invalid"
+error_code PRIVATE_KEY_MISSING, "Private key required for the operation is missing"
+error_code ALG_NOT_SUPP, "Algorithm not supported"
+error_code ISSUER_NOT_FOUND, "Issuer couldn't be found"
+error_code VERIFY_CONSTRAINTS, "Error verifing constraints"
+error_code RANGE, "Number too large"
+error_code NAME_CONSTRAINT_ERROR, "Error while verifing name constraints"
+error_code PATH_TOO_LONG, "Path is too long, failed to find valid anchor"
+error_code KU_CERT_MISSING, "Required keyusage for this certificate is missing"
+error_code CERT_NOT_FOUND, "Certificate not found"
+error_code UNKNOWN_LOCK_COMMAND, "Unknown lock command"
+error_code PARENT_IS_CA, "Parent certificate is a CA"
+error_code EXTRA_DATA_AFTER_STRUCTURE, "Extra data was found after the structure"
+error_code PROXY_CERT_INVALID, "Proxy certificate is invalid"
+error_code PROXY_CERT_NAME_WRONG, "Proxy certificate name is wrong"
+error_code NAME_MALFORMED, "Name is malformated"
+error_code CERTIFICATE_MALFORMED, "Certificate is malformated"
+error_code CERTIFICATE_MISSING_EKU, "Certificate is missing a required EKU"
+error_code PROXY_CERTIFICATE_NOT_CANONICALIZED, "Proxy certificate not canonicalize"
+
+# cms related errors
+index 32
+prefix HX509_CMS
+error_code FAILED_CREATE_SIGATURE, "Failed to create signature"
+error_code MISSING_SIGNER_DATA, "Missing signer data"
+error_code SIGNER_NOT_FOUND, "Couldn't find signers certificate"
+error_code NO_DATA_AVAILABLE, "No data to perform the operation on"
+error_code INVALID_DATA, "Data in the message is invalid"
+error_code PADDING_ERROR, "Padding in the message invalid"
+error_code NO_RECIPIENT_CERTIFICATE, "Couldn't find recipient certificate"
+error_code DATA_OID_MISMATCH, "Mismatch bewteen signed type and unsigned type"
+
+# crypto related errors
+index 64
+prefix HX509_CRYPTO
+error_code INTERNAL_ERROR, "Internal error in the crypto engine"
+error_code EXTERNAL_ERROR, "External error in the crypto engine"
+error_code SIGNATURE_MISSING, "Signature missing for data"
+error_code BAD_SIGNATURE, "Signature is not valid"
+error_code SIG_NO_CONF, "Sigature doesn't provide confidentiality"
+error_code SIG_INVALID_FORMAT, "Invalid format on signature"
+error_code OID_MISMATCH, "Mismatch bewteen oids"
+error_code NO_PROMPTER, "No prompter function defined"
+error_code SIGNATURE_WITHOUT_SIGNER, "Signature require signer, but non available"
+error_code RSA_PUBLIC_ENCRYPT, "RSA public encyption failed"
+error_code RSA_PRIVATE_ENCRYPT, "RSA public encyption failed"
+error_code RSA_PUBLIC_DECRYPT, "RSA private decryption failed"
+error_code RSA_PRIVATE_DECRYPT, "RSA private decryption failed"
+
+# revoke related errors
+index 96
+prefix HX509
+error_code CRL_USED_BEFORE_TIME, "CRL used before it became valid"
+error_code CRL_USED_AFTER_TIME, "CRL used after it became invalid"
+error_code CRL_INVALID_FORMAT, "CRL have invalid format"
+error_code CRL_CERT_REVOKED, "Certificate is included in CRL"
+error_code REVOKE_STATUS_MISSING, "No revoke status found for certificates"
+error_code CRL_UNKNOWN_EXTENSION, "Unknown extension"
+error_code REVOKE_WRONG_DATA, "Got wrong CRL/OCSP data from server"
+error_code REVOKE_NOT_SAME_PARENT, "Doesn't have same parent as other certificaes"
+
+# misc error
+index 108
+error_code LOCAL_ATTRIBUTE_MISSING, "No local key attribute"
+error_code PARSING_KEY_FAILED, "Failed to parse key"
+error_code UNSUPPORTED_OPERATION, "Unsupported operation"
+error_code UNIMPLEMENTED_OPERATION, "Unimplemented operation"
+error_code PARSING_NAME_FAILED, "Failed to parse name"
+
+# keystore related error
+index 128
+prefix HX509_PKCS11
+error_code NO_SLOT, "No smartcard reader/device found"
+error_code NO_TOKEN, "No smartcard in reader"
+error_code NO_MECH, "No supported mech(s)"
+error_code TOKEN_CONFUSED, "Token or slot failed in inconsistent way"
+error_code OPEN_SESSION, "Failed to open session to slot"
+error_code LOGIN, "Failed to login to slot"
+error_code LOAD, "Failed to load PKCS module"
+
+end
diff --git a/source4/heimdal/lib/hx509/hx_locl.h b/source4/heimdal/lib/hx509/hx_locl.h
new file mode 100644
index 0000000000..78d158f8b1
--- /dev/null
+++ b/source4/heimdal/lib/hx509/hx_locl.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2004 - 2006 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.
+ */
+
+/* $Id: hx_locl.h,v 1.30 2007/01/09 10:52:06 lha Exp $ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <strings.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <err.h>
+#include <getarg.h>
+#include <base64.h>
+#include <hex.h>
+#include <roken.h>
+#include <com_err.h>
+#include <parse_units.h>
+#include <parse_bytes.h>
+
+#include <krb5-types.h>
+
+#include <rfc2459_asn1.h>
+#include <cms_asn1.h>
+#include <pkcs8_asn1.h>
+#include <pkcs9_asn1.h>
+#include <pkcs12_asn1.h>
+#include <ocsp_asn1.h>
+#include <pkcs10_asn1.h>
+#include <asn1_err.h>
+#include <pkinit_asn1.h>
+
+#include <der.h>
+
+#include "crypto-headers.h"
+
+struct hx509_keyset_ops;
+struct hx509_collector;
+typedef struct hx509_path hx509_path;
+
+#include <hx509.h>
+
+typedef void (*_hx509_cert_release_func)(struct hx509_cert_data *, void *);
+
+typedef struct hx509_private_key_ops hx509_private_key_ops;
+
+#include <hx509-private.h>
+#include <hx509_err.h>
+
+struct hx509_peer_info {
+ hx509_cert cert;
+ AlgorithmIdentifier *val;
+ size_t len;
+};
+
+#define HX509_CERTS_FIND_SERIALNUMBER 1
+#define HX509_CERTS_FIND_ISSUER 2
+#define HX509_CERTS_FIND_SUBJECT 4
+#define HX509_CERTS_FIND_ISSUER_KEY_ID 8
+#define HX509_CERTS_FIND_SUBJECT_KEY_ID 16
+
+struct hx509_name_data {
+ Name der_name;
+};
+
+struct hx509_path {
+ size_t len;
+ hx509_cert *val;
+};
+
+struct hx509_query_data {
+ int match;
+#define HX509_QUERY_FIND_ISSUER_CERT 0x000001
+#define HX509_QUERY_MATCH_SERIALNUMBER 0x000002
+#define HX509_QUERY_MATCH_ISSUER_NAME 0x000004
+#define HX509_QUERY_MATCH_SUBJECT_NAME 0x000008
+#define HX509_QUERY_MATCH_SUBJECT_KEY_ID 0x000010
+#define HX509_QUERY_MATCH_ISSUER_ID 0x000020
+#define HX509_QUERY_PRIVATE_KEY 0x000040
+#define HX509_QUERY_KU_ENCIPHERMENT 0x000080
+#define HX509_QUERY_KU_DIGITALSIGNATURE 0x000100
+#define HX509_QUERY_KU_KEYCERTSIGN 0x000200
+#define HX509_QUERY_KU_CRLSIGN 0x000400
+#define HX509_QUERY_KU_NONREPUDIATION 0x000800
+#define HX509_QUERY_KU_KEYAGREEMENT 0x001000
+#define HX509_QUERY_KU_DATAENCIPHERMENT 0x002000
+#define HX509_QUERY_ANCHOR 0x004000
+#define HX509_QUERY_MATCH_CERTIFICATE 0x008000
+#define HX509_QUERY_MATCH_LOCAL_KEY_ID 0x010000
+#define HX509_QUERY_NO_MATCH_PATH 0x020000
+#define HX509_QUERY_MATCH_FRIENDLY_NAME 0x040000
+#define HX509_QUERY_MATCH_FUNCTION 0x080000
+#define HX509_QUERY_MATCH_KEY_HASH_SHA1 0x100000
+#define HX509_QUERY_MATCH_TIME 0x200000
+#define HX509_QUERY_MASK 0x3fffff
+ Certificate *subject;
+ Certificate *certificate;
+ heim_integer *serial;
+ heim_octet_string *subject_id;
+ heim_octet_string *local_key_id;
+ Name *issuer_name;
+ Name *subject_name;
+ hx509_path *path;
+ char *friendlyname;
+ int (*cmp_func)(void *, hx509_cert);
+ void *cmp_func_ctx;
+ heim_octet_string *keyhash_sha1;
+ time_t timenow;
+};
+
+struct hx509_keyset_ops {
+ char *name;
+ int flags;
+ int (*init)(hx509_context, hx509_certs, void **,
+ int, const char *, hx509_lock);
+ int (*store)(hx509_context, hx509_certs, void *, int, hx509_lock);
+ int (*free)(hx509_certs, void *);
+ int (*add)(hx509_context, hx509_certs, void *, hx509_cert);
+ int (*query)(hx509_context, hx509_certs, void *,
+ const hx509_query *, hx509_cert *);
+ int (*iter_start)(hx509_context, hx509_certs, void *, void **);
+ int (*iter)(hx509_context, hx509_certs, void *, void *, hx509_cert *);
+ int (*iter_end)(hx509_context, hx509_certs, void *, void *);
+ int (*printinfo)(hx509_context, hx509_certs,
+ void *, int (*)(void *, char *), void *);
+ int (*getkeys)(hx509_context, hx509_certs, void *, hx509_private_key **);
+ int (*addkey)(hx509_context, hx509_certs, void *, hx509_private_key);
+};
+
+struct _hx509_password {
+ size_t len;
+ char **val;
+};
+
+extern hx509_lock _hx509_empty_lock;
+
+struct hx509_context_data {
+ struct hx509_keyset_ops **ks_ops;
+ int ks_num_ops;
+ int flags;
+#define HX509_CTX_VERIFY_MISSING_OK 1
+ int ocsp_time_diff;
+#define HX509_DEFAULT_OCSP_TIME_DIFF (5*60)
+ hx509_error error;
+ struct et_list *et_list;
+};
+
+/* _hx509_calculate_path flag field */
+#define HX509_CALCULATE_PATH_NO_ANCHOR 1
diff --git a/source4/heimdal/lib/hx509/keyset.c b/source4/heimdal/lib/hx509/keyset.c
new file mode 100644
index 0000000000..c3d5ee210c
--- /dev/null
+++ b/source4/heimdal/lib/hx509/keyset.c
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2004 - 2006 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: keyset.c,v 1.18 2007/01/09 10:52:07 lha Exp $");
+
+struct hx509_certs_data {
+ struct hx509_keyset_ops *ops;
+ void *ops_data;
+};
+
+static struct hx509_keyset_ops *
+_hx509_ks_type(hx509_context context, const char *type)
+{
+ int i;
+
+ for (i = 0; i < context->ks_num_ops; i++)
+ if (strcasecmp(type, context->ks_ops[i]->name) == 0)
+ return context->ks_ops[i];
+
+ return NULL;
+}
+
+void
+_hx509_ks_register(hx509_context context, struct hx509_keyset_ops *ops)
+{
+ struct hx509_keyset_ops **val;
+
+ if (_hx509_ks_type(context, ops->name))
+ return;
+
+ val = realloc(context->ks_ops,
+ (context->ks_num_ops + 1) * sizeof(context->ks_ops[0]));
+ if (val == NULL)
+ return;
+ val[context->ks_num_ops] = ops;
+ context->ks_ops = val;
+ context->ks_num_ops++;
+}
+
+int
+hx509_certs_init(hx509_context context,
+ const char *name, int flags,
+ hx509_lock lock, hx509_certs *certs)
+{
+ struct hx509_keyset_ops *ops;
+ const char *residue;
+ hx509_certs c;
+ char *type;
+ int ret;
+
+ *certs = NULL;
+
+ residue = strchr(name, ':');
+ if (residue) {
+ type = malloc(residue - name + 1);
+ if (type)
+ strlcpy(type, name, residue - name + 1);
+ residue++;
+ if (residue[0] == '\0')
+ residue = NULL;
+ } else {
+ type = strdup("MEMORY");
+ residue = name;
+ }
+ if (type == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ops = _hx509_ks_type(context, type);
+ free(type);
+ if (ops == NULL) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Keyset type %s is not supported", type);
+ return ENOENT;
+ }
+ c = calloc(1, sizeof(*c));
+ if (c == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ c->ops = ops;
+
+ ret = (*ops->init)(context, c, &c->ops_data, flags, residue, lock);
+ if (ret) {
+ free(c);
+ return ret;
+ }
+
+ *certs = c;
+ return 0;
+}
+
+int
+hx509_certs_store(hx509_context context,
+ hx509_certs certs,
+ int flags,
+ hx509_lock lock)
+{
+ if (certs->ops->store == NULL) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "keystore if type %s doesn't support "
+ "store operation",
+ certs->ops->name);
+ return EINVAL;
+ }
+
+ return (*certs->ops->store)(context, certs, certs->ops_data, flags, lock);
+}
+
+
+void
+hx509_certs_free(hx509_certs *certs)
+{
+ if (*certs) {
+ (*(*certs)->ops->free)(*certs, (*certs)->ops_data);
+ free(*certs);
+ *certs = NULL;
+ }
+}
+
+int
+hx509_certs_start_seq(hx509_context context,
+ hx509_certs certs,
+ hx509_cursor *cursor)
+{
+ int ret;
+
+ if (certs->ops->iter_start == NULL) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Keyset type %s doesn't support iteration",
+ certs->ops->name);
+ return ENOENT;
+ }
+
+ ret = (*certs->ops->iter_start)(context, certs, certs->ops_data, cursor);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int
+hx509_certs_next_cert(hx509_context context,
+ hx509_certs certs,
+ hx509_cursor cursor,
+ hx509_cert *cert)
+{
+ *cert = NULL;
+ return (*certs->ops->iter)(context, certs, certs->ops_data, cursor, cert);
+}
+
+int
+hx509_certs_end_seq(hx509_context context,
+ hx509_certs certs,
+ hx509_cursor cursor)
+{
+ (*certs->ops->iter_end)(context, certs, certs->ops_data, cursor);
+ return 0;
+}
+
+
+int
+hx509_certs_iter(hx509_context context,
+ hx509_certs certs,
+ int (*fn)(hx509_context, void *, hx509_cert),
+ void *ctx)
+{
+ hx509_cursor cursor;
+ hx509_cert c;
+ int ret;
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret)
+ return ret;
+
+ while (1) {
+ ret = hx509_certs_next_cert(context, certs, cursor, &c);
+ if (ret)
+ break;
+ if (c == NULL) {
+ ret = 0;
+ break;
+ }
+ ret = (*fn)(context, ctx, c);
+ hx509_cert_free(c);
+ if (ret)
+ break;
+ }
+
+ hx509_certs_end_seq(context, certs, cursor);
+
+ return ret;
+}
+
+int
+hx509_ci_print_names(hx509_context context, void *ctx, hx509_cert c)
+{
+ Certificate *cert;
+ hx509_name n;
+ char *s, *i;
+
+ cert = _hx509_get_cert(c);
+
+ _hx509_name_from_Name(&cert->tbsCertificate.subject, &n);
+ hx509_name_to_string(n, &s);
+ hx509_name_free(&n);
+ _hx509_name_from_Name(&cert->tbsCertificate.issuer, &n);
+ hx509_name_to_string(n, &i);
+ hx509_name_free(&n);
+ fprintf(ctx, "subject: %s\nissuer: %s\n", s, i);
+ free(s);
+ free(i);
+ return 0;
+}
+
+/*
+ * The receiving keyset `certs´ will either increase reference counter
+ * of the `cert´ or make a deep copy, either way, the caller needs to
+ * free the `cert´ itself.
+ */
+
+int
+hx509_certs_add(hx509_context context, hx509_certs certs, hx509_cert cert)
+{
+ if (certs->ops->add == NULL) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Keyset type %s doesn't support add operation",
+ certs->ops->name);
+ return ENOENT;
+ }
+
+ return (*certs->ops->add)(context, certs, certs->ops_data, cert);
+}
+
+int
+hx509_certs_find(hx509_context context,
+ hx509_certs certs,
+ const hx509_query *q,
+ hx509_cert *r)
+{
+ hx509_cursor cursor;
+ hx509_cert c;
+ int ret;
+
+ *r = NULL;
+
+ if (certs->ops->query)
+ return (*certs->ops->query)(context, certs, certs->ops_data, q, r);
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret)
+ return ret;
+
+ c = NULL;
+ while (1) {
+ ret = hx509_certs_next_cert(context, certs, cursor, &c);
+ if (ret)
+ break;
+ if (c == NULL)
+ break;
+ if (_hx509_query_match_cert(context, q, c)) {
+ *r = c;
+ break;
+ }
+ hx509_cert_free(c);
+ }
+
+ hx509_certs_end_seq(context, certs, cursor);
+ if (ret)
+ return ret;
+ if (c == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_CERT_NOT_FOUND;
+ }
+
+ return 0;
+}
+
+static int
+certs_merge_func(hx509_context context, void *ctx, hx509_cert c)
+{
+ return hx509_certs_add(context, (hx509_certs)ctx, c);
+}
+
+int
+hx509_certs_merge(hx509_context context, hx509_certs to, hx509_certs from)
+{
+ return hx509_certs_iter(context, from, certs_merge_func, to);
+}
+
+int
+hx509_certs_append(hx509_context context,
+ hx509_certs to,
+ hx509_lock lock,
+ const char *name)
+{
+ hx509_certs s;
+ int ret;
+
+ ret = hx509_certs_init(context, name, 0, lock, &s);
+ if (ret)
+ return ret;
+ ret = hx509_certs_merge(context, to, s);
+ hx509_certs_free(&s);
+ return ret;
+}
+
+int
+hx509_get_one_cert(hx509_context context, hx509_certs certs, hx509_cert *c)
+{
+ hx509_cursor cursor;
+ int ret;
+
+ *c = NULL;
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret)
+ return ret;
+
+ ret = hx509_certs_next_cert(context, certs, cursor, c);
+ if (ret)
+ return ret;
+
+ hx509_certs_end_seq(context, certs, cursor);
+ return 0;
+}
+
+static int
+certs_info_stdio(void *ctx, char *str)
+{
+ FILE *f = ctx;
+ fprintf(f, "%s\n", str);
+ return 0;
+}
+
+int
+hx509_certs_info(hx509_context context,
+ hx509_certs certs,
+ int (*func)(void *, char *),
+ void *ctx)
+{
+ if (func == NULL) {
+ func = certs_info_stdio;
+ if (ctx == NULL)
+ ctx = stdout;
+ }
+ if (certs->ops->printinfo == NULL) {
+ (*func)(ctx, "No info function for certs");
+ return 0;
+ }
+ return (*certs->ops->printinfo)(context, certs, certs->ops_data,
+ func, ctx);
+}
+
+void
+_hx509_pi_printf(int (*func)(void *, char *), void *ctx,
+ char *fmt, ...)
+{
+ va_list ap;
+ char *str;
+
+ va_start(ap, fmt);
+ vasprintf(&str, fmt, ap);
+ va_end(ap);
+ if (str == NULL)
+ return;
+ (*func)(ctx, str);
+ free(str);
+}
+
+int
+_hx509_certs_keys_get(hx509_context context,
+ hx509_certs certs,
+ hx509_private_key **keys)
+{
+ if (certs->ops->getkeys == NULL) {
+ *keys = NULL;
+ return 0;
+ }
+ return (*certs->ops->getkeys)(context, certs, certs->ops_data, keys);
+}
+
+int
+_hx509_certs_keys_add(hx509_context context,
+ hx509_certs certs,
+ hx509_private_key key)
+{
+ if (certs->ops->addkey == NULL) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "keystore if type %s doesn't support "
+ "key add operation",
+ certs->ops->name);
+ return EINVAL;
+ }
+ return (*certs->ops->addkey)(context, certs, certs->ops_data, key);
+}
+
+
+void
+_hx509_certs_keys_free(hx509_context context,
+ hx509_private_key *keys)
+{
+ int i;
+ for (i = 0; keys[i]; i++)
+ _hx509_private_key_free(&keys[i]);
+ free(keys);
+}
diff --git a/source4/heimdal/lib/hx509/ks_dir.c b/source4/heimdal/lib/hx509/ks_dir.c
new file mode 100644
index 0000000000..01dcf5795b
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_dir.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2006 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: ks_dir.c,v 1.7 2007/01/09 10:52:08 lha Exp $");
+#include <dirent.h>
+
+/*
+ * The DIR keyset module is strange compared to the other modules
+ * since it does lazy evaluation and really doesn't keep any local
+ * state except for the directory iteration and cert iteration of
+ * files. DIR ignores most errors so that the consumer doesn't get
+ * failes for stray files in directories.
+ */
+
+struct dircursor {
+ DIR *dir;
+ hx509_certs certs;
+ void *iter;
+};
+
+/*
+ *
+ */
+
+static int
+dir_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ *data = NULL;
+
+ {
+ struct stat sb;
+ int ret;
+
+ ret = stat(residue, &sb);
+ if (ret == -1) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "No such file %s", residue);
+ return ENOENT;
+ }
+
+ if ((sb.st_mode & S_IFDIR) == 0) {
+ hx509_set_error_string(context, 0, ENOTDIR,
+ "%s is not a directory", residue);
+ return ENOTDIR;
+ }
+ }
+
+ *data = strdup(residue);
+ if (*data == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+static int
+dir_free(hx509_certs certs, void *data)
+{
+ free(data);
+ return 0;
+}
+
+
+
+static int
+dir_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ struct dircursor *d;
+
+ *cursor = NULL;
+
+ d = calloc(1, sizeof(*d));
+ if (d == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ d->dir = opendir(data);
+ if (d->dir == NULL) {
+ hx509_clear_error_string(context);
+ free(d);
+ return errno;
+ }
+ d->certs = NULL;
+ d->iter = NULL;
+
+ *cursor = d;
+ return 0;
+}
+
+static int
+dir_iter(hx509_context context,
+ hx509_certs certs, void *data, void *iter, hx509_cert *cert)
+{
+ struct dircursor *d = iter;
+ int ret = 0;
+
+ *cert = NULL;
+
+ do {
+ struct dirent *dir;
+ char *fn;
+
+ if (d->certs) {
+ ret = hx509_certs_next_cert(context, d->certs, d->iter, cert);
+ if (ret) {
+ hx509_certs_end_seq(context, d->certs, d->iter);
+ d->iter = NULL;
+ hx509_certs_free(&d->certs);
+ return ret;
+ }
+ if (*cert) {
+ ret = 0;
+ break;
+ }
+ hx509_certs_end_seq(context, d->certs, d->iter);
+ d->iter = NULL;
+ hx509_certs_free(&d->certs);
+ }
+
+ dir = readdir(d->dir);
+ if (dir == NULL) {
+ ret = 0;
+ break;
+ }
+ if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0)
+ continue;
+
+ if (asprintf(&fn, "FILE:%s/%s", (char *)data, dir->d_name) == -1)
+ return ENOMEM;
+
+ ret = hx509_certs_init(context, fn, 0, NULL, &d->certs);
+ if (ret == 0) {
+
+ ret = hx509_certs_start_seq(context, d->certs, &d->iter);
+ if (ret)
+ hx509_certs_free(&d->certs);
+ }
+ /* ignore errors */
+ if (ret) {
+ d->certs = NULL;
+ ret = 0;
+ }
+
+ free(fn);
+ } while(ret == 0);
+
+ return ret;
+}
+
+
+static int
+dir_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ struct dircursor *d = cursor;
+
+ if (d->certs) {
+ hx509_certs_end_seq(context, d->certs, d->iter);
+ d->iter = NULL;
+ hx509_certs_free(&d->certs);
+ }
+ closedir(d->dir);
+ free(d);
+ return 0;
+}
+
+
+static struct hx509_keyset_ops keyset_dir = {
+ "DIR",
+ 0,
+ dir_init,
+ NULL,
+ dir_free,
+ NULL,
+ NULL,
+ dir_iter_start,
+ dir_iter,
+ dir_iter_end
+};
+
+void
+_hx509_ks_dir_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_dir);
+}
diff --git a/source4/heimdal/lib/hx509/ks_file.c b/source4/heimdal/lib/hx509/ks_file.c
new file mode 100644
index 0000000000..db0f475129
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_file.c
@@ -0,0 +1,794 @@
+/*
+ * Copyright (c) 2005 - 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: ks_file.c,v 1.31 2007/01/09 10:52:08 lha Exp $");
+
+struct ks_file {
+ hx509_certs certs;
+ char *fn;
+};
+
+struct header {
+ char *header;
+ char *value;
+ struct header *next;
+};
+
+static int
+add_headers(struct header **headers, const char *header, const char *value)
+{
+ struct header *h;
+ h = calloc(1, sizeof(*h));
+ if (h == NULL)
+ return ENOMEM;
+ h->header = strdup(header);
+ if (h->header == NULL) {
+ free(h);
+ return ENOMEM;
+ }
+ h->value = strdup(value);
+ if (h->value == NULL) {
+ free(h->header);
+ free(h);
+ return ENOMEM;
+ }
+
+ h->next = *headers;
+ *headers = h;
+
+ return 0;
+}
+
+static void
+free_headers(struct header *headers)
+{
+ struct header *h;
+ while (headers) {
+ h = headers;
+ headers = headers->next;
+ free(h->header);
+ free(h->value);
+ free(h);
+ }
+}
+
+static const char *
+find_header(const struct header *headers, const char *header)
+{
+ while(headers) {
+ if (strcmp(header, headers->header) == 0)
+ return headers->value;
+ headers = headers->next;
+ }
+ return NULL;
+}
+
+/*
+ *
+ */
+
+static int
+parse_certificate(hx509_context context, const char *fn,
+ struct hx509_collector *c,
+ const struct header *headers,
+ const void *data, size_t len)
+{
+ hx509_cert cert;
+ Certificate t;
+ size_t size;
+ int ret;
+
+ ret = decode_Certificate(data, len, &t, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to parse certificate in %s",
+ fn);
+ return ret;
+ }
+
+ ret = hx509_cert_init(context, &t, &cert);
+ free_Certificate(&t);
+ if (ret)
+ return ret;
+
+ ret = _hx509_collector_certs_add(context, c, cert);
+ hx509_cert_free(cert);
+ return ret;
+}
+
+static int
+try_decrypt(hx509_context context,
+ struct hx509_collector *collector,
+ const AlgorithmIdentifier *alg,
+ const EVP_CIPHER *c,
+ const void *ivdata,
+ const void *password,
+ size_t passwordlen,
+ const void *cipher,
+ size_t len)
+{
+ heim_octet_string clear;
+ size_t keylen;
+ void *key;
+ int ret;
+
+ keylen = EVP_CIPHER_key_length(c);
+
+ key = malloc(keylen);
+ if (key == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ret = EVP_BytesToKey(c, EVP_md5(), ivdata,
+ password, passwordlen,
+ 1, key, NULL);
+ if (ret <= 0) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR,
+ "Failed to do string2key for private key");
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+
+ clear.data = malloc(len);
+ if (clear.data == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "Out of memory to decrypt for private key");
+ ret = ENOMEM;
+ goto out;
+ }
+ clear.length = len;
+
+ {
+ EVP_CIPHER_CTX ctx;
+ EVP_CIPHER_CTX_init(&ctx);
+ EVP_CipherInit_ex(&ctx, c, NULL, key, ivdata, 0);
+ EVP_Cipher(&ctx, clear.data, cipher, len);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ }
+
+ ret = _hx509_collector_private_key_add(context,
+ collector,
+ alg,
+ NULL,
+ &clear,
+ NULL);
+
+ memset(clear.data, 0, clear.length);
+ free(clear.data);
+out:
+ memset(key, 0, keylen);
+ free(key);
+ return ret;
+}
+
+static int
+parse_rsa_private_key(hx509_context context, const char *fn,
+ struct hx509_collector *c,
+ const struct header *headers,
+ const void *data, size_t len)
+{
+ int ret = 0;
+ const char *enc;
+
+ enc = find_header(headers, "Proc-Type");
+ if (enc) {
+ const char *dek;
+ char *type, *iv;
+ ssize_t ssize, size;
+ void *ivdata;
+ const EVP_CIPHER *cipher;
+ const struct _hx509_password *pw;
+ hx509_lock lock;
+ int i, decrypted = 0;
+
+ lock = _hx509_collector_get_lock(c);
+ if (lock == NULL) {
+ hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
+ "Failed to get password for "
+ "password protected file %s", fn);
+ return HX509_ALG_NOT_SUPP;
+ }
+
+ if (strcmp(enc, "4,ENCRYPTED") != 0) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "RSA key encrypted in unknown method %s "
+ "in file",
+ enc, fn);
+ hx509_clear_error_string(context);
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ dek = find_header(headers, "DEK-Info");
+ if (dek == NULL) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Encrypted RSA missing DEK-Info");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ type = strdup(dek);
+ if (type == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ iv = strchr(type, ',');
+ if (iv)
+ *iv++ = '\0';
+
+ size = strlen(iv);
+ ivdata = malloc(size);
+ if (ivdata == NULL) {
+ hx509_clear_error_string(context);
+ free(type);
+ return ENOMEM;
+ }
+
+ cipher = EVP_get_cipherbyname(type);
+ if (cipher == NULL) {
+ free(ivdata);
+ hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
+ "RSA key encrypted with "
+ "unsupported cipher: %s",
+ type);
+ free(type);
+ return HX509_ALG_NOT_SUPP;
+ }
+
+#define PKCS5_SALT_LEN 8
+
+ ssize = hex_decode(iv, ivdata, size);
+ free(type);
+ type = NULL;
+ iv = NULL;
+
+ if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) {
+ free(ivdata);
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Salt have wrong length in RSA key file");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ pw = _hx509_lock_get_passwords(lock);
+ if (pw != NULL) {
+ const void *password;
+ size_t passwordlen;
+
+ for (i = 0; i < pw->len; i++) {
+ password = pw->val[i];
+ passwordlen = strlen(password);
+
+ ret = try_decrypt(context, c, hx509_signature_rsa(),
+ cipher, ivdata, password, passwordlen,
+ data, len);
+ if (ret == 0) {
+ decrypted = 1;
+ break;
+ }
+ }
+ }
+ if (!decrypted) {
+ hx509_prompt prompt;
+ char password[128];
+
+ memset(&prompt, 0, sizeof(prompt));
+
+ prompt.prompt = "Password for keyfile: ";
+ prompt.type = HX509_PROMPT_TYPE_PASSWORD;
+ prompt.reply.data = password;
+ prompt.reply.length = sizeof(password);
+
+ ret = hx509_lock_prompt(lock, &prompt);
+ if (ret == 0)
+ ret = try_decrypt(context, c, hx509_signature_rsa(),
+ cipher, ivdata, password, strlen(password),
+ data, len);
+ /* XXX add password to lock password collection ? */
+ memset(password, 0, sizeof(password));
+ }
+ free(ivdata);
+
+ } else {
+ heim_octet_string keydata;
+
+ keydata.data = rk_UNCONST(data);
+ keydata.length = len;
+
+ ret = _hx509_collector_private_key_add(context,
+ c,
+ hx509_signature_rsa(),
+ NULL,
+ &keydata,
+ NULL);
+ }
+
+ return ret;
+}
+
+
+struct pem_formats {
+ const char *name;
+ int (*func)(hx509_context, const char *, struct hx509_collector *,
+ const struct header *, const void *, size_t);
+} formats[] = {
+ { "CERTIFICATE", parse_certificate },
+ { "RSA PRIVATE KEY", parse_rsa_private_key }
+};
+
+
+static int
+parse_pem_file(hx509_context context,
+ const char *fn,
+ struct hx509_collector *c,
+ int *found_data)
+{
+ struct header *headers = NULL;
+ char *type = NULL;
+ void *data = NULL;
+ size_t len = 0;
+ char buf[1024];
+ int ret;
+ FILE *f;
+
+
+ enum { BEFORE, SEARCHHEADER, INHEADER, INDATA, DONE } where;
+
+ where = BEFORE;
+ *found_data = 0;
+
+ if ((f = fopen(fn, "r")) == NULL) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Failed to open PEM file \"%s\": %s",
+ fn, strerror(errno));
+ return ENOENT;
+ }
+ ret = 0;
+
+ while (fgets(buf, sizeof(buf), f) != NULL) {
+ char *p;
+ int i;
+
+ i = strcspn(buf, "\n");
+ if (buf[i] == '\n') {
+ buf[i] = '\0';
+ if (i > 0)
+ i--;
+ }
+ if (buf[i] == '\r') {
+ buf[i] = '\0';
+ if (i > 0)
+ i--;
+ }
+
+ switch (where) {
+ case BEFORE:
+ if (strncmp("-----BEGIN ", buf, 11) == 0) {
+ type = strdup(buf + 11);
+ if (type == NULL)
+ break;
+ p = strchr(type, '-');
+ if (p)
+ *p = '\0';
+ *found_data = 1;
+ where = SEARCHHEADER;
+ }
+ break;
+ case SEARCHHEADER:
+ p = strchr(buf, ':');
+ if (p == NULL) {
+ where = INDATA;
+ goto indata;
+ }
+ /* FALLTHOUGH */
+ case INHEADER:
+ if (buf[0] == '\0') {
+ where = INDATA;
+ break;
+ }
+ p = strchr(buf, ':');
+ if (p) {
+ *p++ = '\0';
+ while (isspace((int)*p))
+ p++;
+ add_headers(&headers, buf, p);
+ }
+ break;
+ case INDATA:
+ indata:
+
+ if (strncmp("-----END ", buf, 9) == 0) {
+ where = DONE;
+ break;
+ }
+
+ p = emalloc(i);
+ i = base64_decode(buf, p);
+ if (i < 0) {
+ free(p);
+ goto out;
+ }
+
+ data = erealloc(data, len + i);
+ memcpy(((char *)data) + len, p, i);
+ free(p);
+ len += i;
+ break;
+ case DONE:
+ abort();
+ }
+
+ if (where == DONE) {
+ int j;
+
+ for (j = 0; j < sizeof(formats)/sizeof(formats[0]); j++) {
+ const char *q = formats[j].name;
+ if (strcasecmp(type, q) == 0) {
+ ret = (*formats[j].func)(context, fn, c,
+ headers, data, len);
+ break;
+ }
+ }
+ if (j == sizeof(formats)/sizeof(formats[0])) {
+ ret = HX509_UNSUPPORTED_OPERATION;
+ hx509_set_error_string(context, 0, ret,
+ "Found no matching PEM format for %s",
+ type);
+ }
+ out:
+ free(data);
+ data = NULL;
+ len = 0;
+ free(type);
+ type = NULL;
+ where = BEFORE;
+ free_headers(headers);
+ headers = NULL;
+ if (ret)
+ break;
+ }
+ }
+
+ fclose(f);
+
+ if (where != BEFORE) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "File ends before end of PEM end tag");
+ ret = HX509_PARSING_KEY_FAILED;
+ }
+ if (data)
+ free(data);
+ if (type)
+ free(type);
+ if (headers)
+ free_headers(headers);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int
+file_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ char *p, *pnext;
+ struct ks_file *f = NULL;
+ struct hx509_collector *c = NULL;
+ hx509_private_key *keys = NULL;
+ int ret;
+
+ *data = NULL;
+
+ if (lock == NULL)
+ lock = _hx509_empty_lock;
+
+ f = calloc(1, sizeof(*f));
+ if (f == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ f->fn = strdup(residue);
+ if (f->fn == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ /*
+ * XXX this is broken, the function should parse the file before
+ * overwriting it
+ */
+
+ if (flags & HX509_CERTS_CREATE) {
+ ret = hx509_certs_init(context, "MEMORY:ks-file-create",
+ 0, lock, &f->certs);
+ if (ret)
+ goto out;
+ *data = f;
+ return 0;
+ }
+
+ c = _hx509_collector_alloc(context, lock);
+ if (c == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ for (p = f->fn; p != NULL; p = pnext) {
+ int found_data;
+
+ pnext = strchr(p, ',');
+ if (pnext)
+ *pnext++ = '\0';
+
+ ret = parse_pem_file(context, p, c, &found_data);
+ if (ret)
+ goto out;
+
+ if (!found_data) {
+ size_t length;
+ void *ptr;
+ int i;
+
+ ret = _hx509_map_file(p, &ptr, &length, NULL);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) {
+ ret = (*formats[i].func)(context, p, c, NULL, ptr, length);
+ if (ret == 0)
+ break;
+ }
+ _hx509_unmap_file(ptr, length);
+ if (ret)
+ goto out;
+ }
+ }
+
+ ret = _hx509_collector_collect_certs(context, c, &f->certs);
+ if (ret)
+ goto out;
+
+ ret = _hx509_collector_collect_private_keys(context, c, &keys);
+ if (ret == 0) {
+ int i;
+
+ for (i = 0; keys[i]; i++)
+ _hx509_certs_keys_add(context, f->certs, keys[i]);
+ _hx509_certs_keys_free(context, keys);
+ }
+
+out:
+ if (ret == 0)
+ *data = f;
+ else {
+ if (f->fn)
+ free(f->fn);
+ free(f);
+ }
+ if (c)
+ _hx509_collector_free(c);
+ return ret;
+}
+
+static int
+file_free(hx509_certs certs, void *data)
+{
+ struct ks_file *f = data;
+ hx509_certs_free(&f->certs);
+ free(f->fn);
+ free(f);
+ return 0;
+}
+
+static void
+pem_header(FILE *f, const char *type, const char *str)
+{
+ fprintf(f, "-----%s %s-----\n", type, str);
+}
+
+static int
+dump_pem_file(hx509_context context, const char *header,
+ FILE *f, const void *data, size_t size)
+{
+ const char *p = data;
+ size_t length;
+ char *line;
+
+#define ENCODE_LINE_LENGTH 54
+
+ pem_header(f, "BEGIN", header);
+
+ while (size > 0) {
+ ssize_t l;
+
+ length = size;
+ if (length > ENCODE_LINE_LENGTH)
+ length = ENCODE_LINE_LENGTH;
+
+ l = base64_encode(p, length, &line);
+ if (l < 0) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "malloc - out of memory");
+ return ENOMEM;
+ }
+ size -= length;
+ fprintf(f, "%s\n", line);
+ p += length;
+ free(line);
+ }
+
+ pem_header(f, "END", header);
+
+ return 0;
+}
+
+static int
+store_private_key(hx509_context context, FILE *f, hx509_private_key key)
+{
+ heim_octet_string data;
+ int ret;
+
+ ret = _hx509_private_key_export(context, key, &data);
+ if (ret == 0)
+ dump_pem_file(context, _hx509_private_pem_name(key), f,
+ data.data, data.length);
+ free(data.data);
+ return ret;
+}
+
+static int
+store_func(hx509_context context, void *ctx, hx509_cert c)
+{
+ FILE *f = (FILE *)ctx;
+ size_t size;
+ heim_octet_string data;
+ int ret;
+
+ ASN1_MALLOC_ENCODE(Certificate, data.data, data.length,
+ _hx509_get_cert(c), &size, ret);
+ if (ret)
+ return ret;
+ if (data.length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ dump_pem_file(context, "CERTIFICATE", f, data.data, data.length);
+ free(data.data);
+
+ if (_hx509_cert_private_key_exportable(c))
+ store_private_key(context, f, _hx509_cert_private_key(c));
+
+ return 0;
+}
+
+static int
+file_store(hx509_context context,
+ hx509_certs certs, void *data, int flags, hx509_lock lock)
+{
+ struct ks_file *f = data;
+ FILE *fh;
+ int ret;
+
+ fh = fopen(f->fn, "w");
+ if (fh == NULL) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Failed to open file %s for writing");
+ return ENOENT;
+ }
+
+ ret = hx509_certs_iter(context, f->certs, store_func, fh);
+ fclose(fh);
+ return ret;
+}
+
+static int
+file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
+{
+ struct ks_file *f = data;
+ return hx509_certs_add(context, f->certs, c);
+}
+
+static int
+file_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ struct ks_file *f = data;
+ return hx509_certs_start_seq(context, f->certs, cursor);
+}
+
+static int
+file_iter(hx509_context context,
+ hx509_certs certs, void *data, void *iter, hx509_cert *cert)
+{
+ struct ks_file *f = data;
+ return hx509_certs_next_cert(context, f->certs, iter, cert);
+}
+
+static int
+file_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ struct ks_file *f = data;
+ return hx509_certs_end_seq(context, f->certs, cursor);
+}
+
+static int
+file_getkeys(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ hx509_private_key **keys)
+{
+ struct ks_file *f = data;
+ return _hx509_certs_keys_get(context, f->certs, keys);
+}
+
+static int
+file_addkey(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ hx509_private_key key)
+{
+ struct ks_file *f = data;
+ return _hx509_certs_keys_add(context, f->certs, key);
+}
+
+static struct hx509_keyset_ops keyset_file = {
+ "FILE",
+ 0,
+ file_init,
+ file_store,
+ file_free,
+ file_add,
+ NULL,
+ file_iter_start,
+ file_iter,
+ file_iter_end,
+ NULL,
+ file_getkeys,
+ file_addkey
+};
+
+void
+_hx509_ks_file_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_file);
+}
diff --git a/source4/heimdal/lib/hx509/ks_mem.c b/source4/heimdal/lib/hx509/ks_mem.c
new file mode 100644
index 0000000000..dd7b7166bc
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_mem.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2005 - 2006 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$");
+
+/*
+ * Should use two hash/tree certificates intead of a array. Criteria
+ * should be subject and subjectKeyIdentifier since those two are
+ * commonly seached on in CMS and path building.
+ */
+
+struct mem_data {
+ char *name;
+ struct {
+ unsigned long len;
+ hx509_cert *val;
+ } certs;
+ hx509_private_key *keys;
+};
+
+static int
+mem_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ struct mem_data *mem;
+ mem = calloc(1, sizeof(*mem));
+ if (mem == NULL)
+ return ENOMEM;
+ if (residue == NULL || residue[0] == '\0')
+ residue = "anonymous";
+ mem->name = strdup(residue);
+ if (mem->name == NULL) {
+ free(mem);
+ return ENOMEM;
+ }
+ *data = mem;
+ return 0;
+}
+
+static int
+mem_free(hx509_certs certs, void *data)
+{
+ struct mem_data *mem = data;
+ unsigned long i;
+
+ for (i = 0; i < mem->certs.len; i++)
+ hx509_cert_free(mem->certs.val[i]);
+ free(mem->certs.val);
+ for (i = 0; mem->keys && mem->keys[i]; i++)
+ _hx509_private_key_free(&mem->keys[i]);
+ free(mem->name);
+ free(mem);
+
+ return 0;
+}
+
+static int
+mem_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
+{
+ struct mem_data *mem = data;
+ hx509_cert *val;
+
+ val = realloc(mem->certs.val,
+ (mem->certs.len + 1) * sizeof(mem->certs.val[0]));
+ if (val == NULL)
+ return ENOMEM;
+
+ mem->certs.val = val;
+ mem->certs.val[mem->certs.len] = hx509_cert_ref(c);
+ mem->certs.len++;
+
+ return 0;
+}
+
+static int
+mem_iter_start(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void **cursor)
+{
+ unsigned long *iter = malloc(sizeof(*iter));
+
+ if (iter == NULL)
+ return ENOMEM;
+
+ *iter = 0;
+ *cursor = iter;
+
+ return 0;
+}
+
+static int
+mem_iter(hx509_context contexst,
+ hx509_certs certs,
+ void *data,
+ void *cursor,
+ hx509_cert *cert)
+{
+ unsigned long *iter = cursor;
+ struct mem_data *mem = data;
+
+ if (*iter >= mem->certs.len) {
+ *cert = NULL;
+ return 0;
+ }
+
+ *cert = hx509_cert_ref(mem->certs.val[*iter]);
+ (*iter)++;
+ return 0;
+}
+
+static int
+mem_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ free(cursor);
+ return 0;
+}
+
+static int
+mem_getkeys(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ hx509_private_key **keys)
+{
+ struct mem_data *mem = data;
+ int i;
+
+ for (i = 0; mem->keys && mem->keys[i]; i++)
+ ;
+ *keys = calloc(i, sizeof(**keys));
+ for (i = 0; mem->keys && mem->keys[i]; i++) {
+ (*keys)[i] = _hx509_private_key_ref(mem->keys[i]);
+ if ((*keys)[i] == NULL) {
+ while (--i >= 0)
+ _hx509_private_key_free(&(*keys)[i]);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ }
+ (*keys)[i] = NULL;
+ return 0;
+}
+
+static int
+mem_addkey(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ hx509_private_key key)
+{
+ struct mem_data *mem = data;
+ void *ptr;
+ int i;
+
+ for (i = 0; mem->keys && mem->keys[i]; i++)
+ ;
+ ptr = realloc(mem->keys, (i + 2) * sizeof(*mem->keys));
+ if (ptr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ mem->keys = ptr;
+ mem->keys[i++] = _hx509_private_key_ref(key);
+ mem->keys[i++] = NULL;
+ return 0;
+}
+
+
+static struct hx509_keyset_ops keyset_mem = {
+ "MEMORY",
+ 0,
+ mem_init,
+ NULL,
+ mem_free,
+ mem_add,
+ NULL,
+ mem_iter_start,
+ mem_iter,
+ mem_iter_end,
+ NULL,
+ mem_getkeys,
+ mem_addkey
+};
+
+void
+_hx509_ks_mem_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_mem);
+}
diff --git a/source4/heimdal/lib/hx509/ks_null.c b/source4/heimdal/lib/hx509/ks_null.c
new file mode 100644
index 0000000000..1e6c2ea3fb
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_null.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2005 - 2006 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: ks_null.c,v 1.5 2007/01/09 10:52:10 lha Exp $");
+
+
+static int
+null_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ *data = NULL;
+ return 0;
+}
+
+static int
+null_free(hx509_certs certs, void *data)
+{
+ assert(data == NULL);
+ return 0;
+}
+
+static int
+null_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ *cursor = NULL;
+ return 0;
+}
+
+static int
+null_iter(hx509_context context,
+ hx509_certs certs, void *data, void *iter, hx509_cert *cert)
+{
+ *cert = NULL;
+ return ENOENT;
+}
+
+static int
+null_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ assert(cursor == NULL);
+ return 0;
+}
+
+
+struct hx509_keyset_ops keyset_null = {
+ "NULL",
+ 0,
+ null_init,
+ NULL,
+ null_free,
+ NULL,
+ NULL,
+ null_iter_start,
+ null_iter,
+ null_iter_end
+};
diff --git a/source4/heimdal/lib/hx509/ks_p11.c b/source4/heimdal/lib/hx509/ks_p11.c
new file mode 100644
index 0000000000..b103264b7a
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_p11.c
@@ -0,0 +1,1183 @@
+/*
+ * Copyright (c) 2004 - 2006 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: ks_p11.c,v 1.45 2007/01/09 19:43:35 lha Exp $");
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#ifdef HAVE_DLOPEN
+
+#include "pkcs11.h"
+
+struct p11_slot {
+ int flags;
+#define P11_SESSION 1
+#define P11_SESSION_IN_USE 2
+#define P11_LOGIN_REQ 4
+#define P11_LOGIN_DONE 8
+#define P11_TOKEN_PRESENT 16
+ CK_SESSION_HANDLE session;
+ CK_SLOT_ID id;
+ CK_BBOOL token;
+ char *name;
+ hx509_certs certs;
+ char *pin;
+ struct {
+ CK_MECHANISM_TYPE_PTR list;
+ CK_ULONG num;
+ CK_MECHANISM_INFO_PTR *infos;
+ } mechs;
+};
+
+struct p11_module {
+ void *dl_handle;
+ CK_FUNCTION_LIST_PTR funcs;
+ CK_ULONG num_slots;
+ unsigned int refcount;
+ struct p11_slot *slot;
+};
+
+#define P11FUNC(module,f,args) (*(module)->funcs->C_##f)args
+
+static int p11_get_session(hx509_context,
+ struct p11_module *,
+ struct p11_slot *,
+ hx509_lock,
+ CK_SESSION_HANDLE *);
+static int p11_put_session(struct p11_module *,
+ struct p11_slot *,
+ CK_SESSION_HANDLE);
+static void p11_release_module(struct p11_module *);
+
+static int p11_list_keys(hx509_context,
+ struct p11_module *,
+ struct p11_slot *,
+ CK_SESSION_HANDLE,
+ hx509_lock,
+ hx509_certs *);
+
+/*
+ *
+ */
+
+struct p11_rsa {
+ struct p11_module *p;
+ struct p11_slot *slot;
+ CK_OBJECT_HANDLE private_key;
+ CK_OBJECT_HANDLE public_key;
+};
+
+static int
+p11_rsa_public_encrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ return -1;
+}
+
+static int
+p11_rsa_public_decrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ return -1;
+}
+
+
+static int
+p11_rsa_private_encrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ struct p11_rsa *p11rsa = RSA_get_app_data(rsa);
+ CK_OBJECT_HANDLE key = p11rsa->private_key;
+ CK_SESSION_HANDLE session;
+ CK_MECHANISM mechanism;
+ CK_ULONG ck_sigsize;
+ int ret;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ memset(&mechanism, 0, sizeof(mechanism));
+ mechanism.mechanism = CKM_RSA_PKCS;
+
+ ck_sigsize = RSA_size(rsa);
+
+ ret = p11_get_session(NULL, p11rsa->p, p11rsa->slot, NULL, &session);
+ if (ret)
+ return -1;
+
+ ret = P11FUNC(p11rsa->p, SignInit, (session, &mechanism, key));
+ if (ret != CKR_OK) {
+ p11_put_session(p11rsa->p, p11rsa->slot, session);
+ return -1;
+ }
+
+ ret = P11FUNC(p11rsa->p, Sign,
+ (session, (CK_BYTE *)from, flen, to, &ck_sigsize));
+ p11_put_session(p11rsa->p, p11rsa->slot, session);
+ if (ret != CKR_OK)
+ return -1;
+
+ return ck_sigsize;
+}
+
+static int
+p11_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to,
+ RSA * rsa, int padding)
+{
+ struct p11_rsa *p11rsa = RSA_get_app_data(rsa);
+ CK_OBJECT_HANDLE key = p11rsa->private_key;
+ CK_SESSION_HANDLE session;
+ CK_MECHANISM mechanism;
+ CK_ULONG ck_sigsize;
+ int ret;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ memset(&mechanism, 0, sizeof(mechanism));
+ mechanism.mechanism = CKM_RSA_PKCS;
+
+ ck_sigsize = RSA_size(rsa);
+
+ ret = p11_get_session(NULL, p11rsa->p, p11rsa->slot, NULL, &session);
+ if (ret)
+ return -1;
+
+ ret = P11FUNC(p11rsa->p, DecryptInit, (session, &mechanism, key));
+ if (ret != CKR_OK) {
+ p11_put_session(p11rsa->p, p11rsa->slot, session);
+ return -1;
+ }
+
+ ret = P11FUNC(p11rsa->p, Decrypt,
+ (session, (CK_BYTE *)from, flen, to, &ck_sigsize));
+ p11_put_session(p11rsa->p, p11rsa->slot, session);
+ if (ret != CKR_OK)
+ return -1;
+
+ return ck_sigsize;
+}
+
+static int
+p11_rsa_init(RSA *rsa)
+{
+ return 1;
+}
+
+static int
+p11_rsa_finish(RSA *rsa)
+{
+ struct p11_rsa *p11rsa = RSA_get_app_data(rsa);
+ p11_release_module(p11rsa->p);
+ free(p11rsa);
+ return 1;
+}
+
+static const RSA_METHOD rsa_pkcs1_method = {
+ "hx509 PKCS11 PKCS#1 RSA",
+ p11_rsa_public_encrypt,
+ p11_rsa_public_decrypt,
+ p11_rsa_private_encrypt,
+ p11_rsa_private_decrypt,
+ NULL,
+ NULL,
+ p11_rsa_init,
+ p11_rsa_finish,
+ 0,
+ NULL,
+ NULL,
+ NULL
+};
+
+/*
+ *
+ */
+
+static int
+p11_mech_info(hx509_context context,
+ struct p11_module *p,
+ struct p11_slot *slot,
+ int num)
+{
+ CK_ULONG i;
+ int ret;
+
+ ret = P11FUNC(p, GetMechanismList, (slot->id, NULL_PTR, &i));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
+ "Failed to get mech list count for slot %d",
+ num);
+ return HX509_PKCS11_NO_MECH;
+ }
+ if (i == 0) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
+ "no mech supported for slot %d", num);
+ return HX509_PKCS11_NO_MECH;
+ }
+ slot->mechs.list = calloc(i, sizeof(slot->mechs.list[0]));
+ if (slot->mechs.list == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "out of memory");
+ return ENOMEM;
+ }
+ slot->mechs.num = i;
+ ret = P11FUNC(p, GetMechanismList, (slot->id, slot->mechs.list, &i));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
+ "Failed to get mech list for slot %d",
+ num);
+ return HX509_PKCS11_NO_MECH;
+ }
+ assert(i == slot->mechs.num);
+
+ slot->mechs.infos = calloc(i, sizeof(*slot->mechs.infos));
+ if (slot->mechs.list == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "out of memory");
+ return ENOMEM;
+ }
+
+ for (i = 0; i < slot->mechs.num; i++) {
+ slot->mechs.infos[i] = calloc(1, sizeof(*(slot->mechs.infos[0])));
+ if (slot->mechs.infos[i] == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "out of memory");
+ return ENOMEM;
+ }
+ ret = P11FUNC(p, GetMechanismInfo, (slot->id, slot->mechs.list[i],
+ slot->mechs.infos[i]));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
+ "Failed to get mech info for slot %d",
+ num);
+ return HX509_PKCS11_NO_MECH;
+ }
+ }
+
+ return 0;
+}
+
+static int
+p11_init_slot(hx509_context context,
+ struct p11_module *p,
+ hx509_lock lock,
+ CK_SLOT_ID id,
+ int num,
+ struct p11_slot *slot)
+{
+ CK_SESSION_HANDLE session;
+ CK_SLOT_INFO slot_info;
+ CK_TOKEN_INFO token_info;
+ int ret, i;
+
+ slot->certs = NULL;
+ slot->id = id;
+
+ ret = P11FUNC(p, GetSlotInfo, (slot->id, &slot_info));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED,
+ "Failed to init PKCS11 slot %d",
+ num);
+ return HX509_PKCS11_TOKEN_CONFUSED;
+ }
+
+ for (i = sizeof(slot_info.slotDescription) - 1; i > 0; i--) {
+ char c = slot_info.slotDescription[i];
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\0')
+ continue;
+ i++;
+ break;
+ }
+
+ asprintf(&slot->name, "%.*s",
+ i, slot_info.slotDescription);
+
+ if ((slot_info.flags & CKF_TOKEN_PRESENT) == 0)
+ return 0;
+
+ ret = P11FUNC(p, GetTokenInfo, (slot->id, &token_info));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_TOKEN,
+ "Failed to init PKCS11 slot %d "
+ "with error 0x08x",
+ num, ret);
+ return HX509_PKCS11_NO_TOKEN;
+ }
+ slot->flags |= P11_TOKEN_PRESENT;
+
+ if (token_info.flags & CKF_LOGIN_REQUIRED)
+ slot->flags |= P11_LOGIN_REQ;
+
+ ret = p11_get_session(context, p, slot, lock, &session);
+ if (ret)
+ return ret;
+
+ ret = p11_mech_info(context, p, slot, num);
+ if (ret)
+ goto out;
+
+ ret = p11_list_keys(context, p, slot, session, lock, &slot->certs);
+ out:
+ p11_put_session(p, slot, session);
+
+ return ret;
+}
+
+static int
+p11_get_session(hx509_context context,
+ struct p11_module *p,
+ struct p11_slot *slot,
+ hx509_lock lock,
+ CK_SESSION_HANDLE *psession)
+{
+ CK_RV ret;
+
+ if (slot->flags & P11_SESSION_IN_USE)
+ _hx509_abort("slot already in session");
+
+ if (slot->flags & P11_SESSION) {
+ slot->flags |= P11_SESSION_IN_USE;
+ *psession = slot->session;
+ return 0;
+ }
+
+ ret = P11FUNC(p, OpenSession, (slot->id,
+ CKF_SERIAL_SESSION,
+ NULL,
+ NULL,
+ &slot->session));
+ if (ret != CKR_OK) {
+ if (context)
+ hx509_set_error_string(context, 0, HX509_PKCS11_OPEN_SESSION,
+ "Failed to OpenSession for slot id %d "
+ "with error: 0x%08x",
+ (int)slot->id, ret);
+ return HX509_PKCS11_OPEN_SESSION;
+ }
+
+ slot->flags |= P11_SESSION;
+
+ /*
+ * If we have have to login, and haven't tried before and have a
+ * prompter or known to work pin code.
+ *
+ * This code is very conversative and only uses the prompter in
+ * the hx509_lock, the reason is that its bad to try many
+ * passwords on a pkcs11 token, it might lock up and have to be
+ * unlocked by a administrator.
+ *
+ * XXX try harder to not use pin several times on the same card.
+ */
+
+ if ( (slot->flags & P11_LOGIN_REQ)
+ && (slot->flags & P11_LOGIN_DONE) == 0
+ && (lock || slot->pin))
+ {
+ hx509_prompt prompt;
+ char pin[20];
+ char *str;
+
+ slot->flags |= P11_LOGIN_DONE;
+
+ if (slot->pin == NULL) {
+
+ memset(&prompt, 0, sizeof(prompt));
+
+ asprintf(&str, "PIN code for %s: ", slot->name);
+ prompt.prompt = str;
+ prompt.type = HX509_PROMPT_TYPE_PASSWORD;
+ prompt.reply.data = pin;
+ prompt.reply.length = sizeof(pin);
+
+ ret = hx509_lock_prompt(lock, &prompt);
+ if (ret) {
+ free(str);
+ if (context)
+ hx509_set_error_string(context, 0, ret,
+ "Failed to get pin code for slot "
+ "id %d with error: %d",
+ (int)slot->id, ret);
+ return ret;
+ }
+ free(str);
+ } else {
+ strlcpy(pin, slot->pin, sizeof(pin));
+ }
+
+ ret = P11FUNC(p, Login, (slot->session, CKU_USER,
+ (unsigned char*)pin, strlen(pin)));
+ if (ret != CKR_OK) {
+ if (context)
+ hx509_set_error_string(context, 0, HX509_PKCS11_LOGIN,
+ "Failed to login on slot id %d "
+ "with error: 0x%08x",
+ (int)slot->id, ret);
+ p11_put_session(p, slot, slot->session);
+ return HX509_PKCS11_LOGIN;
+ }
+ if (slot->pin == NULL) {
+ slot->pin = strdup(pin);
+ if (slot->pin == NULL) {
+ if (context)
+ hx509_set_error_string(context, 0, ENOMEM,
+ "out of memory");
+ p11_put_session(p, slot, slot->session);
+ return ENOMEM;
+ }
+ }
+ } else
+ slot->flags |= P11_LOGIN_DONE;
+
+ slot->flags |= P11_SESSION_IN_USE;
+
+ *psession = slot->session;
+
+ return 0;
+}
+
+static int
+p11_put_session(struct p11_module *p,
+ struct p11_slot *slot,
+ CK_SESSION_HANDLE session)
+{
+ if ((slot->flags & P11_SESSION_IN_USE) == 0)
+ _hx509_abort("slot not in session");
+ slot->flags &= ~P11_SESSION_IN_USE;
+
+ return 0;
+}
+
+static int
+iterate_entries(hx509_context context,
+ struct p11_module *p, struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ CK_ATTRIBUTE *search_data, int num_search_data,
+ CK_ATTRIBUTE *query, int num_query,
+ int (*func)(hx509_context,
+ struct p11_module *, struct p11_slot *,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ void *, CK_ATTRIBUTE *, int), void *ptr)
+{
+ CK_OBJECT_HANDLE object;
+ CK_ULONG object_count;
+ int ret, i;
+
+ ret = P11FUNC(p, FindObjectsInit, (session, search_data, num_search_data));
+ if (ret != CKR_OK) {
+ return -1;
+ }
+ while (1) {
+ ret = P11FUNC(p, FindObjects, (session, &object, 1, &object_count));
+ if (ret != CKR_OK) {
+ return -1;
+ }
+ if (object_count == 0)
+ break;
+
+ for (i = 0; i < num_query; i++)
+ query[i].pValue = NULL;
+
+ ret = P11FUNC(p, GetAttributeValue,
+ (session, object, query, num_query));
+ if (ret != CKR_OK) {
+ return -1;
+ }
+ for (i = 0; i < num_query; i++) {
+ query[i].pValue = malloc(query[i].ulValueLen);
+ if (query[i].pValue == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ }
+ ret = P11FUNC(p, GetAttributeValue,
+ (session, object, query, num_query));
+ if (ret != CKR_OK) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = (*func)(context, p, slot, session, object, ptr, query, num_query);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < num_query; i++) {
+ if (query[i].pValue)
+ free(query[i].pValue);
+ query[i].pValue = NULL;
+ }
+ }
+ out:
+
+ for (i = 0; i < num_query; i++) {
+ if (query[i].pValue)
+ free(query[i].pValue);
+ query[i].pValue = NULL;
+ }
+
+ ret = P11FUNC(p, FindObjectsFinal, (session));
+ if (ret != CKR_OK) {
+ return -2;
+ }
+
+
+ return 0;
+}
+
+static BIGNUM *
+getattr_bn(struct p11_module *p,
+ struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ unsigned int type)
+{
+ CK_ATTRIBUTE query;
+ BIGNUM *bn;
+ int ret;
+
+ query.type = type;
+ query.pValue = NULL;
+ query.ulValueLen = 0;
+
+ ret = P11FUNC(p, GetAttributeValue,
+ (session, object, &query, 1));
+ if (ret != CKR_OK)
+ return NULL;
+
+ query.pValue = malloc(query.ulValueLen);
+
+ ret = P11FUNC(p, GetAttributeValue,
+ (session, object, &query, 1));
+ if (ret != CKR_OK) {
+ free(query.pValue);
+ return NULL;
+ }
+ bn = BN_bin2bn(query.pValue, query.ulValueLen, NULL);
+ free(query.pValue);
+
+ return bn;
+}
+
+static int
+collect_private_key(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;
+ hx509_private_key key;
+ heim_octet_string localKeyId;
+ int ret;
+ RSA *rsa;
+ struct p11_rsa *p11rsa;
+
+ localKeyId.data = query[0].pValue;
+ localKeyId.length = query[0].ulValueLen;
+
+ ret = _hx509_private_key_init(&key, NULL, NULL);
+ if (ret)
+ return ret;
+
+ rsa = RSA_new();
+ if (rsa == NULL)
+ _hx509_abort("out of memory");
+
+ /*
+ * The exponent and modulus should always be present according to
+ * the pkcs11 specification, but some smartcards leaves it out,
+ * let ignore any failure to fetch it.
+ */
+ rsa->n = getattr_bn(p, slot, session, object, CKA_MODULUS);
+ rsa->e = getattr_bn(p, slot, session, object, CKA_PUBLIC_EXPONENT);
+
+ p11rsa = calloc(1, sizeof(*p11rsa));
+ if (p11rsa == NULL)
+ _hx509_abort("out of memory");
+
+ p11rsa->p = p;
+ p11rsa->slot = slot;
+ p11rsa->private_key = object;
+
+ p->refcount++;
+ if (p->refcount == 0)
+ _hx509_abort("pkcs11 refcount to high");
+
+ RSA_set_method(rsa, &rsa_pkcs1_method);
+ ret = RSA_set_app_data(rsa, p11rsa);
+ if (ret != 1)
+ _hx509_abort("RSA_set_app_data");
+
+ _hx509_private_key_assign_rsa(key, rsa);
+
+ ret = _hx509_collector_private_key_add(context,
+ collector,
+ hx509_signature_rsa(),
+ key,
+ NULL,
+ &localKeyId);
+
+ if (ret) {
+ _hx509_private_key_free(&key);
+ return ret;
+ }
+ return 0;
+}
+
+static void
+p11_cert_release(hx509_cert cert, void *ctx)
+{
+ struct p11_module *p = ctx;
+ p11_release_module(p);
+}
+
+
+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;
+ hx509_cert cert;
+ Certificate t;
+ int ret;
+
+ if ((CK_LONG)query[0].ulValueLen == -1 ||
+ (CK_LONG)query[1].ulValueLen == -1)
+ {
+ return 0;
+ }
+
+
+ ret = decode_Certificate(query[1].pValue, query[1].ulValueLen,
+ &t, NULL);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return 0;
+ }
+
+ ret = hx509_cert_init(context, &t, &cert);
+ free_Certificate(&t);
+ if (ret)
+ return ret;
+
+ p->refcount++;
+ if (p->refcount == 0)
+ _hx509_abort("pkcs11 refcount 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,
+ oid_id_pkcs_9_at_localKeyId(),
+ &data);
+ }
+
+ if ((CK_LONG)query[2].ulValueLen != -1) {
+ char *str;
+
+ asprintf(&str, "%.*s",
+ (int)query[2].ulValueLen, (char *)query[2].pValue);
+ if (str) {
+ hx509_cert_set_friendly_name(cert, str);
+ free(str);
+ }
+ }
+
+ ret = _hx509_collector_certs_add(context, collector, cert);
+ hx509_cert_free(cert);
+
+ return ret;
+}
+
+
+static int
+p11_list_keys(hx509_context context,
+ struct p11_module *p,
+ struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ hx509_lock lock,
+ hx509_certs *certs)
+{
+ struct hx509_collector *collector;
+ CK_OBJECT_CLASS key_class;
+ CK_ATTRIBUTE search_data[] = {
+ {CKA_CLASS, NULL, 0},
+ };
+ CK_ATTRIBUTE query_data[3] = {
+ {CKA_ID, NULL, 0},
+ {CKA_VALUE, NULL, 0},
+ {CKA_LABEL, NULL, 0}
+ };
+ int ret;
+
+ search_data[0].pValue = &key_class;
+ search_data[0].ulValueLen = sizeof(key_class);
+
+ if (lock == NULL)
+ lock = _hx509_empty_lock;
+
+ collector = _hx509_collector_alloc(context, lock);
+ if (collector == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ key_class = CKO_PRIVATE_KEY;
+ ret = iterate_entries(context, p, slot, session,
+ search_data, 1,
+ query_data, 1,
+ collect_private_key, collector);
+ if (ret)
+ goto out;
+
+ key_class = CKO_CERTIFICATE;
+ ret = iterate_entries(context, p, slot, session,
+ search_data, 1,
+ query_data, 3,
+ collect_cert, collector);
+ if (ret)
+ goto out;
+
+ ret = _hx509_collector_collect_certs(context, collector, &slot->certs);
+
+out:
+ _hx509_collector_free(collector);
+
+ return ret;
+}
+
+
+static int
+p11_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ CK_C_GetFunctionList getFuncs;
+ struct p11_module *p;
+ char *list, *str;
+ int ret;
+
+ *data = NULL;
+
+ list = strdup(residue);
+ if (list == NULL)
+ return ENOMEM;
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ free(list);
+ return ENOMEM;
+ }
+
+ p->refcount = 1;
+
+ str = strchr(list, ',');
+ if (str)
+ *str++ = '\0';
+ while (str) {
+ char *strnext;
+ strnext = strchr(str, ',');
+ if (strnext)
+ *strnext++ = '\0';
+#if 0
+ if (strncasecmp(str, "slot=", 5) == 0)
+ p->selected_slot = atoi(str + 5);
+#endif
+ str = strnext;
+ }
+
+ p->dl_handle = dlopen(list, RTLD_NOW);
+ free(list);
+ if (p->dl_handle == NULL) {
+ ret = HX509_PKCS11_LOAD;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to open %s: %s", list, dlerror());
+ goto out;
+ }
+
+ getFuncs = dlsym(p->dl_handle, "C_GetFunctionList");
+ if (getFuncs == NULL) {
+ ret = HX509_PKCS11_LOAD;
+ hx509_set_error_string(context, 0, ret,
+ "C_GetFunctionList missing in %s: %s",
+ list, dlerror());
+ goto out;
+ }
+
+ ret = (*getFuncs)(&p->funcs);
+ if (ret) {
+ ret = HX509_PKCS11_LOAD;
+ hx509_set_error_string(context, 0, ret,
+ "C_GetFunctionList failed in %s", list);
+ goto out;
+ }
+
+ ret = P11FUNC(p, Initialize, (NULL_PTR));
+ if (ret != CKR_OK) {
+ ret = HX509_PKCS11_TOKEN_CONFUSED;
+ hx509_set_error_string(context, 0, ret,
+ "Failed initialize the PKCS11 module");
+ goto out;
+ }
+
+ ret = P11FUNC(p, GetSlotList, (FALSE, NULL, &p->num_slots));
+ if (ret) {
+ ret = HX509_PKCS11_TOKEN_CONFUSED;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to get number of PKCS11 slots");
+ goto out;
+ }
+
+ if (p->num_slots == 0) {
+ ret = HX509_PKCS11_NO_SLOT;
+ hx509_set_error_string(context, 0, ret,
+ "Selected PKCS11 module have no slots");
+ goto out;
+ }
+
+
+ {
+ CK_SLOT_ID_PTR slot_ids;
+ int i, num_tokens = 0;
+
+ slot_ids = malloc(p->num_slots * sizeof(*slot_ids));
+ if (slot_ids == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = P11FUNC(p, GetSlotList, (FALSE, slot_ids, &p->num_slots));
+ if (ret) {
+ free(slot_ids);
+ hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED,
+ "Failed getting slot-list from "
+ "PKCS11 module");
+ ret = HX509_PKCS11_TOKEN_CONFUSED;
+ goto out;
+ }
+
+ p->slot = calloc(p->num_slots, sizeof(p->slot[0]));
+ if (p->slot == NULL) {
+ free(slot_ids);
+ hx509_set_error_string(context, 0, ENOMEM,
+ "Failed to get memory for slot-list");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < p->num_slots; i++) {
+ ret = p11_init_slot(context, p, lock, slot_ids[i], i, &p->slot[i]);
+ if (ret)
+ break;
+ if (p->slot[i].flags & P11_TOKEN_PRESENT)
+ num_tokens++;
+ }
+ free(slot_ids);
+ if (ret)
+ goto out;
+ if (num_tokens == 0) {
+ ret = HX509_PKCS11_NO_TOKEN;
+ goto out;
+ }
+ }
+
+ *data = p;
+
+ return 0;
+ out:
+ p11_release_module(p);
+ return ret;
+}
+
+static void
+p11_release_module(struct p11_module *p)
+{
+ int i;
+
+ if (p->refcount == 0)
+ _hx509_abort("pkcs11 refcount to low");
+ if (--p->refcount > 0)
+ return;
+
+ for (i = 0; i < p->num_slots; i++) {
+ if (p->slot[i].flags & P11_SESSION_IN_USE)
+ _hx509_abort("pkcs11 module release while session in use");
+ if (p->slot[i].flags & P11_SESSION) {
+ int ret;
+
+ ret = P11FUNC(p, CloseSession, (p->slot[i].session));
+ if (ret != CKR_OK)
+ ;
+ }
+
+ if (p->slot[i].name)
+ free(p->slot[i].name);
+ if (p->slot[i].pin) {
+ memset(p->slot[i].pin, 0, strlen(p->slot[i].pin));
+ free(p->slot[i].pin);
+ }
+ if (p->slot[i].mechs.num) {
+ free(p->slot[i].mechs.list);
+
+ if (p->slot[i].mechs.infos) {
+ int j;
+
+ for (j = 0 ; j < p->slot[i].mechs.num ; j++)
+ free(p->slot[i].mechs.infos[j]);
+ free(p->slot[i].mechs.infos);
+ }
+ }
+ }
+ free(p->slot);
+
+ if (p->funcs)
+ P11FUNC(p, Finalize, (NULL));
+
+ if (p->dl_handle)
+ dlclose(p->dl_handle);
+
+ memset(p, 0, sizeof(*p));
+ free(p);
+}
+
+static int
+p11_free(hx509_certs certs, void *data)
+{
+ struct p11_module *p = data;
+ int i;
+
+ for (i = 0; i < p->num_slots; i++) {
+ if (p->slot[i].certs)
+ hx509_certs_free(&p->slot[i].certs);
+ }
+ p11_release_module(p);
+ return 0;
+}
+
+struct p11_cursor {
+ hx509_certs certs;
+ void *cursor;
+};
+
+static int
+p11_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ struct p11_module *p = data;
+ struct p11_cursor *c;
+ int ret, i;
+
+ c = malloc(sizeof(*c));
+ if (c == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ ret = hx509_certs_init(context, "MEMORY:pkcs11-iter", 0, NULL, &c->certs);
+ if (ret) {
+ free(c);
+ return ret;
+ }
+
+ for (i = 0 ; i < p->num_slots; i++) {
+ if (p->slot[i].certs == NULL)
+ continue;
+ ret = hx509_certs_merge(context, c->certs, p->slot[i].certs);
+ if (ret) {
+ hx509_certs_free(&c->certs);
+ free(c);
+ return ret;
+ }
+ }
+
+ ret = hx509_certs_start_seq(context, c->certs, &c->cursor);
+ if (ret) {
+ hx509_certs_free(&c->certs);
+ free(c);
+ return 0;
+ }
+ *cursor = c;
+
+ return 0;
+}
+
+static int
+p11_iter(hx509_context context,
+ hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
+{
+ struct p11_cursor *c = cursor;
+ return hx509_certs_next_cert(context, c->certs, c->cursor, cert);
+}
+
+static int
+p11_iter_end(hx509_context context,
+ hx509_certs certs, void *data, void *cursor)
+{
+ struct p11_cursor *c = cursor;
+ int ret;
+ ret = hx509_certs_end_seq(context, c->certs, c->cursor);
+ hx509_certs_free(&c->certs);
+ free(c);
+ return ret;
+}
+
+#define MECHFLAG(x) { "unknown-flag-" #x, x }
+static struct units mechflags[] = {
+ MECHFLAG(0x80000000),
+ MECHFLAG(0x40000000),
+ MECHFLAG(0x20000000),
+ MECHFLAG(0x10000000),
+ MECHFLAG(0x08000000),
+ MECHFLAG(0x04000000),
+ {"ec-compress", 0x2000000 },
+ {"ec-uncompress", 0x1000000 },
+ {"ec-namedcurve", 0x0800000 },
+ {"ec-ecparameters", 0x0400000 },
+ {"ec-f-2m", 0x0200000 },
+ {"ec-f-p", 0x0100000 },
+ {"derive", 0x0080000 },
+ {"unwrap", 0x0040000 },
+ {"wrap", 0x0020000 },
+ {"genereate-key-pair", 0x0010000 },
+ {"generate", 0x0008000 },
+ {"verify-recover", 0x0004000 },
+ {"verify", 0x0002000 },
+ {"sign-recover", 0x0001000 },
+ {"sign", 0x0000800 },
+ {"digest", 0x0000400 },
+ {"decrypt", 0x0000200 },
+ {"encrypt", 0x0000100 },
+ MECHFLAG(0x00080),
+ MECHFLAG(0x00040),
+ MECHFLAG(0x00020),
+ MECHFLAG(0x00010),
+ MECHFLAG(0x00008),
+ MECHFLAG(0x00004),
+ MECHFLAG(0x00002),
+ {"hw", 0x0000001 },
+ { NULL, 0x0000000 }
+};
+#undef MECHFLAG
+
+static int
+p11_printinfo(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ int (*func)(void *, char *),
+ void *ctx)
+{
+ struct p11_module *p = data;
+ int i, j;
+
+ _hx509_pi_printf(func, ctx, "pkcs11 driver with %d slot%s",
+ p->num_slots, p->num_slots > 1 ? "s" : "");
+
+ for (i = 0; i < p->num_slots; i++) {
+ struct p11_slot *s = &p->slot[i];
+
+ _hx509_pi_printf(func, ctx, "slot %d: id: %d name: %s flags: %08x",
+ i, (int)s->id, s->name, s->flags);
+
+ _hx509_pi_printf(func, ctx, "number of supported mechanisms: %lu",
+ (unsigned long)s->mechs.num);
+ for (j = 0; j < s->mechs.num; j++) {
+ const char *mechname = "unknown";
+ char flags[256], unknownname[40];
+#define MECHNAME(s,n) case s: mechname = n; break
+ switch(s->mechs.list[j]) {
+ MECHNAME(CKM_RSA_PKCS_KEY_PAIR_GEN, "rsa-pkcs-key-pair-gen");
+ MECHNAME(CKM_RSA_PKCS, "rsa-pkcs");
+ MECHNAME(CKM_RSA_X_509, "rsa-x-509");
+ MECHNAME(CKM_MD5_RSA_PKCS, "md5-rsa-pkcs");
+ MECHNAME(CKM_SHA1_RSA_PKCS, "sha1-rsa-pkcs");
+ default:
+ snprintf(unknownname, sizeof(unknownname),
+ "unknown-mech-%lu",
+ (unsigned long)s->mechs.list[j]);
+ mechname = unknownname;
+ break;
+ }
+#undef MECHNAME
+ unparse_flags(s->mechs.infos[j]->flags, mechflags,
+ flags, sizeof(flags));
+
+ _hx509_pi_printf(func, ctx, " %s: %s", mechname, flags);
+ }
+ }
+
+ return 0;
+}
+
+static struct hx509_keyset_ops keyset_pkcs11 = {
+ "PKCS11",
+ 0,
+ p11_init,
+ NULL,
+ p11_free,
+ NULL,
+ NULL,
+ p11_iter_start,
+ p11_iter,
+ p11_iter_end,
+ p11_printinfo
+};
+
+#endif /* HAVE_DLOPEN */
+
+void
+_hx509_ks_pkcs11_register(hx509_context context)
+{
+#ifdef HAVE_DLOPEN
+ _hx509_ks_register(context, &keyset_pkcs11);
+#endif
+}
diff --git a/source4/heimdal/lib/hx509/ks_p12.c b/source4/heimdal/lib/hx509/ks_p12.c
new file mode 100644
index 0000000000..69dba802e5
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_p12.c
@@ -0,0 +1,697 @@
+/*
+ * 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: ks_p12.c,v 1.18 2007/01/09 10:52:11 lha Exp $");
+
+struct ks_pkcs12 {
+ hx509_certs certs;
+ char *fn;
+};
+
+typedef int (*collector_func)(hx509_context,
+ struct hx509_collector *,
+ const void *, size_t,
+ const PKCS12_Attributes *);
+
+struct type {
+ const heim_oid * (*oid)(void);
+ collector_func func;
+};
+
+static void
+parse_pkcs12_type(hx509_context, struct hx509_collector *, const heim_oid *,
+ const void *, size_t, const PKCS12_Attributes *);
+
+
+static const PKCS12_Attribute *
+find_attribute(const PKCS12_Attributes *attrs, const heim_oid *oid)
+{
+ int i;
+ if (attrs == NULL)
+ return NULL;
+ for (i = 0; i < attrs->len; i++)
+ if (der_heim_oid_cmp(oid, &attrs->val[i].attrId) == 0)
+ return &attrs->val[i];
+ return NULL;
+}
+
+static int
+keyBag_parser(hx509_context context,
+ struct hx509_collector *c,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ const PKCS12_Attribute *attr;
+ PKCS8PrivateKeyInfo ki;
+ const heim_octet_string *os = NULL;
+ int ret;
+
+ attr = find_attribute(attrs, oid_id_pkcs_9_at_localKeyId());
+ if (attr)
+ os = &attr->attrValues;
+
+ ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL);
+ if (ret)
+ return ret;
+
+ _hx509_collector_private_key_add(context,
+ c,
+ &ki.privateKeyAlgorithm,
+ NULL,
+ &ki.privateKey,
+ &attr->attrValues);
+ free_PKCS8PrivateKeyInfo(&ki);
+ return 0;
+}
+
+static int
+ShroudedKeyBag_parser(hx509_context context,
+ struct hx509_collector *c,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ PKCS8EncryptedPrivateKeyInfo pk;
+ heim_octet_string content;
+ int ret;
+
+ memset(&pk, 0, sizeof(pk));
+
+ ret = decode_PKCS8EncryptedPrivateKeyInfo(data, length, &pk, NULL);
+ if (ret)
+ return ret;
+
+ ret = _hx509_pbe_decrypt(context,
+ _hx509_collector_get_lock(c),
+ &pk.encryptionAlgorithm,
+ &pk.encryptedData,
+ &content);
+ free_PKCS8EncryptedPrivateKeyInfo(&pk);
+ if (ret)
+ return ret;
+
+ ret = keyBag_parser(context, c, content.data, content.length, attrs);
+ der_free_octet_string(&content);
+ return ret;
+}
+
+static int
+certBag_parser(hx509_context context,
+ struct hx509_collector *c,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ heim_octet_string os;
+ Certificate t;
+ hx509_cert cert;
+ PKCS12_CertBag cb;
+ int ret;
+
+ ret = decode_PKCS12_CertBag(data, length, &cb, NULL);
+ if (ret)
+ return ret;
+
+ if (der_heim_oid_cmp(oid_id_pkcs_9_at_certTypes_x509(), &cb.certType)) {
+ free_PKCS12_CertBag(&cb);
+ return 0;
+ }
+
+ ret = decode_PKCS12_OctetString(cb.certValue.data,
+ cb.certValue.length,
+ &os,
+ NULL);
+ free_PKCS12_CertBag(&cb);
+ if (ret)
+ return ret;
+
+ ret = decode_Certificate(os.data, os.length, &t, NULL);
+ der_free_octet_string(&os);
+ if (ret)
+ return ret;
+
+ ret = hx509_cert_init(context, &t, &cert);
+ free_Certificate(&t);
+ if (ret)
+ return ret;
+
+ ret = _hx509_collector_certs_add(context, c, cert);
+ if (ret) {
+ hx509_cert_free(cert);
+ return ret;
+ }
+
+ {
+ const PKCS12_Attribute *attr;
+ const heim_oid * (*oids[])(void) = {
+ oid_id_pkcs_9_at_localKeyId, oid_id_pkcs_9_at_friendlyName
+ };
+ int i;
+
+ for (i = 0; i < sizeof(oids)/sizeof(oids[0]); i++) {
+ const heim_oid *oid = (*(oids[i]))();
+ attr = find_attribute(attrs, oid);
+ if (attr)
+ _hx509_set_cert_attribute(context, cert, oid,
+ &attr->attrValues);
+ }
+ }
+
+ hx509_cert_free(cert);
+
+ return 0;
+}
+
+static int
+parse_safe_content(hx509_context context,
+ struct hx509_collector *c,
+ const unsigned char *p, size_t len)
+{
+ PKCS12_SafeContents sc;
+ int ret, i;
+
+ memset(&sc, 0, sizeof(sc));
+
+ ret = decode_PKCS12_SafeContents(p, len, &sc, NULL);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < sc.len ; i++)
+ parse_pkcs12_type(context,
+ c,
+ &sc.val[i].bagId,
+ sc.val[i].bagValue.data,
+ sc.val[i].bagValue.length,
+ sc.val[i].bagAttributes);
+
+ free_PKCS12_SafeContents(&sc);
+ return 0;
+}
+
+static int
+safeContent_parser(hx509_context context,
+ struct hx509_collector *c,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ heim_octet_string os;
+ int ret;
+
+ ret = decode_PKCS12_OctetString(data, length, &os, NULL);
+ if (ret)
+ return ret;
+ ret = parse_safe_content(context, c, os.data, os.length);
+ der_free_octet_string(&os);
+ return ret;
+}
+
+static int
+encryptedData_parser(hx509_context context,
+ struct hx509_collector *c,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ heim_octet_string content;
+ heim_oid contentType;
+ int ret;
+
+ memset(&contentType, 0, sizeof(contentType));
+
+ ret = hx509_cms_decrypt_encrypted(context,
+ _hx509_collector_get_lock(c),
+ data, length,
+ &contentType,
+ &content);
+ if (ret)
+ return ret;
+
+ if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) == 0)
+ ret = parse_safe_content(context, c, content.data, content.length);
+
+ der_free_octet_string(&content);
+ der_free_oid(&contentType);
+ return ret;
+}
+
+static int
+envelopedData_parser(hx509_context context,
+ struct hx509_collector *c,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ heim_octet_string content;
+ heim_oid contentType;
+ hx509_lock lock;
+ int ret;
+
+ memset(&contentType, 0, sizeof(contentType));
+
+ lock = _hx509_collector_get_lock(c);
+
+ ret = hx509_cms_unenvelope(context,
+ _hx509_lock_unlock_certs(lock),
+ 0,
+ data, length,
+ NULL,
+ &contentType,
+ &content);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "PKCS12 failed to unenvelope");
+ return ret;
+ }
+
+ if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) == 0)
+ ret = parse_safe_content(context, c, content.data, content.length);
+
+ der_free_octet_string(&content);
+ der_free_oid(&contentType);
+
+ return ret;
+}
+
+
+struct type bagtypes[] = {
+ { oid_id_pkcs12_keyBag, keyBag_parser },
+ { oid_id_pkcs12_pkcs8ShroudedKeyBag, ShroudedKeyBag_parser },
+ { oid_id_pkcs12_certBag, certBag_parser },
+ { oid_id_pkcs7_data, safeContent_parser },
+ { oid_id_pkcs7_encryptedData, encryptedData_parser },
+ { oid_id_pkcs7_envelopedData, envelopedData_parser }
+};
+
+static void
+parse_pkcs12_type(hx509_context context,
+ struct hx509_collector *c,
+ const heim_oid *oid,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ int i;
+
+ for (i = 0; i < sizeof(bagtypes)/sizeof(bagtypes[0]); i++)
+ if (der_heim_oid_cmp((*bagtypes[i].oid)(), oid) == 0)
+ (*bagtypes[i].func)(context, c, data, length, attrs);
+}
+
+static int
+p12_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ struct ks_pkcs12 *p12;
+ size_t len;
+ void *buf;
+ PKCS12_PFX pfx;
+ PKCS12_AuthenticatedSafe as;
+ int ret, i;
+ struct hx509_collector *c;
+
+ *data = NULL;
+
+ if (lock == NULL)
+ lock = _hx509_empty_lock;
+
+ c = _hx509_collector_alloc(context, lock);
+ if (c == NULL)
+ return ENOMEM;
+
+ p12 = calloc(1, sizeof(*p12));
+ if (p12 == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ p12->fn = strdup(residue);
+ if (p12->fn == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ if (flags & HX509_CERTS_CREATE) {
+ ret = hx509_certs_init(context, "MEMORY:ks-file-create",
+ 0, lock, &p12->certs);
+ if (ret)
+ goto out;
+ *data = p12;
+ return 0;
+ }
+
+ ret = _hx509_map_file(residue, &buf, &len, NULL);
+ if (ret)
+ goto out;
+
+ ret = decode_PKCS12_PFX(buf, len, &pfx, NULL);
+ _hx509_unmap_file(buf, len);
+ if (ret)
+ goto out;
+
+ if (der_heim_oid_cmp(&pfx.authSafe.contentType, oid_id_pkcs7_data()) != 0) {
+ free_PKCS12_PFX(&pfx);
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret,
+ "PKCS PFX isn't a pkcs7-data container");
+ goto out;
+ }
+
+ if (pfx.authSafe.content == NULL) {
+ free_PKCS12_PFX(&pfx);
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret,
+ "PKCS PFX missing data");
+ goto out;
+ }
+
+ {
+ heim_octet_string asdata;
+
+ ret = decode_PKCS12_OctetString(pfx.authSafe.content->data,
+ pfx.authSafe.content->length,
+ &asdata,
+ NULL);
+ free_PKCS12_PFX(&pfx);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ ret = decode_PKCS12_AuthenticatedSafe(asdata.data,
+ asdata.length,
+ &as,
+ NULL);
+ der_free_octet_string(&asdata);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ for (i = 0; i < as.len; i++)
+ parse_pkcs12_type(context,
+ c,
+ &as.val[i].contentType,
+ as.val[i].content->data,
+ as.val[i].content->length,
+ NULL);
+
+ free_PKCS12_AuthenticatedSafe(&as);
+
+ ret = _hx509_collector_collect_certs(context, c, &p12->certs);
+ if (ret == 0)
+ *data = p12;
+
+out:
+ _hx509_collector_free(c);
+
+ if (ret) {
+ if (p12->certs)
+ hx509_certs_free(&p12->certs);
+ free(p12);
+ }
+
+ return ret;
+}
+
+static int
+addBag(hx509_context context,
+ PKCS12_AuthenticatedSafe *as,
+ const heim_oid *oid,
+ void *data,
+ size_t length)
+{
+ void *ptr;
+ int ret;
+
+ ptr = realloc(as->val, sizeof(as->val[0]) * (as->len + 1));
+ if (ptr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "malloc out of memory");
+ return ENOMEM;
+ }
+ as->val = ptr;
+
+ ret = der_copy_oid(oid, &as->val[as->len].contentType);
+
+ as->val[as->len].content = calloc(1, sizeof(*as->val[0].content));
+ if (as->val[as->len].content == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "malloc out of memory");
+ return ENOMEM;
+ }
+
+ as->val[as->len].content->data = data;
+ as->val[as->len].content->length = length;
+
+ as->len++;
+
+ return 0;
+}
+
+static int
+store_func(hx509_context context, void *ctx, hx509_cert c)
+{
+ PKCS12_AuthenticatedSafe *as = ctx;
+ PKCS12_OctetString os;
+ PKCS12_CertBag cb;
+ size_t size;
+ int ret;
+
+ memset(&os, 0, sizeof(os));
+ memset(&cb, 0, sizeof(cb));
+
+ os.data = NULL;
+ os.length = 0;
+
+ ASN1_MALLOC_ENCODE(Certificate, os.data, os.length,
+ _hx509_get_cert(c), &size, ret);
+ if (ret)
+ goto out;
+ ASN1_MALLOC_ENCODE(PKCS12_OctetString,
+ cb.certValue.data,cb.certValue.length,
+ &os, &size, ret);
+ free(os.data);
+ if (ret)
+ goto out;
+ ret = der_copy_oid(oid_id_pkcs_9_at_certTypes_x509(), &cb.certType);
+ if (ret) {
+ free_PKCS12_CertBag(&cb);
+ goto out;
+ }
+ ASN1_MALLOC_ENCODE(PKCS12_CertBag, os.data, os.length,
+ &cb, &size, ret);
+ free(cb.certValue.data);
+ if (ret)
+ goto out;
+
+ ret = addBag(context, as, oid_id_pkcs12_certBag(), os.data, os.length);
+
+ if (_hx509_cert_private_key_exportable(c)) {
+ hx509_private_key key = _hx509_cert_private_key(c);
+ PKCS8PrivateKeyInfo pki;
+
+ memset(&pki, 0, sizeof(pki));
+
+ ret = der_parse_hex_heim_integer("00", &pki.version);
+ if (ret)
+ return ret;
+ ret = _hx509_private_key_oid(context, key,
+ &pki.privateKeyAlgorithm.algorithm);
+ if (ret) {
+ free_PKCS8PrivateKeyInfo(&pki);
+ return ret;
+ }
+ ret = _hx509_private_key_export(context,
+ _hx509_cert_private_key(c),
+ &pki.privateKey);
+ if (ret) {
+ free_PKCS8PrivateKeyInfo(&pki);
+ return ret;
+ }
+ /* set attribute, oid_id_pkcs_9_at_localKeyId() */
+
+ ASN1_MALLOC_ENCODE(PKCS8PrivateKeyInfo, os.data, os.length,
+ &pki, &size, ret);
+ free_PKCS8PrivateKeyInfo(&pki);
+ if (ret)
+ return ret;
+
+ ret = addBag(context, as, oid_id_pkcs12_keyBag(), os.data, os.length);
+ if (ret)
+ return ret;
+ }
+
+out:
+ return ret;
+}
+
+static int
+p12_store(hx509_context context,
+ hx509_certs certs, void *data, int flags, hx509_lock lock)
+{
+ struct ks_pkcs12 *p12 = data;
+ PKCS12_PFX pfx;
+ PKCS12_AuthenticatedSafe as;
+ PKCS12_OctetString asdata;
+ size_t size;
+ int ret;
+
+ memset(&as, 0, sizeof(as));
+ memset(&pfx, 0, sizeof(pfx));
+
+ ret = hx509_certs_iter(context, p12->certs, store_func, &as);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(PKCS12_AuthenticatedSafe, asdata.data, asdata.length,
+ &as, &size, ret);
+ free_PKCS12_AuthenticatedSafe(&as);
+ if (ret)
+ return ret;
+
+ ret = der_parse_hex_heim_integer("03", &pfx.version);
+ if (ret) {
+ free(asdata.data);
+ goto out;
+ }
+
+ pfx.authSafe.content = calloc(1, sizeof(*pfx.authSafe.content));
+
+ ASN1_MALLOC_ENCODE(PKCS12_OctetString,
+ pfx.authSafe.content->data,
+ pfx.authSafe.content->length,
+ &asdata, &size, ret);
+ free(asdata.data);
+ if (ret)
+ goto out;
+
+ ret = der_copy_oid(oid_id_pkcs7_data(), &pfx.authSafe.contentType);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(PKCS12_PFX, asdata.data, asdata.length,
+ &pfx, &size, ret);
+ if (ret)
+ goto out;
+
+#if 0
+ const struct _hx509_password *pw;
+
+ pw = _hx509_lock_get_passwords(lock);
+ if (pw != NULL) {
+ pfx.macData = calloc(1, sizeof(*pfx.macData));
+ if (pfx.macData == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "malloc out of memory");
+ return ret;
+ }
+ if (pfx.macData == NULL) {
+ free(asdata.data);
+ goto out;
+ }
+ }
+ ret = calculate_hash(&aspath, pw, pfx.macData);
+#endif
+
+ rk_dumpdata(p12->fn, asdata.data, asdata.length);
+ free(asdata.data);
+
+out:
+ free_PKCS12_AuthenticatedSafe(&as);
+ free_PKCS12_PFX(&pfx);
+
+ return ret;
+}
+
+
+static int
+p12_free(hx509_certs certs, void *data)
+{
+ struct ks_pkcs12 *p12 = data;
+ hx509_certs_free(&p12->certs);
+ free(p12->fn);
+ free(p12);
+ return 0;
+}
+
+static int
+p12_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
+{
+ struct ks_pkcs12 *p12 = data;
+ return hx509_certs_add(context, p12->certs, c);
+}
+
+static int
+p12_iter_start(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void **cursor)
+{
+ struct ks_pkcs12 *p12 = data;
+ return hx509_certs_start_seq(context, p12->certs, cursor);
+}
+
+static int
+p12_iter(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor,
+ hx509_cert *cert)
+{
+ struct ks_pkcs12 *p12 = data;
+ return hx509_certs_next_cert(context, p12->certs, cursor, cert);
+}
+
+static int
+p12_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ struct ks_pkcs12 *p12 = data;
+ return hx509_certs_end_seq(context, p12->certs, cursor);
+}
+
+static struct hx509_keyset_ops keyset_pkcs12 = {
+ "PKCS12",
+ 0,
+ p12_init,
+ p12_store,
+ p12_free,
+ p12_add,
+ NULL,
+ p12_iter_start,
+ p12_iter,
+ p12_iter_end
+};
+
+void
+_hx509_ks_pkcs12_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_pkcs12);
+}
diff --git a/source4/heimdal/lib/hx509/lock.c b/source4/heimdal/lib/hx509/lock.c
new file mode 100644
index 0000000000..95fc0aa26d
--- /dev/null
+++ b/source4/heimdal/lib/hx509/lock.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2005 - 2006 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: lock.c,v 1.13 2006/10/14 09:41:05 lha Exp $");
+
+struct hx509_lock_data {
+ struct _hx509_password password;
+ hx509_certs certs;
+ hx509_prompter_fct prompt;
+ void *prompt_data;
+};
+
+static struct hx509_lock_data empty_lock_data = {
+ { 0, NULL }
+};
+
+hx509_lock _hx509_empty_lock = &empty_lock_data;
+
+/*
+ *
+ */
+
+int
+hx509_lock_init(hx509_context context, hx509_lock *lock)
+{
+ hx509_lock l;
+ int ret;
+
+ *lock = NULL;
+
+ l = calloc(1, sizeof(*l));
+ if (l == NULL)
+ return ENOMEM;
+
+ ret = hx509_certs_init(context,
+ "MEMORY:locks-internal",
+ 0,
+ NULL,
+ &l->certs);
+ if (ret) {
+ free(l);
+ return ret;
+ }
+
+ *lock = l;
+
+ return 0;
+}
+
+int
+hx509_lock_add_password(hx509_lock lock, const char *password)
+{
+ void *d;
+ char *s;
+
+ s = strdup(password);
+ if (s == NULL)
+ return ENOMEM;
+
+ d = realloc(lock->password.val,
+ (lock->password.len + 1) * sizeof(lock->password.val[0]));
+ if (d == NULL) {
+ free(s);
+ return ENOMEM;
+ }
+ lock->password.val = d;
+ lock->password.val[lock->password.len] = s;
+ lock->password.len++;
+
+ return 0;
+}
+
+const struct _hx509_password *
+_hx509_lock_get_passwords(hx509_lock lock)
+{
+ return &lock->password;
+}
+
+hx509_certs
+_hx509_lock_unlock_certs(hx509_lock lock)
+{
+ return lock->certs;
+}
+
+void
+hx509_lock_reset_passwords(hx509_lock lock)
+{
+ int i;
+ for (i = 0; i < lock->password.len; i++)
+ free(lock->password.val[i]);
+ free(lock->password.val);
+ lock->password.val = NULL;
+ lock->password.len = 0;
+}
+
+int
+hx509_lock_add_cert(hx509_context context, hx509_lock lock, hx509_cert cert)
+{
+ return hx509_certs_add(context, lock->certs, cert);
+}
+
+int
+hx509_lock_add_certs(hx509_context context, hx509_lock lock, hx509_certs certs)
+{
+ return hx509_certs_merge(context, lock->certs, certs);
+}
+
+void
+hx509_lock_reset_certs(hx509_context context, hx509_lock lock)
+{
+ hx509_certs certs = lock->certs;
+ int ret;
+
+ ret = hx509_certs_init(context,
+ "MEMORY:locks-internal",
+ 0,
+ NULL,
+ &lock->certs);
+ if (ret == 0)
+ hx509_certs_free(&certs);
+ else
+ lock->certs = certs;
+}
+
+int
+_hx509_lock_find_cert(hx509_lock lock, const hx509_query *q, hx509_cert *c)
+{
+ *c = NULL;
+ return 0;
+}
+
+int
+hx509_lock_set_prompter(hx509_lock lock, hx509_prompter_fct prompt, void *data)
+{
+ lock->prompt = prompt;
+ lock->prompt_data = data;
+ return 0;
+}
+
+void
+hx509_lock_reset_promper(hx509_lock lock)
+{
+ lock->prompt = NULL;
+ lock->prompt_data = NULL;
+}
+
+static int
+default_prompter(void *data, const hx509_prompt *prompter)
+{
+ if (hx509_prompt_hidden(prompter->type)) {
+ if(UI_UTIL_read_pw_string(prompter->reply.data,
+ prompter->reply.length,
+ prompter->prompt,
+ 0))
+ return 1;
+ } else {
+ char *s = prompter->reply.data;
+
+ fputs (prompter->prompt, stdout);
+ fflush (stdout);
+ if(fgets(prompter->reply.data,
+ prompter->reply.length,
+ stdin) == NULL)
+ return 1;
+ s[strcspn(s, "\n")] = '\0';
+ }
+ return 0;
+}
+
+int
+hx509_lock_prompt(hx509_lock lock, hx509_prompt *prompt)
+{
+ if (lock->prompt == NULL)
+ return HX509_CRYPTO_NO_PROMPTER;
+ return (*lock->prompt)(lock->prompt_data, prompt);
+}
+
+void
+hx509_lock_free(hx509_lock lock)
+{
+ hx509_certs_free(&lock->certs);
+ hx509_lock_reset_passwords(lock);
+ memset(lock, 0, sizeof(*lock));
+ free(lock);
+}
+
+int
+hx509_prompt_hidden(hx509_prompt_type type)
+{
+ /* default to hidden if unknown */
+
+ switch (type) {
+ case HX509_PROMPT_TYPE_QUESTION:
+ case HX509_PROMPT_TYPE_INFO:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+int
+hx509_lock_command_string(hx509_lock lock, const char *string)
+{
+ if (strncasecmp(string, "PASS:", 5) == 0) {
+ hx509_lock_add_password(lock, string + 5);
+ } else if (strcasecmp(string, "PROMPT") == 0) {
+ hx509_lock_set_prompter(lock, default_prompter, NULL);
+ } else
+ return HX509_UNKNOWN_LOCK_COMMAND;
+ return 0;
+}
diff --git a/source4/heimdal/lib/hx509/name.c b/source4/heimdal/lib/hx509/name.c
new file mode 100644
index 0000000000..92e9e6f974
--- /dev/null
+++ b/source4/heimdal/lib/hx509/name.c
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 2004 - 2006 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: name.c,v 1.33 2006/12/30 23:04:11 lha Exp $");
+
+/*
+ * name parsing from rfc2253
+ * fix so parsing rfc1779 works too
+ * rfc3280
+ */
+
+static const struct {
+ char *n;
+ const heim_oid *(*o)(void);
+} no[] = {
+ { "C", oid_id_at_countryName },
+ { "CN", oid_id_at_commonName },
+ { "DC", oid_id_domainComponent },
+ { "L", oid_id_at_localityName },
+ { "O", oid_id_at_organizationName },
+ { "OU", oid_id_at_organizationalUnitName },
+ { "S", oid_id_at_stateOrProvinceName },
+ { "UID", oid_id_Userid },
+ { "emailAddress", oid_id_pkcs9_emailAddress },
+ { "serialNumber", oid_id_at_serialNumber }
+};
+
+static char *
+quote_string(const char *f, size_t len, size_t *rlen)
+{
+ size_t i, j, tolen;
+ const char *from = f;
+ char *to;
+
+ tolen = len * 3 + 1;
+ to = malloc(tolen);
+ if (to == NULL)
+ return NULL;
+
+ for (i = 0, j = 0; i < len; i++) {
+ if (from[i] == ' ' && i + 1 < len)
+ to[j++] = from[i];
+ else if (from[i] == ',' || from[i] == '=' || from[i] == '+' ||
+ from[i] == '<' || from[i] == '>' || from[i] == '#' ||
+ from[i] == ';' || from[i] == ' ')
+ {
+ to[j++] = '\\';
+ to[j++] = from[i];
+ } else if (((unsigned char)from[i]) >= 32 && ((unsigned char)from[i]) <= 127) {
+ to[j++] = from[i];
+ } else {
+ int l = snprintf(&to[j], tolen - j - 1,
+ "#%02x", (unsigned int)from[i]);
+ j += l;
+ }
+ }
+ to[j] = '\0';
+ *rlen = j;
+ return to;
+}
+
+
+static int
+append_string(char **str, size_t *total_len, char *ss, size_t len, int quote)
+{
+ char *s, *qs;
+
+ if (quote)
+ qs = quote_string(ss, len, &len);
+ else
+ qs = ss;
+
+ s = realloc(*str, len + *total_len + 1);
+ if (s == NULL)
+ _hx509_abort("allocation failure"); /* XXX */
+ memcpy(s + *total_len, qs, len);
+ if (qs != ss)
+ free(qs);
+ s[*total_len + len] = '\0';
+ *str = s;
+ *total_len += len;
+ return 0;
+}
+
+static char *
+oidtostring(const heim_oid *type)
+{
+ char *s;
+ size_t i;
+
+ for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
+ if (der_heim_oid_cmp((*no[i].o)(), type) == 0)
+ return strdup(no[i].n);
+ }
+ if (der_print_heim_oid(type, '.', &s) != 0)
+ return NULL;
+ return s;
+}
+
+static int
+stringtooid(const char *name, size_t len, heim_oid *oid)
+{
+ int i, ret;
+ char *s;
+
+ memset(oid, 0, sizeof(*oid));
+
+ for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
+ if (strncasecmp(no[i].n, name, len) == 0)
+ return der_copy_oid((*no[i].o)(), oid);
+ }
+ s = malloc(len + 1);
+ if (s == NULL)
+ return ENOMEM;
+ memcpy(s, name, len);
+ s[len] = '\0';
+ ret = der_parse_heim_oid(s, ".", oid);
+ free(s);
+ return ret;
+}
+
+int
+hx509_name_to_string(const hx509_name name, char **str)
+{
+ return _hx509_Name_to_string(&name->der_name, str);
+}
+
+int
+_hx509_Name_to_string(const Name *n, char **str)
+{
+ size_t total_len = 0;
+ int i, j;
+
+ *str = strdup("");
+ if (*str == NULL)
+ return ENOMEM;
+
+ for (i = n->u.rdnSequence.len - 1 ; i >= 0 ; i--) {
+ int len;
+
+ for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
+ DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
+ char *oidname;
+ char *ss;
+
+ oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type);
+
+ switch(ds->element) {
+ case choice_DirectoryString_ia5String:
+ ss = ds->u.ia5String;
+ break;
+ case choice_DirectoryString_printableString:
+ ss = ds->u.ia5String;
+ break;
+ case choice_DirectoryString_utf8String:
+ ss = ds->u.ia5String;
+ break;
+ case choice_DirectoryString_bmpString: {
+ uint16_t *bmp = ds->u.bmpString.data;
+ size_t bmplen = ds->u.bmpString.length;
+ size_t k;
+
+ ss = malloc(bmplen + 1);
+ if (ss == NULL)
+ _hx509_abort("allocation failure"); /* XXX */
+ for (k = 0; k < bmplen; k++)
+ ss[k] = bmp[k] & 0xff; /* XXX */
+ ss[k] = '\0';
+ break;
+ }
+ case choice_DirectoryString_teletexString:
+ ss = "teletex-string"; /* XXX */
+ break;
+ case choice_DirectoryString_universalString:
+ ss = "universalString"; /* XXX */
+ break;
+ default:
+ _hx509_abort("unknown directory type: %d", ds->element);
+ exit(1);
+ }
+ append_string(str, &total_len, oidname, strlen(oidname), 0);
+ free(oidname);
+ append_string(str, &total_len, "=", 1, 0);
+ len = strlen(ss);
+ append_string(str, &total_len, ss, len, 1);
+ if (ds->element == choice_DirectoryString_bmpString)
+ free(ss);
+ if (j + 1 < n->u.rdnSequence.val[i].len)
+ append_string(str, &total_len, "+", 1, 0);
+ }
+
+ if (i > 0)
+ append_string(str, &total_len, ",", 1, 0);
+ }
+ return 0;
+}
+
+/*
+ * XXX this function is broken, it needs to compare code points, not
+ * bytes.
+ */
+
+int
+_hx509_name_ds_cmp(const DirectoryString *ds1, const DirectoryString *ds2)
+{
+ int c;
+
+ c = ds1->element - ds2->element;
+ if (c)
+ return c;
+
+ switch(ds1->element) {
+ case choice_DirectoryString_ia5String:
+ c = strcmp(ds1->u.ia5String, ds2->u.ia5String);
+ break;
+ case choice_DirectoryString_teletexString:
+ c = der_heim_octet_string_cmp(&ds1->u.teletexString,
+ &ds2->u.teletexString);
+ break;
+ case choice_DirectoryString_printableString:
+ c = strcasecmp(ds1->u.printableString, ds2->u.printableString);
+ break;
+ case choice_DirectoryString_utf8String:
+ c = strcmp(ds1->u.utf8String, ds2->u.utf8String);
+ break;
+ case choice_DirectoryString_universalString:
+ c = der_heim_universal_string_cmp(&ds1->u.universalString,
+ &ds2->u.universalString);
+ break;
+ case choice_DirectoryString_bmpString:
+ c = der_heim_bmp_string_cmp(&ds1->u.bmpString,
+ &ds2->u.bmpString);
+ break;
+ default:
+ c = 1;
+ break;
+ }
+ return c;
+}
+
+int
+_hx509_name_cmp(const Name *n1, const Name *n2)
+{
+ int i, j, c;
+
+ c = n1->u.rdnSequence.len - n2->u.rdnSequence.len;
+ if (c)
+ return c;
+
+ for (i = 0 ; i < n1->u.rdnSequence.len; i++) {
+ c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len;
+ if (c)
+ return c;
+
+ for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) {
+ c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type,
+ &n1->u.rdnSequence.val[i].val[j].type);
+ if (c)
+ return c;
+
+ c = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value,
+ &n2->u.rdnSequence.val[i].val[j].value);
+ if (c)
+ return c;
+ }
+ }
+ return 0;
+}
+
+int
+_hx509_name_from_Name(const Name *n, hx509_name *name)
+{
+ int ret;
+ *name = calloc(1, sizeof(**name));
+ if (*name == NULL)
+ return ENOMEM;
+ ret = copy_Name(n, &(*name)->der_name);
+ if (ret) {
+ free(*name);
+ *name = NULL;
+ }
+ return ret;
+}
+
+static int
+hx509_der_parse_name(const void *data, size_t length, hx509_name *name)
+{
+ int ret;
+ Name n;
+
+ *name = NULL;
+ ret = decode_Name(data, length, &n, NULL);
+ if (ret)
+ return ret;
+ return _hx509_name_from_Name(&n, name);
+}
+
+int
+_hx509_name_modify(hx509_context context,
+ Name *name,
+ int append,
+ const heim_oid *oid,
+ const char *str)
+{
+ RelativeDistinguishedName *rdn;
+ int ret;
+ void *ptr;
+
+ ptr = realloc(name->u.rdnSequence.val,
+ sizeof(name->u.rdnSequence.val[0]) *
+ (name->u.rdnSequence.len + 1));
+ if (ptr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
+ return ENOMEM;
+ }
+ name->u.rdnSequence.val = ptr;
+
+ if (append) {
+ rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len];
+ } else {
+ memmove(&name->u.rdnSequence.val[1],
+ &name->u.rdnSequence.val[0],
+ name->u.rdnSequence.len *
+ sizeof(name->u.rdnSequence.val[0]));
+
+ rdn = &name->u.rdnSequence.val[0];
+ }
+ rdn->val = malloc(sizeof(rdn->val[0]));
+ if (rdn->val == NULL)
+ return ENOMEM;
+ rdn->len = 1;
+ ret = der_copy_oid(oid, &rdn->val[0].type);
+ if (ret)
+ return ret;
+ rdn->val[0].value.element = choice_DirectoryString_utf8String;
+ rdn->val[0].value.u.utf8String = strdup(str);
+ if (rdn->val[0].value.u.utf8String == NULL)
+ return ENOMEM;
+ name->u.rdnSequence.len += 1;
+
+ return 0;
+}
+
+int
+hx509_parse_name(hx509_context context, const char *str, hx509_name *name)
+{
+ const char *p, *q;
+ size_t len;
+ hx509_name n;
+ int ret;
+
+ *name = NULL;
+
+ n = calloc(1, sizeof(*n));
+ if (n == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ n->der_name.element = choice_Name_rdnSequence;
+
+ p = str;
+
+ while (p != NULL && *p != '\0') {
+ heim_oid oid;
+ int last;
+
+ q = strchr(p, ',');
+ if (q) {
+ len = (q - p);
+ last = 1;
+ } else {
+ len = strlen(p);
+ last = 0;
+ }
+
+ q = strchr(p, '=');
+ if (q == NULL) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret, "missing = in %s", p);
+ goto out;
+ }
+ if (q == p) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret,
+ "missing name before = in %s", p);
+ goto out;
+ }
+
+ if ((q - p) > len) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret, " = after , in %s", p);
+ goto out;
+ }
+
+ ret = stringtooid(p, q - p, &oid);
+ if (ret) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret,
+ "unknown type: %.*s", (int)(q - p), p);
+ goto out;
+ }
+
+ {
+ size_t pstr_len = len - (q - p) - 1;
+ const char *pstr = p + (q - p) + 1;
+ char *r;
+
+ r = malloc(pstr_len + 1);
+ if (r == NULL) {
+ der_free_oid(&oid);
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+ memcpy(r, pstr, pstr_len);
+ r[pstr_len] = '\0';
+
+ ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r);
+ free(r);
+ der_free_oid(&oid);
+ if(ret)
+ goto out;
+ }
+ p += len + last;
+ }
+
+ *name = n;
+
+ return 0;
+out:
+ hx509_name_free(&n);
+ return HX509_NAME_MALFORMED;
+}
+
+int
+hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to)
+{
+ int ret;
+
+ *to = calloc(1, sizeof(**to));
+ if (*to == NULL)
+ return ENOMEM;
+ ret = copy_Name(&from->der_name, &(*to)->der_name);
+ if (ret) {
+ free(*to);
+ *to = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+int
+hx509_name_to_Name(const hx509_name from, Name *to)
+{
+ return copy_Name(&from->der_name, to);
+}
+
+
+void
+hx509_name_free(hx509_name *name)
+{
+ free_Name(&(*name)->der_name);
+ memset(*name, 0, sizeof(**name));
+ free(*name);
+ *name = NULL;
+}
+
+int
+hx509_unparse_der_name(const void *data, size_t length, char **str)
+{
+ hx509_name name;
+ int ret;
+
+ ret = hx509_der_parse_name(data, length, &name);
+ if (ret)
+ return ret;
+
+ ret = hx509_name_to_string(name, str);
+ hx509_name_free(&name);
+ return ret;
+}
+
+int
+hx509_name_to_der_name(const hx509_name name, void **data, size_t *length)
+{
+ size_t size;
+ int ret;
+
+ ASN1_MALLOC_ENCODE(Name, *data, *length, &name->der_name, &size, ret);
+ if (ret)
+ return ret;
+ if (*length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ return 0;
+}
+
+
+int
+_hx509_unparse_Name(const Name *aname, char **str)
+{
+ hx509_name name;
+ int ret;
+
+ ret = _hx509_name_from_Name(aname, &name);
+ if (ret)
+ return ret;
+
+ ret = hx509_name_to_string(name, str);
+ hx509_name_free(&name);
+ return ret;
+}
+
+int
+hx509_name_is_null_p(const hx509_name name)
+{
+ return name->der_name.u.rdnSequence.len == 0;
+}
diff --git a/source4/heimdal/lib/hx509/ocsp.asn1 b/source4/heimdal/lib/hx509/ocsp.asn1
new file mode 100644
index 0000000000..62a2750b96
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ocsp.asn1
@@ -0,0 +1,113 @@
+-- From rfc2560
+-- $Id: ocsp.asn1,v 1.4 2006/12/30 12:38:44 lha Exp $
+OCSP DEFINITIONS EXPLICIT TAGS::=
+
+BEGIN
+
+IMPORTS
+ Certificate, AlgorithmIdentifier, CRLReason,
+ Name, GeneralName, CertificateSerialNumber, Extensions
+ FROM rfc2459;
+
+OCSPVersion ::= INTEGER { ocsp-v1(0) }
+
+OCSPCertStatus ::= CHOICE {
+ good [0] IMPLICIT NULL,
+ revoked [1] IMPLICIT -- OCSPRevokedInfo -- SEQUENCE {
+ revocationTime GeneralizedTime,
+ revocationReason[0] EXPLICIT CRLReason OPTIONAL
+ },
+ unknown [2] IMPLICIT NULL }
+
+OCSPCertID ::= SEQUENCE {
+ hashAlgorithm AlgorithmIdentifier,
+ issuerNameHash OCTET STRING, -- Hash of Issuer's DN
+ issuerKeyHash OCTET STRING, -- Hash of Issuers public key
+ serialNumber CertificateSerialNumber }
+
+OCSPSingleResponse ::= SEQUENCE {
+ certID OCSPCertID,
+ certStatus OCSPCertStatus,
+ thisUpdate GeneralizedTime,
+ nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
+ singleExtensions [1] EXPLICIT Extensions OPTIONAL }
+
+OCSPInnerRequest ::= SEQUENCE {
+ reqCert OCSPCertID,
+ singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL }
+
+OCSPTBSRequest ::= SEQUENCE {
+ version [0] EXPLICIT OCSPVersion -- DEFAULT v1 -- OPTIONAL,
+ requestorName [1] EXPLICIT GeneralName OPTIONAL,
+ requestList SEQUENCE OF OCSPInnerRequest,
+ requestExtensions [2] EXPLICIT Extensions OPTIONAL }
+
+OCSPSignature ::= SEQUENCE {
+ signatureAlgorithm AlgorithmIdentifier,
+ signature BIT STRING,
+ certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+
+OCSPRequest ::= SEQUENCE {
+ tbsRequest OCSPTBSRequest,
+ optionalSignature [0] EXPLICIT OCSPSignature OPTIONAL }
+
+OCSPResponseBytes ::= SEQUENCE {
+ responseType OBJECT IDENTIFIER,
+ response OCTET STRING }
+
+OCSPResponseStatus ::= ENUMERATED {
+ successful (0), --Response has valid confirmations
+ malformedRequest (1), --Illegal confirmation request
+ internalError (2), --Internal error in issuer
+ tryLater (3), --Try again later
+ --(4) is not used
+ sigRequired (5), --Must sign the request
+ unauthorized (6) --Request unauthorized
+}
+
+OCSPResponse ::= SEQUENCE {
+ responseStatus OCSPResponseStatus,
+ responseBytes [0] EXPLICIT OCSPResponseBytes OPTIONAL }
+
+OCSPKeyHash ::= OCTET STRING --SHA-1 hash of responder's public key
+ --(excluding the tag and length fields)
+
+OCSPResponderID ::= CHOICE {
+ byName [1] Name,
+ byKey [2] OCSPKeyHash }
+
+OCSPResponseData ::= SEQUENCE {
+ version [0] EXPLICIT OCSPVersion -- DEFAULT v1 -- OPTIONAL,
+ responderID OCSPResponderID,
+ producedAt GeneralizedTime,
+ responses SEQUENCE OF OCSPSingleResponse,
+ responseExtensions [1] EXPLICIT Extensions OPTIONAL }
+
+OCSPBasicOCSPResponse ::= SEQUENCE {
+ tbsResponseData OCSPResponseData,
+ signatureAlgorithm AlgorithmIdentifier,
+ signature BIT STRING,
+ certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+
+-- ArchiveCutoff ::= GeneralizedTime
+
+-- AcceptableResponses ::= SEQUENCE OF OBJECT IDENTIFIER
+
+-- Object Identifiers
+
+id-pkix-ocsp OBJECT IDENTIFIER ::= {
+ iso(1) identified-organization(3) dod(6) internet(1)
+ security(5) mechanisms(5) pkix(7) pkix-ad(48) 1
+}
+
+id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 }
+id-pkix-ocsp-nonce OBJECT IDENTIFIER ::= { id-pkix-ocsp 2 }
+-- id-pkix-ocsp-crl OBJECT IDENTIFIER ::= { id-pkix-ocsp 3 }
+-- id-pkix-ocsp-response OBJECT IDENTIFIER ::= { id-pkix-ocsp 4 }
+-- id-pkix-ocsp-nocheck OBJECT IDENTIFIER ::= { id-pkix-ocsp 5 }
+-- id-pkix-ocsp-archive-cutoff OBJECT IDENTIFIER ::= { id-pkix-ocsp 6 }
+-- id-pkix-ocsp-service-locator OBJECT IDENTIFIER ::= { id-pkix-ocsp 7 }
+
+
+END
+
diff --git a/source4/heimdal/lib/hx509/peer.c b/source4/heimdal/lib/hx509/peer.c
new file mode 100644
index 0000000000..f82f2877f6
--- /dev/null
+++ b/source4/heimdal/lib/hx509/peer.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2006 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: peer.c,v 1.1 2006/11/26 15:49:01 lha Exp $");
+
+int
+hx509_peer_info_alloc(hx509_context context, hx509_peer_info *peer)
+{
+ *peer = calloc(1, sizeof(**peer));
+ if (*peer == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ return 0;
+}
+
+
+static void
+free_cms_alg(hx509_peer_info peer)
+{
+ if (peer->val) {
+ size_t i;
+ for (i = 0; i < peer->len; i++)
+ free_AlgorithmIdentifier(&peer->val[i]);
+ free(peer->val);
+ peer->val = NULL;
+ peer->len = 0;
+ }
+}
+
+int
+hx509_peer_info_free(hx509_peer_info peer)
+{
+ if (peer->cert)
+ hx509_cert_free(peer->cert);
+ free_cms_alg(peer);
+ memset(peer, 0, sizeof(*peer));
+ return 0;
+}
+
+int
+hx509_peer_info_set_cert(hx509_peer_info peer,
+ hx509_cert cert)
+{
+ if (peer->cert)
+ hx509_cert_free(peer->cert);
+ peer->cert = hx509_cert_ref(cert);
+ return 0;
+}
+
+int
+hx509_peer_info_set_cms_algs(hx509_context context,
+ hx509_peer_info peer,
+ const AlgorithmIdentifier *val,
+ size_t len)
+{
+ size_t i;
+
+ free_cms_alg(peer);
+
+ peer->val = calloc(len, sizeof(*peer->val));
+ if (peer->val == NULL) {
+ peer->len = 0;
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ peer->len = len;
+ for (i = 0; i < len; i++) {
+ int ret;
+ ret = copy_AlgorithmIdentifier(&val[i], &peer->val[i]);
+ if (ret) {
+ hx509_clear_error_string(context);
+ free_cms_alg(peer);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+#if 0
+
+/*
+ * S/MIME
+ */
+
+int
+hx509_peer_info_parse_smime(hx509_peer_info peer,
+ const heim_octet_string *data)
+{
+ return 0;
+}
+
+int
+hx509_peer_info_unparse_smime(hx509_peer_info peer,
+ heim_octet_string *data)
+{
+ return 0;
+}
+
+/*
+ * For storing hx509_peer_info to be able to cache them.
+ */
+
+int
+hx509_peer_info_parse(hx509_peer_info peer,
+ const heim_octet_string *data)
+{
+ return 0;
+}
+
+int
+hx509_peer_info_unparse(hx509_peer_info peer,
+ heim_octet_string *data)
+{
+ return 0;
+}
+#endif
diff --git a/source4/heimdal/lib/hx509/pkcs10.asn1 b/source4/heimdal/lib/hx509/pkcs10.asn1
new file mode 100644
index 0000000000..c33fd36cb2
--- /dev/null
+++ b/source4/heimdal/lib/hx509/pkcs10.asn1
@@ -0,0 +1,25 @@
+-- $Id: pkcs10.asn1,v 1.1 2006/04/01 09:46:57 lha Exp $
+PKCS10 DEFINITIONS ::=
+
+BEGIN
+
+IMPORTS
+ Name, SubjectPublicKeyInfo, Attribute, AlgorithmIdentifier
+ FROM rfc2459;
+
+
+CertificationRequestInfo ::= SEQUENCE {
+ version INTEGER { pkcs10-v1(0) },
+ subject Name,
+ subjectPKInfo SubjectPublicKeyInfo,
+ attributes [0] IMPLICIT SET OF Attribute OPTIONAL
+}
+
+CertificationRequest ::= SEQUENCE {
+ certificationRequestInfo CertificationRequestInfo,
+ signatureAlgorithm AlgorithmIdentifier,
+ signature BIT STRING
+}
+
+END
+
diff --git a/source4/heimdal/lib/hx509/print.c b/source4/heimdal/lib/hx509/print.c
new file mode 100644
index 0000000000..802ac12b4e
--- /dev/null
+++ b/source4/heimdal/lib/hx509/print.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 2004 - 2006 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: print.c,v 1.15 2006/12/07 20:37:57 lha Exp $");
+
+
+struct hx509_validate_ctx_data {
+ int flags;
+ hx509_vprint_func vprint_func;
+ void *ctx;
+};
+
+/*
+ *
+ */
+
+static int
+Time2string(const Time *T, char **str)
+{
+ time_t t;
+ char *s;
+ struct tm *tm;
+
+ *str = NULL;
+ t = _hx509_Time2time_t(T);
+ tm = gmtime (&t);
+ s = malloc(30);
+ if (s == NULL)
+ return ENOMEM;
+ strftime(s, 30, "%Y-%m-%d %H:%M:%S", tm);
+ *str = s;
+ return 0;
+}
+
+void
+hx509_print_stdout(void *ctx, const char *fmt, va_list va)
+{
+ FILE *f = ctx;
+ vfprintf(f, fmt, va);
+}
+
+void
+hx509_print_func(hx509_vprint_func func, void *ctx, const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ (*func)(ctx, fmt, va);
+ va_end(va);
+}
+
+int
+hx509_oid_sprint(const heim_oid *oid, char **str)
+{
+ return der_print_heim_oid(oid, '.', str);
+}
+
+void
+hx509_oid_print(const heim_oid *oid, hx509_vprint_func func, void *ctx)
+{
+ char *str;
+ hx509_oid_sprint(oid, &str);
+ hx509_print_func(func, ctx, "%s", str);
+ free(str);
+}
+
+void
+hx509_bitstring_print(const heim_bit_string *b,
+ hx509_vprint_func func, void *ctx)
+{
+ int i;
+ hx509_print_func(func, ctx, "\tlength: %d\n\t", b->length);
+ for (i = 0; i < (b->length + 7) / 8; i++)
+ hx509_print_func(func, ctx, "%02x%s%s",
+ ((unsigned char *)b->data)[i],
+ i < (b->length - 7) / 8
+ && (i == 0 || (i % 16) != 15) ? ":" : "",
+ i != 0 && (i % 16) == 15 ?
+ (i <= ((b->length + 7) / 8 - 2) ? "\n\t" : "\n"):"");
+}
+
+int
+hx509_cert_keyusage_print(hx509_context context, hx509_cert c, char **s)
+{
+ KeyUsage ku;
+ char buf[256];
+ int ret;
+
+ *s = NULL;
+
+ ret = _hx509_cert_get_keyusage(context, c, &ku);
+ if (ret)
+ return ret;
+ unparse_flags(KeyUsage2int(ku), asn1_KeyUsage_units(), buf, sizeof(buf));
+ *s = strdup(buf);
+ if (*s == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static void
+validate_vprint(void *c, const char *fmt, va_list va)
+{
+ hx509_validate_ctx ctx = c;
+ if (ctx->vprint_func == NULL)
+ return;
+ (ctx->vprint_func)(ctx->ctx, fmt, va);
+}
+
+static void
+validate_print(hx509_validate_ctx ctx, int flags, const char *fmt, ...)
+{
+ va_list va;
+ if ((ctx->flags & flags) == 0)
+ return;
+ va_start(va, fmt);
+ validate_vprint(ctx, fmt, va);
+ va_end(va);
+}
+
+enum critical_flag { D_C = 0, S_C, S_N_C, M_C, M_N_C };
+
+static int
+check_Null(hx509_validate_ctx ctx, enum critical_flag cf, const Extension *e)
+{
+ switch(cf) {
+ case D_C:
+ break;
+ case S_C:
+ if (!e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tCritical not set on SHOULD\n");
+ break;
+ case S_N_C:
+ if (e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tCritical set on SHOULD NOT\n");
+ break;
+ case M_C:
+ if (!e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tCritical not set on MUST\n");
+ break;
+ case M_N_C:
+ if (e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tCritical set on MUST NOT\n");
+ break;
+ default:
+ _hx509_abort("internal check_Null state error");
+ }
+ return 0;
+}
+
+static int
+check_subjectKeyIdentifier(hx509_validate_ctx ctx,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ check_Null(ctx, cf, e);
+ return 0;
+}
+
+static int
+check_pkinit_san(hx509_validate_ctx ctx, heim_any *a)
+{
+ KRB5PrincipalName kn;
+ unsigned i;
+ size_t size;
+ int ret;
+
+ ret = decode_KRB5PrincipalName(a->data, a->length,
+ &kn, &size);
+ if (ret) {
+ printf("Decoding kerberos name in SAN failed: %d", ret);
+ return 1;
+ }
+
+ if (size != a->length) {
+ printf("Decoding kerberos name have extra bits on the end");
+ return 1;
+ }
+
+ /* print kerberos principal, add code to quote / within components */
+ for (i = 0; i < kn.principalName.name_string.len; i++) {
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s",
+ kn.principalName.name_string.val[i]);
+ if (i + 1 < kn.principalName.name_string.len)
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "/");
+ }
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "@");
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s", kn.realm);
+
+ free_KRB5PrincipalName(&kn);
+ return 0;
+}
+
+static int
+check_dnssrv_san(hx509_validate_ctx ctx, heim_any *a)
+{
+ return 0;
+}
+
+struct {
+ const char *name;
+ const heim_oid *(*oid)(void);
+ int (*func)(hx509_validate_ctx, heim_any *);
+} check_altname[] = {
+ { "pk-init", oid_id_pkinit_san, check_pkinit_san },
+ { "dns-srv", oid_id_pkix_on_dnsSRV, check_dnssrv_san }
+};
+
+static int
+check_altName(hx509_validate_ctx ctx,
+ const char *name,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ GeneralNames gn;
+ size_t size;
+ int ret, i;
+
+ check_Null(ctx, cf, e);
+
+ if (e->extnValue.length == 0) {
+ printf("%sAltName empty, not allowed", name);
+ return 1;
+ }
+ ret = decode_GeneralNames(e->extnValue.data, e->extnValue.length,
+ &gn, &size);
+ if (ret) {
+ printf("\tret = %d while decoding %s GeneralNames\n", ret, name);
+ return 1;
+ }
+ if (gn.len == 0) {
+ printf("%sAltName generalName empty, not allowed", name);
+ return 1;
+ }
+
+ for (i = 0; i < gn.len; i++) {
+ switch (gn.val[i].element) {
+ case choice_GeneralName_otherName: {
+ unsigned j;
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%sAltName otherName ", name);
+
+ for (j = 0; j < sizeof(check_altname)/sizeof(check_altname[0]); j++) {
+ if (der_heim_oid_cmp((*check_altname[j].oid)(),
+ &gn.val[i].u.otherName.type_id) != 0)
+ continue;
+
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s: ",
+ check_altname[j].name);
+ (*check_altname[j].func)(ctx, &gn.val[i].u.otherName.value);
+ break;
+ }
+ if (j == sizeof(check_altname)/sizeof(check_altname[0])) {
+ hx509_oid_print(&gn.val[i].u.otherName.type_id,
+ validate_vprint, ctx);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, " unknown");
+ }
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\n");
+ break;
+ }
+ case choice_GeneralName_rfc822Name:
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "rfc822Name: %s\n",
+ gn.val[i].u.rfc822Name);
+ break;
+ case choice_GeneralName_dNSName:
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "dNSName: %s\n",
+ gn.val[i].u.dNSName);
+ break;
+ case choice_GeneralName_directoryName: {
+ Name dir;
+ char *s;
+ dir.element = gn.val[i].u.directoryName.element;
+ dir.u.rdnSequence = gn.val[i].u.directoryName.u.rdnSequence;
+ ret = _hx509_unparse_Name(&dir, &s);
+ if (ret) {
+ printf("unable to parse %sAltName directoryName\n", name);
+ return 1;
+ }
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "directoryName: %s\n", s);
+ free(s);
+ break;
+ }
+ case choice_GeneralName_uniformResourceIdentifier:
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "uri: %s\n",
+ gn.val[i].u.uniformResourceIdentifier);
+ break;
+ case choice_GeneralName_iPAddress:
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "ip address\n");
+ break;
+ case choice_GeneralName_registeredID:
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "registered id: ");
+ hx509_oid_print(&gn.val[i].u.registeredID,
+ validate_vprint, ctx);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\n");
+ break;
+ }
+ }
+
+ free_GeneralNames(&gn);
+
+ return 0;
+}
+
+static int
+check_subjectAltName(hx509_validate_ctx ctx,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ return check_altName(ctx, "subject", cf, e);
+}
+
+static int
+check_issuerAltName(hx509_validate_ctx ctx,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ return check_altName(ctx, "issuer", cf, e);
+}
+
+
+static int
+check_basicConstraints(hx509_validate_ctx ctx,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ BasicConstraints b;
+ size_t size;
+ int ret;
+
+ check_Null(ctx, cf, e);
+
+ ret = decode_BasicConstraints(e->extnValue.data, e->extnValue.length,
+ &b, &size);
+ if (ret) {
+ printf("\tret = %d while decoding BasicConstraints\n", ret);
+ return 0;
+ }
+ if (size != e->extnValue.length)
+ printf("\tlength of der data isn't same as extension\n");
+
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tis %sa CA\n", b.cA && *b.cA ? "" : "NOT ");
+ if (b.pathLenConstraint)
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tpathLenConstraint: %d\n", *b.pathLenConstraint);
+
+ return 0;
+}
+
+struct {
+ const char *name;
+ const heim_oid *(*oid)(void);
+ int (*func)(hx509_validate_ctx ctx,
+ enum critical_flag cf,
+ const Extension *);
+ enum critical_flag cf;
+} check_extension[] = {
+#define ext(name, checkname) #name, &oid_id_x509_ce_##name, check_##checkname
+ { ext(subjectDirectoryAttributes, Null), M_N_C },
+ { ext(subjectKeyIdentifier, subjectKeyIdentifier), M_N_C },
+ { ext(keyUsage, Null), S_C },
+ { ext(subjectAltName, subjectAltName), M_N_C },
+ { ext(issuerAltName, issuerAltName), S_N_C },
+ { ext(basicConstraints, basicConstraints), M_C },
+ { ext(cRLNumber, Null), M_N_C },
+ { ext(cRLReason, Null), M_N_C },
+ { ext(holdInstructionCode, Null), M_N_C },
+ { ext(invalidityDate, Null), M_N_C },
+ { ext(deltaCRLIndicator, Null), M_C },
+ { ext(issuingDistributionPoint, Null), M_C },
+ { ext(certificateIssuer, Null), M_C },
+ { ext(nameConstraints, Null), M_C },
+ { ext(cRLDistributionPoints, Null), S_N_C },
+ { ext(certificatePolicies, Null) },
+ { ext(policyMappings, Null), M_N_C },
+ { ext(authorityKeyIdentifier, Null), M_N_C },
+ { ext(policyConstraints, Null), D_C },
+ { ext(extKeyUsage, Null), D_C },
+ { ext(freshestCRL, Null), M_N_C },
+ { ext(inhibitAnyPolicy, Null), M_C },
+ { NULL }
+};
+
+int
+hx509_validate_ctx_init(hx509_context context, hx509_validate_ctx *ctx)
+{
+ *ctx = malloc(sizeof(**ctx));
+ if (*ctx == NULL)
+ return ENOMEM;
+ memset(*ctx, 0, sizeof(**ctx));
+ return 0;
+}
+
+void
+hx509_validate_ctx_set_print(hx509_validate_ctx ctx,
+ hx509_vprint_func func,
+ void *c)
+{
+ ctx->vprint_func = func;
+ ctx->ctx = c;
+}
+
+void
+hx509_validate_ctx_add_flags(hx509_validate_ctx ctx, int flags)
+{
+ ctx->flags |= flags;
+}
+
+void
+hx509_validate_ctx_free(hx509_validate_ctx ctx)
+{
+ free(ctx);
+}
+
+int
+hx509_validate_cert(hx509_context context,
+ hx509_validate_ctx ctx,
+ hx509_cert cert)
+{
+ Certificate *c = _hx509_get_cert(cert);
+ TBSCertificate *t = &c->tbsCertificate;
+ hx509_name name;
+ char *str;
+
+ if (_hx509_cert_get_version(c) != 3)
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "Not version 3 certificate\n");
+
+ if (t->version && *t->version < 2 && t->extensions)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Not version 3 certificate with extensions\n");
+
+ _hx509_name_from_Name(&t->subject, &name);
+ hx509_name_to_string(name, &str);
+ hx509_name_free(&name);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "subject name: %s\n", str);
+ free(str);
+
+ _hx509_name_from_Name(&t->issuer, &name);
+ hx509_name_to_string(name, &str);
+ hx509_name_free(&name);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "issuer name: %s\n", str);
+ free(str);
+
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "Validity:\n");
+
+ Time2string(&t->validity.notBefore, &str);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tnotBefore %s\n", str);
+ free(str);
+ Time2string(&t->validity.notAfter, &str);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tnotAfter %s\n", str);
+ free(str);
+
+ if (t->extensions) {
+ int i, j;
+
+ if (t->extensions->len == 0) {
+ validate_print(ctx,
+ HX509_VALIDATE_F_VALIDATE|HX509_VALIDATE_F_VERBOSE,
+ "The empty extensions list is not "
+ "allowed by PKIX\n");
+ }
+
+ for (i = 0; i < t->extensions->len; i++) {
+
+ for (j = 0; check_extension[j].name; j++)
+ if (der_heim_oid_cmp((*check_extension[j].oid)(),
+ &t->extensions->val[i].extnID) == 0)
+ break;
+ if (check_extension[j].name == NULL) {
+ int flags = HX509_VALIDATE_F_VERBOSE;
+ if (t->extensions->val[i].critical)
+ flags |= HX509_VALIDATE_F_VALIDATE;
+ validate_print(ctx, flags, "don't know what ");
+ if (t->extensions->val[i].critical)
+ validate_print(ctx, flags, "and is CRITICAL ");
+ if (ctx->flags & flags)
+ hx509_oid_print(&t->extensions->val[i].extnID,
+ validate_vprint, ctx);
+ validate_print(ctx, flags, " is\n");
+ continue;
+ }
+ validate_print(ctx,
+ HX509_VALIDATE_F_VALIDATE|HX509_VALIDATE_F_VERBOSE,
+ "checking extention: %s\n",
+ check_extension[j].name);
+ (*check_extension[j].func)(ctx,
+ check_extension[j].cf,
+ &t->extensions->val[i]);
+ }
+ } else
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "no extentions\n");
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/hx509/req.c b/source4/heimdal/lib/hx509/req.c
new file mode 100644
index 0000000000..ca7baa514b
--- /dev/null
+++ b/source4/heimdal/lib/hx509/req.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2006 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 <pkcs10_asn1.h>
+RCSID("$Id: req.c,v 1.7 2007/01/04 20:20:11 lha Exp $");
+
+struct hx509_request_data {
+ hx509_name name;
+ SubjectPublicKeyInfo key;
+ ExtKeyUsage eku;
+ GeneralNames san;
+};
+
+/*
+ *
+ */
+
+int
+_hx509_request_init(hx509_context context, hx509_request *req)
+{
+ *req = calloc(1, sizeof(**req));
+ if (*req == NULL)
+ return ENOMEM;
+
+ return 0;
+}
+
+void
+_hx509_request_free(hx509_request *req)
+{
+ if ((*req)->name)
+ hx509_name_free(&(*req)->name);
+ free_SubjectPublicKeyInfo(&(*req)->key);
+ free_ExtKeyUsage(&(*req)->eku);
+ free_GeneralNames(&(*req)->san);
+ memset(*req, 0, sizeof(**req));
+ free(*req);
+ *req = NULL;
+}
+
+int
+_hx509_request_set_name(hx509_context context,
+ hx509_request req,
+ hx509_name name)
+{
+ if (req->name)
+ hx509_name_free(&req->name);
+ if (name) {
+ int ret = hx509_name_copy(context, name, &req->name);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+int
+_hx509_request_set_SubjectPublicKeyInfo(hx509_context context,
+ hx509_request req,
+ const SubjectPublicKeyInfo *key)
+{
+ free_SubjectPublicKeyInfo(&req->key);
+ return copy_SubjectPublicKeyInfo(key, &req->key);
+}
+
+int
+_hx509_request_add_eku(hx509_context context,
+ hx509_request req,
+ const heim_oid *oid)
+{
+ void *val;
+ int ret;
+
+ val = realloc(req->eku.val, sizeof(req->eku.val[0]) * (req->eku.len + 1));
+ if (val == NULL)
+ return ENOMEM;
+ req->eku.val = val;
+
+ ret = der_copy_oid(oid, &req->eku.val[req->eku.len]);
+ if (ret)
+ return ret;
+
+ req->eku.len += 1;
+
+ return 0;
+}
+
+int
+_hx509_request_add_dns_name(hx509_context context,
+ hx509_request req,
+ const char *hostname)
+{
+ GeneralName name;
+
+ memset(&name, 0, sizeof(name));
+ name.element = choice_GeneralName_dNSName;
+ name.u.dNSName = rk_UNCONST(hostname);
+
+ return add_GeneralNames(&req->san, &name);
+}
+
+int
+_hx509_request_add_email(hx509_context context,
+ hx509_request req,
+ const char *email)
+{
+ GeneralName name;
+
+ memset(&name, 0, sizeof(name));
+ name.element = choice_GeneralName_rfc822Name;
+ name.u.dNSName = rk_UNCONST(email);
+
+ return add_GeneralNames(&req->san, &name);
+}
+
+
+
+int
+_hx509_request_to_pkcs10(hx509_context context,
+ const hx509_request req,
+ const hx509_private_key signer,
+ heim_octet_string *request)
+{
+ CertificationRequest r;
+ heim_octet_string data, os;
+ int ret;
+ size_t size;
+
+ if (req->name == NULL) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "PKCS10 needs to have a subject");
+ return EINVAL;
+ }
+
+ memset(&r, 0, sizeof(r));
+ memset(request, 0, sizeof(*request));
+
+ r.certificationRequestInfo.version = pkcs10_v1;
+
+ ret = copy_Name(&req->name->der_name,
+ &r.certificationRequestInfo.subject);
+ if (ret)
+ goto out;
+ ret = copy_SubjectPublicKeyInfo(&req->key,
+ &r.certificationRequestInfo.subjectPKInfo);
+ if (ret)
+ goto out;
+ r.certificationRequestInfo.attributes =
+ calloc(1, sizeof(*r.certificationRequestInfo.attributes));
+ if (r.certificationRequestInfo.attributes == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(CertificationRequestInfo, data.data, data.length,
+ &r.certificationRequestInfo, &size, ret);
+ if (ret)
+ goto out;
+ if (data.length != size)
+ abort();
+
+ ret = _hx509_create_signature(context,
+ signer,
+ hx509_signature_rsa_with_sha1(),
+ &data,
+ &r.signatureAlgorithm,
+ &os);
+ free(data.data);
+ if (ret)
+ goto out;
+ r.signature.data = os.data;
+ r.signature.length = os.length * 8;
+
+ ASN1_MALLOC_ENCODE(CertificationRequest, data.data, data.length,
+ &r, &size, ret);
+ if (ret)
+ goto out;
+ if (data.length != size)
+ abort();
+
+ *request = data;
+
+out:
+ free_CertificationRequest(&r);
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/hx509/revoke.c b/source4/heimdal/lib/hx509/revoke.c
new file mode 100644
index 0000000000..8067b29c10
--- /dev/null
+++ b/source4/heimdal/lib/hx509/revoke.c
@@ -0,0 +1,1020 @@
+/*
+ * Copyright (c) 2006 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: revoke.c,v 1.32 2006/12/30 17:09:06 lha Exp $");
+
+struct revoke_crl {
+ char *path;
+ time_t last_modfied;
+ CRLCertificateList crl;
+ int verified;
+};
+
+struct revoke_ocsp {
+ char *path;
+ time_t last_modfied;
+ OCSPBasicOCSPResponse ocsp;
+ hx509_certs certs;
+ hx509_cert signer;
+};
+
+
+struct hx509_revoke_ctx_data {
+ struct {
+ struct revoke_crl *val;
+ size_t len;
+ } crls;
+ struct {
+ struct revoke_ocsp *val;
+ size_t len;
+ } ocsps;
+};
+
+int
+hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
+{
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL)
+ return ENOMEM;
+
+ (*ctx)->crls.len = 0;
+ (*ctx)->crls.val = NULL;
+ (*ctx)->ocsps.len = 0;
+ (*ctx)->ocsps.val = NULL;
+
+ return 0;
+}
+
+static void
+free_ocsp(struct revoke_ocsp *ocsp)
+{
+ free(ocsp->path);
+ free_OCSPBasicOCSPResponse(&ocsp->ocsp);
+ hx509_certs_free(&ocsp->certs);
+ hx509_cert_free(ocsp->signer);
+}
+
+void
+hx509_revoke_free(hx509_revoke_ctx *ctx)
+{
+ size_t i ;
+
+ if (ctx == NULL || *ctx == NULL)
+ return;
+
+ for (i = 0; i < (*ctx)->crls.len; i++) {
+ free((*ctx)->crls.val[i].path);
+ free_CRLCertificateList(&(*ctx)->crls.val[i].crl);
+ }
+
+ for (i = 0; i < (*ctx)->ocsps.len; i++)
+ free_ocsp(&(*ctx)->ocsps.val[i]);
+ free((*ctx)->ocsps.val);
+
+ free((*ctx)->crls.val);
+
+ memset(*ctx, 0, sizeof(**ctx));
+ free(*ctx);
+ *ctx = NULL;
+}
+
+static int
+verify_ocsp(hx509_context context,
+ struct revoke_ocsp *ocsp,
+ time_t time_now,
+ hx509_certs certs,
+ hx509_cert parent)
+{
+ hx509_cert signer = NULL;
+ hx509_query q;
+ int ret;
+
+ _hx509_query_clear(&q);
+
+ /*
+ * Need to match on issuer too in case there are two CA that have
+ * issued the same name to a certificate. One example of this is
+ * the www.openvalidation.org test's ocsp validator.
+ */
+
+ q.match = HX509_QUERY_MATCH_ISSUER_NAME;
+ q.issuer_name = &_hx509_get_cert(parent)->tbsCertificate.issuer;
+
+ switch(ocsp->ocsp.tbsResponseData.responderID.element) {
+ case choice_OCSPResponderID_byName:
+ q.match |= HX509_QUERY_MATCH_SUBJECT_NAME;
+ q.subject_name = &ocsp->ocsp.tbsResponseData.responderID.u.byName;
+ break;
+ case choice_OCSPResponderID_byKey:
+ q.match |= HX509_QUERY_MATCH_KEY_HASH_SHA1;
+ q.keyhash_sha1 = &ocsp->ocsp.tbsResponseData.responderID.u.byKey;
+ break;
+ }
+
+ ret = hx509_certs_find(context, certs, &q, &signer);
+ if (ret && ocsp->certs)
+ ret = hx509_certs_find(context, ocsp->certs, &q, &signer);
+ if (ret)
+ goto out;
+
+ /*
+ * If signer certificate isn't the CA certificate, lets check the
+ * its the CA that signed the signer certificate and the OCSP EKU
+ * is set.
+ */
+ if (hx509_cert_cmp(signer, parent) != 0) {
+ Certificate *p = _hx509_get_cert(parent);
+ Certificate *s = _hx509_get_cert(signer);
+
+ ret = _hx509_cert_is_parent_cmp(s, p, 0);
+ if (ret != 0) {
+ ret = HX509_PARENT_NOT_CA;
+ hx509_set_error_string(context, 0, ret, "Revoke OSCP signer is "
+ "doesn't have CA as signer certificate");
+ goto out;
+ }
+
+ ret = _hx509_verify_signature_bitstring(context,
+ p,
+ &s->signatureAlgorithm,
+ &s->tbsCertificate._save,
+ &s->signatureValue);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "OSCP signer signature invalid");
+ goto out;
+ }
+
+ ret = hx509_cert_check_eku(context, signer,
+ oid_id_pkix_kp_OCSPSigning(), 0);
+ if (ret)
+ goto out;
+ }
+
+ ret = _hx509_verify_signature_bitstring(context,
+ _hx509_get_cert(signer),
+ &ocsp->ocsp.signatureAlgorithm,
+ &ocsp->ocsp.tbsResponseData._save,
+ &ocsp->ocsp.signature);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "OSCP signature invalid");
+ goto out;
+ }
+
+ ocsp->signer = signer;
+ signer = NULL;
+out:
+ if (signer)
+ hx509_cert_free(signer);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int
+parse_ocsp_basic(const void *data, size_t length, OCSPBasicOCSPResponse *basic)
+{
+ OCSPResponse resp;
+ size_t size;
+ int ret;
+
+ memset(basic, 0, sizeof(*basic));
+
+ ret = decode_OCSPResponse(data, length, &resp, &size);
+ if (ret)
+ return ret;
+ if (length != size) {
+ free_OCSPResponse(&resp);
+ return ASN1_EXTRA_DATA;
+ }
+
+ switch (resp.responseStatus) {
+ case successful:
+ break;
+ default:
+ free_OCSPResponse(&resp);
+ return HX509_REVOKE_WRONG_DATA;
+ }
+
+ if (resp.responseBytes == NULL) {
+ free_OCSPResponse(&resp);
+ return EINVAL;
+ }
+
+ ret = der_heim_oid_cmp(&resp.responseBytes->responseType,
+ oid_id_pkix_ocsp_basic());
+ if (ret != 0) {
+ free_OCSPResponse(&resp);
+ return HX509_REVOKE_WRONG_DATA;
+ }
+
+ ret = decode_OCSPBasicOCSPResponse(resp.responseBytes->response.data,
+ resp.responseBytes->response.length,
+ basic,
+ &size);
+ if (ret) {
+ free_OCSPResponse(&resp);
+ return ret;
+ }
+ if (size != resp.responseBytes->response.length) {
+ free_OCSPResponse(&resp);
+ free_OCSPBasicOCSPResponse(basic);
+ return ASN1_EXTRA_DATA;
+ }
+ free_OCSPResponse(&resp);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
+{
+ OCSPBasicOCSPResponse basic;
+ hx509_certs certs = NULL;
+ size_t length;
+ struct stat sb;
+ void *data;
+ int ret;
+
+ ret = _hx509_map_file(ocsp->path, &data, &length, &sb);
+ if (ret)
+ return ret;
+
+ ret = parse_ocsp_basic(data, length, &basic);
+ _hx509_unmap_file(data, length);
+ if (ret)
+ return ret;
+
+ if (basic.certs) {
+ int i;
+
+ ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0,
+ NULL, &certs);
+ if (ret) {
+ free_OCSPBasicOCSPResponse(&basic);
+ return ret;
+ }
+
+ for (i = 0; i < basic.certs->len; i++) {
+ hx509_cert c;
+
+ ret = hx509_cert_init(context, &basic.certs->val[i], &c);
+ if (ret)
+ continue;
+
+ ret = hx509_certs_add(context, certs, c);
+ hx509_cert_free(c);
+ if (ret)
+ continue;
+ }
+ }
+
+ ocsp->last_modfied = sb.st_mtime;
+
+ free_OCSPBasicOCSPResponse(&ocsp->ocsp);
+ hx509_certs_free(&ocsp->certs);
+ hx509_cert_free(ocsp->signer);
+
+ ocsp->ocsp = basic;
+ ocsp->certs = certs;
+ ocsp->signer = NULL;
+
+ return 0;
+}
+
+int
+hx509_revoke_add_ocsp(hx509_context context,
+ hx509_revoke_ctx ctx,
+ const char *path)
+{
+ void *data;
+ int ret;
+ size_t i;
+
+ if (strncmp(path, "FILE:", 5) != 0) {
+ hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
+ "unsupport type in %s", path);
+ return HX509_UNSUPPORTED_OPERATION;
+ }
+
+ path += 5;
+
+ for (i = 0; i < ctx->ocsps.len; i++) {
+ if (strcmp(ctx->ocsps.val[0].path, path) == 0)
+ return 0;
+ }
+
+ data = realloc(ctx->ocsps.val,
+ (ctx->ocsps.len + 1) * sizeof(ctx->ocsps.val[0]));
+ if (data == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ctx->ocsps.val = data;
+
+ memset(&ctx->ocsps.val[ctx->ocsps.len], 0,
+ sizeof(ctx->ocsps.val[0]));
+
+ ctx->ocsps.val[ctx->ocsps.len].path = strdup(path);
+ if (ctx->ocsps.val[ctx->ocsps.len].path == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ret = load_ocsp(context, &ctx->ocsps.val[ctx->ocsps.len]);
+ if (ret) {
+ free(ctx->ocsps.val[ctx->ocsps.len].path);
+ return ret;
+ }
+ ctx->ocsps.len++;
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int
+verify_crl(hx509_context context,
+ CRLCertificateList *crl,
+ time_t time_now,
+ hx509_certs certs,
+ hx509_cert parent)
+{
+ hx509_cert signer;
+ hx509_query q;
+ time_t t;
+ int ret;
+
+ t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate);
+ if (t > time_now)
+ return HX509_CRL_USED_BEFORE_TIME;
+
+ if (crl->tbsCertList.nextUpdate == NULL)
+ return HX509_CRL_INVALID_FORMAT;
+
+ t = _hx509_Time2time_t(crl->tbsCertList.nextUpdate);
+ if (t < time_now)
+ return HX509_CRL_USED_AFTER_TIME;
+
+ _hx509_query_clear(&q);
+
+ q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
+ q.subject_name = &crl->tbsCertList.issuer;
+
+ ret = hx509_certs_find(context, certs, &q, &signer);
+ if (ret)
+ return ret;
+
+ /* verify is parent or CRLsigner */
+ if (hx509_cert_cmp(signer, parent) != 0) {
+ Certificate *p = _hx509_get_cert(parent);
+ Certificate *s = _hx509_get_cert(signer);
+
+ ret = _hx509_cert_is_parent_cmp(s, p, 0);
+ if (ret != 0) {
+ ret = HX509_PARENT_NOT_CA;
+ hx509_set_error_string(context, 0, ret, "Revoke CRL signer is "
+ "doesn't have CA as signer certificate");
+ goto out;
+ }
+
+ ret = _hx509_verify_signature_bitstring(context,
+ p,
+ &s->signatureAlgorithm,
+ &s->tbsCertificate._save,
+ &s->signatureValue);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "CRL signer signature invalid");
+ goto out;
+ }
+
+ ret = _hx509_check_key_usage(context, signer, 1 << 6, TRUE); /* crl */
+ if (ret != 0)
+ goto out;
+ }
+
+ ret = _hx509_verify_signature_bitstring(context,
+ _hx509_get_cert(signer),
+ &crl->signatureAlgorithm,
+ &crl->tbsCertList._save,
+ &crl->signatureValue);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret, "CRL signature invalid");
+ goto out;
+ }
+
+out:
+ hx509_cert_free(signer);
+
+ return ret;
+}
+
+static int
+load_crl(const char *path, time_t *t, CRLCertificateList *crl)
+{
+ size_t length, size;
+ struct stat sb;
+ void *data;
+ int ret;
+
+ memset(crl, 0, sizeof(*crl));
+
+ ret = _hx509_map_file(path, &data, &length, &sb);
+ if (ret)
+ return ret;
+
+ *t = sb.st_mtime;
+
+ ret = decode_CRLCertificateList(data, length, crl, &size);
+ _hx509_unmap_file(data, length);
+ if (ret)
+ return ret;
+
+ /* check signature is aligned */
+ if (crl->signatureValue.length & 7) {
+ free_CRLCertificateList(crl);
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+ return 0;
+}
+
+int
+hx509_revoke_add_crl(hx509_context context,
+ hx509_revoke_ctx ctx,
+ const char *path)
+{
+ void *data;
+ size_t i;
+ int ret;
+
+ if (strncmp(path, "FILE:", 5) != 0) {
+ hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
+ "unsupport type in %s", path);
+ return HX509_UNSUPPORTED_OPERATION;
+ }
+
+
+ path += 5;
+
+ for (i = 0; i < ctx->crls.len; i++) {
+ if (strcmp(ctx->crls.val[0].path, path) == 0)
+ return 0;
+ }
+
+ data = realloc(ctx->crls.val,
+ (ctx->crls.len + 1) * sizeof(ctx->crls.val[0]));
+ if (data == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ ctx->crls.val = data;
+
+ memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));
+
+ ctx->crls.val[ctx->crls.len].path = strdup(path);
+ if (ctx->crls.val[ctx->crls.len].path == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ret = load_crl(path,
+ &ctx->crls.val[ctx->crls.len].last_modfied,
+ &ctx->crls.val[ctx->crls.len].crl);
+ if (ret) {
+ free(ctx->crls.val[ctx->crls.len].path);
+ return ret;
+ }
+
+ ctx->crls.len++;
+
+ return ret;
+}
+
+
+int
+hx509_revoke_verify(hx509_context context,
+ hx509_revoke_ctx ctx,
+ hx509_certs certs,
+ time_t now,
+ hx509_cert cert,
+ hx509_cert parent_cert)
+{
+ const Certificate *c = _hx509_get_cert(cert);
+ const Certificate *p = _hx509_get_cert(parent_cert);
+ unsigned long i, j, k;
+ int ret;
+
+ for (i = 0; i < ctx->ocsps.len; i++) {
+ struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
+ struct stat sb;
+
+ /* check this ocsp apply to this cert */
+
+ /* check if there is a newer version of the file */
+ ret = stat(ocsp->path, &sb);
+ if (ret == 0 && ocsp->last_modfied != sb.st_mtime) {
+ ret = load_ocsp(context, ocsp);
+ if (ret)
+ continue;
+ }
+
+ /* verify signature in ocsp if not already done */
+ if (ocsp->signer == NULL) {
+ ret = verify_ocsp(context, ocsp, now, certs, parent_cert);
+ if (ret)
+ continue;
+ }
+
+ for (i = 0; i < ocsp->ocsp.tbsResponseData.responses.len; i++) {
+ heim_octet_string os;
+
+ ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[i].certID.serialNumber,
+ &c->tbsCertificate.serialNumber);
+ if (ret != 0)
+ continue;
+
+ /* verify issuer hashes hash */
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,
+ &c->tbsCertificate.issuer._save,
+ &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash);
+ if (ret != 0)
+ continue;
+
+ os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
+ os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
+
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,
+ &os,
+ &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerKeyHash);
+ if (ret != 0)
+ continue;
+
+ switch (ocsp->ocsp.tbsResponseData.responses.val[i].certStatus.element) {
+ case choice_OCSPCertStatus_good:
+ break;
+ case choice_OCSPCertStatus_revoked:
+ case choice_OCSPCertStatus_unknown:
+ continue;
+ }
+
+ /* don't allow the update to be in the future */
+ if (ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate >
+ now + context->ocsp_time_diff)
+ continue;
+
+ /* don't allow the next updte to be in the past */
+ if (ocsp->ocsp.tbsResponseData.responses.val[i].nextUpdate) {
+ if (*ocsp->ocsp.tbsResponseData.responses.val[i].nextUpdate < now)
+ continue;
+ } else
+ /* Should force a refetch, but can we ? */;
+
+ return 0;
+ }
+ }
+
+ for (i = 0; i < ctx->crls.len; i++) {
+ struct revoke_crl *crl = &ctx->crls.val[i];
+ struct stat sb;
+
+ /* check if cert.issuer == crls.val[i].crl.issuer */
+ ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
+ &crl->crl.tbsCertList.issuer);
+ if (ret)
+ continue;
+
+ ret = stat(crl->path, &sb);
+ if (ret == 0 && crl->last_modfied != sb.st_mtime) {
+ CRLCertificateList cl;
+
+ ret = load_crl(crl->path, &crl->last_modfied, &cl);
+ if (ret == 0) {
+ free_CRLCertificateList(&crl->crl);
+ crl->crl = cl;
+ crl->verified = 0;
+ }
+ }
+
+ /* verify signature in crl if not already done */
+ if (crl->verified == 0) {
+ ret = verify_crl(context, &crl->crl, now, certs, parent_cert);
+ if (ret)
+ return ret;
+ crl->verified = 1;
+ }
+
+ if (crl->crl.tbsCertList.crlExtensions)
+ for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++)
+ if (crl->crl.tbsCertList.crlExtensions->val[j].critical)
+ return HX509_CRL_UNKNOWN_EXTENSION;
+
+ if (crl->crl.tbsCertList.revokedCertificates == NULL)
+ return 0;
+
+ /* check if cert is in crl */
+ for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
+ time_t t;
+
+ ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
+ &c->tbsCertificate.serialNumber);
+ if (ret != 0)
+ continue;
+
+ t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
+ if (t > now)
+ continue;
+
+ if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)
+ for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)
+ if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
+ return HX509_CRL_UNKNOWN_EXTENSION;
+
+ return HX509_CRL_CERT_REVOKED;
+ }
+
+ return 0;
+ }
+
+
+ if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
+ return 0;
+ return HX509_REVOKE_STATUS_MISSING;
+}
+
+struct ocsp_add_ctx {
+ OCSPTBSRequest *req;
+ hx509_certs certs;
+ const AlgorithmIdentifier *digest;
+ hx509_cert parent;
+};
+
+static int
+add_to_req(hx509_context context, void *ptr, hx509_cert cert)
+{
+ struct ocsp_add_ctx *ctx = ptr;
+ OCSPInnerRequest *one;
+ hx509_cert parent = NULL;
+ Certificate *p, *c = _hx509_get_cert(cert);
+ heim_octet_string os;
+ int ret;
+ hx509_query q;
+ void *d;
+
+ d = realloc(ctx->req->requestList.val,
+ sizeof(ctx->req->requestList.val[0]) *
+ (ctx->req->requestList.len + 1));
+ if (d == NULL)
+ return ENOMEM;
+ ctx->req->requestList.val = d;
+
+ one = &ctx->req->requestList.val[ctx->req->requestList.len];
+ memset(one, 0, sizeof(*one));
+
+ _hx509_query_clear(&q);
+
+ q.match |= HX509_QUERY_FIND_ISSUER_CERT;
+ q.subject = c;
+
+ ret = hx509_certs_find(context, ctx->certs, &q, &parent);
+ if (ret)
+ goto out;
+
+ if (ctx->parent) {
+ if (hx509_cert_cmp(ctx->parent, parent) != 0) {
+ ret = HX509_REVOKE_NOT_SAME_PARENT;
+ hx509_set_error_string(context, 0, ret,
+ "Not same parent certifate as "
+ "last certificate in request");
+ goto out;
+ }
+ } else
+ ctx->parent = hx509_cert_ref(parent);
+
+ p = _hx509_get_cert(parent);
+
+ ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
+ if (ret)
+ goto out;
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ &one->reqCert.hashAlgorithm,
+ &c->tbsCertificate.issuer._save,
+ NULL,
+ &one->reqCert.issuerNameHash);
+ if (ret)
+ goto out;
+
+ os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
+ os.length =
+ p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ &one->reqCert.hashAlgorithm,
+ &os,
+ NULL,
+ &one->reqCert.issuerKeyHash);
+ if (ret)
+ goto out;
+
+ ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
+ &one->reqCert.serialNumber);
+ if (ret)
+ goto out;
+
+ ctx->req->requestList.len++;
+out:
+ hx509_cert_free(parent);
+ if (ret) {
+ free_OCSPInnerRequest(one);
+ memset(one, 0, sizeof(*one));
+ }
+
+ return ret;
+}
+
+
+int
+hx509_ocsp_request(hx509_context context,
+ hx509_certs reqcerts,
+ hx509_certs pool,
+ hx509_cert signer,
+ const AlgorithmIdentifier *digest,
+ heim_octet_string *request,
+ heim_octet_string *nonce)
+{
+ OCSPRequest req;
+ size_t size;
+ int ret;
+ struct ocsp_add_ctx ctx;
+ Extensions *es;
+
+ memset(&req, 0, sizeof(req));
+
+ if (digest == NULL)
+ digest = hx509_signature_sha1();
+
+ ctx.req = &req.tbsRequest;
+ ctx.certs = pool;
+ ctx.digest = digest;
+ ctx.parent = NULL;
+
+ ret = hx509_certs_iter(context, reqcerts, add_to_req, &ctx);
+ hx509_cert_free(ctx.parent);
+ if (ret) {
+ free_OCSPRequest(&req);
+ return ret;
+ }
+
+ if (nonce) {
+
+ req.tbsRequest.requestExtensions =
+ calloc(1, sizeof(*req.tbsRequest.requestExtensions));
+ if (req.tbsRequest.requestExtensions == NULL) {
+ free_OCSPRequest(&req);
+ return ENOMEM;
+ }
+
+ es = req.tbsRequest.requestExtensions;
+
+ es->len = 1;
+ es->val = calloc(es->len, sizeof(es->val[0]));
+
+ ret = der_copy_oid(oid_id_pkix_ocsp_nonce(), &es->val[0].extnID);
+ if (ret)
+ abort();
+
+ es->val[0].extnValue.data = malloc(10);
+ if (es->val[0].extnValue.data == NULL) {
+ free_OCSPRequest(&req);
+ return ENOMEM;
+ }
+ es->val[0].extnValue.length = 10;
+
+ ret = RAND_bytes(es->val[0].extnValue.data,
+ es->val[0].extnValue.length);
+ if (ret != 1) {
+ free_OCSPRequest(&req);
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length,
+ &req, &size, ret);
+ free_OCSPRequest(&req);
+ if (ret)
+ return ret;
+ if (size != request->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+
+ return 0;
+}
+
+static char *
+printable_time(time_t t)
+{
+ static char s[128];
+ strlcpy(s, ctime(&t)+ 4, sizeof(s));
+ s[20] = 0;
+ return s;
+}
+
+int
+hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
+{
+ struct revoke_ocsp ocsp;
+ int ret, i;
+
+ if (out == NULL)
+ out = stdout;
+
+ memset(&ocsp, 0, sizeof(ocsp));
+
+ ocsp.path = strdup(path);
+ if (ocsp.path == NULL)
+ return ENOMEM;
+
+ ret = load_ocsp(context, &ocsp);
+ if (ret) {
+ free_ocsp(&ocsp);
+ return ret;
+ }
+
+ fprintf(out, "signer: ");
+
+ switch(ocsp.ocsp.tbsResponseData.responderID.element) {
+ case choice_OCSPResponderID_byName: {
+ hx509_name n;
+ char *s;
+ _hx509_name_from_Name(&ocsp.ocsp.tbsResponseData.responderID.u.byName, &n);
+ hx509_name_to_string(n, &s);
+ hx509_name_free(&n);
+ fprintf(out, " byName: %s\n", s);
+ free(s);
+ break;
+ }
+ case choice_OCSPResponderID_byKey: {
+ char *s;
+ hex_encode(ocsp.ocsp.tbsResponseData.responderID.u.byKey.data,
+ ocsp.ocsp.tbsResponseData.responderID.u.byKey.length,
+ &s);
+ fprintf(out, " byKey: %s\n", s);
+ free(s);
+ break;
+ }
+ default:
+ _hx509_abort("choice_OCSPResponderID unknown");
+ break;
+ }
+
+ fprintf(out, "producedAt: %s\n",
+ printable_time(ocsp.ocsp.tbsResponseData.producedAt));
+
+ fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);
+
+ for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) {
+ char *status;
+ switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) {
+ case choice_OCSPCertStatus_good:
+ status = "good";
+ break;
+ case choice_OCSPCertStatus_revoked:
+ status = "revoked";
+ break;
+ case choice_OCSPCertStatus_unknown:
+ status = "unknown";
+ break;
+ default:
+ status = "element unknown";
+ }
+
+ fprintf(out, "\t%d. status: %s\n", i, status);
+
+ fprintf(out, "\tthisUpdate: %s\n",
+ printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
+ if (ocsp.ocsp.tbsResponseData.responses.val[i].nextUpdate)
+ fprintf(out, "\tproducedAt: %s\n",
+ printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
+
+ }
+
+ fprintf(out, "appended certs:\n");
+ if (ocsp.certs)
+ ret = hx509_certs_iter(context, ocsp.certs, hx509_ci_print_names, out);
+
+ free_ocsp(&ocsp);
+ return ret;
+}
+
+int
+hx509_ocsp_verify(hx509_context context,
+ time_t now,
+ hx509_cert cert,
+ int flags,
+ const void *data, size_t length,
+ time_t *expiration)
+{
+ const Certificate *c = _hx509_get_cert(cert);
+ OCSPBasicOCSPResponse basic;
+ int ret, i;
+
+ *expiration = 0;
+
+ ret = parse_ocsp_basic(data, length, &basic);
+ if (ret)
+ return ret;
+
+
+ for (i = 0; i < basic.tbsResponseData.responses.len; i++) {
+
+ ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,
+ &c->tbsCertificate.serialNumber);
+ if (ret != 0)
+ continue;
+
+ /* verify issuer hashes hash */
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm,
+ &c->tbsCertificate.issuer._save,
+ &basic.tbsResponseData.responses.val[i].certID.issuerNameHash);
+ if (ret != 0)
+ continue;
+
+ switch (basic.tbsResponseData.responses.val[i].certStatus.element) {
+ case choice_OCSPCertStatus_good:
+ break;
+ case choice_OCSPCertStatus_revoked:
+ case choice_OCSPCertStatus_unknown:
+ continue;
+ }
+
+ /* don't allow the update to be in the future */
+ if (basic.tbsResponseData.responses.val[i].thisUpdate >
+ now + context->ocsp_time_diff)
+ continue;
+
+ /* don't allow the next updte to be in the past */
+ if (basic.tbsResponseData.responses.val[i].nextUpdate) {
+ if (*basic.tbsResponseData.responses.val[i].nextUpdate < now)
+ continue;
+ } else
+ continue;
+
+ *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate;
+
+ return 0;
+ }
+ free_OCSPBasicOCSPResponse(&basic);
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/hx509/test_name.c b/source4/heimdal/lib/hx509/test_name.c
new file mode 100644
index 0000000000..9017e54ab1
--- /dev/null
+++ b/source4/heimdal/lib/hx509/test_name.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2006 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: test_name.c,v 1.6 2006/12/30 23:04:54 lha Exp $");
+
+static int
+test_name(hx509_context context, const char *name)
+{
+ hx509_name n;
+ char *s;
+ int ret;
+
+ ret = hx509_parse_name(context, name, &n);
+ if (ret)
+ return 1;
+
+ ret = hx509_name_to_string(n, &s);
+ if (ret)
+ return 1;
+
+ if (strcmp(s, name) != 0)
+ return 1;
+
+ hx509_name_free(&n);
+ free(s);
+
+ return 0;
+}
+
+static int
+test_name_fail(hx509_context context, const char *name)
+{
+ hx509_name n;
+
+ if (hx509_parse_name(context, name, &n) == HX509_NAME_MALFORMED)
+ return 0;
+ hx509_name_free(&n);
+ return 1;
+}
+
+int
+main(int argc, char **argv)
+{
+ hx509_context context;
+ int ret = 0;
+
+ ret = hx509_context_init(&context);
+ if (ret)
+ errx(1, "hx509_context_init failed with %d", ret);
+
+ ret += test_name(context, "CN=foo,C=SE");
+ ret += test_name(context, "CN=foo,CN=kaka,CN=FOO,DC=ad1,C=SE");
+ ret += test_name(context, "1.2.3.4=foo,C=SE");
+ ret += test_name_fail(context, "=");
+ ret += test_name_fail(context, "CN=foo,=foo");
+ ret += test_name_fail(context, "CN=foo,really-unknown-type=foo");
+
+ hx509_context_free(&context);
+
+ return ret;
+}