summaryrefslogtreecommitdiff
path: root/sbin/isakmpd/x509.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/isakmpd/x509.c')
-rw-r--r--sbin/isakmpd/x509.c1155
1 files changed, 499 insertions, 656 deletions
diff --git a/sbin/isakmpd/x509.c b/sbin/isakmpd/x509.c
index 548f22cf31f..9ecf5c76cd0 100644
--- a/sbin/isakmpd/x509.c
+++ b/sbin/isakmpd/x509.c
@@ -1,8 +1,8 @@
-/* $OpenBSD: x509.c,v 1.11 1999/07/07 22:15:42 niklas Exp $ */
-/* $EOM: x509.c,v 1.16 1999/06/15 11:21:19 niklas Exp $ */
+/* $OpenBSD: x509.c,v 1.12 1999/07/17 21:54:39 niklas Exp $ */
+/* $EOM: x509.c,v 1.17 1999/07/17 20:44:12 niklas Exp $ */
/*
- * Copyright (c) 1998 Niels Provos. All rights reserved.
+ * Copyright (c) 1998, 1999 Niels Provos. All rights reserved.
* Copyright (c) 1999 Niklas Hallqvist. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -38,12 +38,18 @@
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <dirent.h>
#include <fcntl.h>
#include <gmp.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <ssl/x509.h>
+#include <ssl/x509_vfy.h>
+#include <ssl/pem.h>
+
#include "sysdep.h"
#include "conf.h"
@@ -53,842 +59,679 @@
#include "sa.h"
#include "ipsec.h"
#include "log.h"
-#include "asn.h"
-#include "asn_useful.h"
-#include "pkcs.h"
#include "x509.h"
-/* X509 Certificate Handling functions */
+/*
+ * X509_STOREs do not support subjectAltNames, so we have to build
+ * our own hash table.
+ */
-/* Validate the BER Encoding of a RDNSequence in the CERT_REQ payload. */
-int
-x509_certreq_validate (u_int8_t *asn, u_int32_t len)
-{
- struct norm_type name = SEQOF ("issuer", RDNSequence);
- int res = 1;
+/*
+ * XXX Actually this store is not really useful, we never use it as we have
+ * our own hash table. It also gets collisons if we have several certificates
+ * only differing in subjectAltName.
+ */
+static X509_STORE *x509_certs;
+static X509_STORE *x509_cas;
- if (!asn_template_clone (&name, 1)
- || (asn = asn_decode_sequence (asn, len, &name)) == 0)
- {
- log_print ("x509_certreq_validate: can not decode 'acceptable CA' info");
- res = 0;
- }
- asn_free (&name);
+/* Initial number of bits used as hash. */
+#define INITIAL_BUCKET_BITS 6
- return res;
-}
+struct x509_hash {
+ LIST_ENTRY (x509_hash) link;
-/* Decode the BER Encoding of a RDNSequence in the CERT_REQ payload. */
-void *
-x509_certreq_decode (u_int8_t *asn, u_int32_t len)
-{
- struct norm_type aca = SEQOF ("aca", RDNSequence);
- struct norm_type *tmp;
- struct x509_aca naca, *ret;
+ X509 *cert;
+};
- if (!asn_template_clone (&aca, 1)
- || (asn = asn_decode_sequence (asn, len, &aca)) == 0)
- {
- log_print ("x509_certreq_validate: can not decode 'acceptable CA' info");
- goto fail;
- }
- memset (&naca, 0, sizeof (naca));
+static LIST_HEAD (x509_list, x509_hash) *x509_tab;
- tmp = asn_decompose ("aca.RelativeDistinguishedName.AttributeValueAssertion",
- &aca);
- if (!tmp)
- goto fail;
- x509_get_attribval (tmp, &naca.name1);
+/* Works both as a maximum index and a mask. */
+static int bucket_mask;
- tmp = asn_decompose ("aca.RelativeDistinguishedName[1]"
- ".AttributeValueAssertion", &aca);
- if (tmp)
- x509_get_attribval (tmp, &naca.name2);
-
- asn_free (&aca);
+u_int16_t
+x509_hash (u_int8_t *id, size_t len)
+{
+ int i;
+ u_int16_t bucket = 0;
- ret = malloc (sizeof (struct x509_aca));
- if (ret)
- memcpy (ret, &naca, sizeof (struct x509_aca));
- else
+ /* XXX We might resize if we are crossing a certain threshold. */
+ len &= ~2;
+ for (i = 0; i < len; i += 2)
{
- log_error ("x509_certreq_decode: malloc (%d) failed",
- sizeof (struct x509_aca));
- x509_free_aca (&aca);
+ /* Doing it this way avoids alignment problems. */
+ bucket ^= (id[i] + 1) * (id[i + 1] + 257);
}
- return ret;
+ bucket &= bucket_mask;
- fail:
- asn_free (&aca);
- return 0;
+ return bucket;
}
void
-x509_free_aca (void *blob)
+x509_hash_init ()
{
- struct x509_aca *aca = blob;
+ int i;
+
+ bucket_mask = (1 << INITIAL_BUCKET_BITS) - 1;
+ x509_tab = malloc ((bucket_mask + 1) * sizeof (struct x509_list));
+ if (!x509_tab)
+ log_fatal ("x509_hash_init: malloc (%d) failed",
+ (bucket_mask + 1) * sizeof (struct x509_list));
+ for (i = 0; i <= bucket_mask; i++)
+ {
+ LIST_INIT (&x509_tab[i]);
+ }
+}
- if (aca->name1.type)
- free (aca->name1.type);
- if (aca->name1.val)
- free (aca->name1.val);
+/* Lookup a certificate by an ID blob. */
+X509 *
+x509_hash_find (u_int8_t *id, size_t len)
+{
+ struct x509_hash *cert;
+ u_int8_t *cid;
+ size_t clen;
- if (aca->name2.type)
- free (aca->name2.type);
- if (aca->name2.val)
- free (aca->name2.val);
+ for (cert = LIST_FIRST (&x509_tab[x509_hash (id, len)]); cert;
+ cert = LIST_NEXT (cert, link))
+ {
+ if (!x509_cert_get_subject (cert->cert, &cid, &clen))
+ continue;
+
+ if (clen != len || memcmp (id, cid, len) != 0)
+ {
+ free (cid);
+ continue;
+ }
+ free (cid);
+
+ log_debug (LOG_CRYPTO, 70, "x509_hash_find: return X509 %p", cert->cert);
+ return cert->cert;
+ }
+
+ log_debug (LOG_CRYPTO, 70, "x509_hash_find: no certificate matched query");
+ return 0;
}
-/*
- * Obtain a Certificate from an acceptable Certification Authority.
- * XXX This is where all the magic should happen, but yet here
- * you will find nothing.
- */
int
-x509_cert_obtain (struct exchange *exchange, void *data, u_int8_t **cert,
- u_int32_t *certlen)
+x509_hash_enter (X509 *cert)
{
- struct x509_aca *aca = data;
- struct ipsec_exch *ie = exchange->data;
- char *certfile;
- int fd, res = 0;
- struct stat st;
- u_int8_t *id_cert, *asn, *id;
- size_t id_len;
- u_int32_t id_cert_len;
-
- if (aca)
- log_debug (LOG_CRYPTO, 60, "x509_cert_obtain: (%s) %s, (%s) %s",
- asn_parse_objectid (asn_ids, aca->name1.type), aca->name1.val,
- asn_parse_objectid (asn_ids, aca->name2.type), aca->name2.val);
+ u_int16_t bucket = 0;
+ u_int8_t *id;
+ u_int32_t len;
+ struct x509_hash *certh;
- /* XXX This needs to be changed - but how else would I know? */
- switch (ie->ike_auth->id)
+ if (!x509_cert_get_subject (cert, &id, &len))
{
- case IKE_AUTH_RSA_SIG:
- certfile = conf_get_str ("RSA_sig", "cert");
- if (!certfile)
- return 0;
- break;
- default:
+ log_print ("x509_hash_enter: can not retrieve subjectAltName");
return 0;
}
- if (stat (certfile, &st) == -1)
+ certh = malloc (sizeof *certh);
+ if (!certh)
{
- log_error ("x509_cert_obtain: failed to state %s", certfile);
+ free (id);
+ log_error ("x509_hash_enter: malloc (%d) failed", sizeof *certh);
return 0;
}
+ memset (certh, 0, sizeof *certh);
+
+ certh->cert = cert;
- *certlen = st.st_size;
+ bucket = x509_hash (id, len);
+ free (id);
+
+ LIST_INSERT_HEAD (&x509_tab[bucket], certh, link);
+ log_debug (LOG_CRYPTO, 70, "x509_hash_enter: cert %p added to bucket %d",
+ cert, bucket);
+ return 1;
+}
- if ((fd = open (certfile, O_RDONLY)) == -1)
+/* X509 Certificate Handling functions. */
+
+int
+x509_read_from_dir (X509_STORE *ctx, char *name, int hash)
+{
+ DIR *dir;
+ struct dirent *file;
+ BIO *certh;
+ X509 *cert;
+ char fullname[PATH_MAX];
+ int off, size;
+
+ if (strlen (name) >= sizeof fullname - 1)
{
- log_error ("x509_cert_obtain: failed to open %s", certfile);
+ log_print ("x509_read_from_dir: directory name too long");
return 0;
}
-
- *cert = malloc (st.st_size);
- if (!*cert)
- {
- log_error ("x509_cert_obtain: malloc (%d) failed", st.st_size);
- res = 0;
- goto done;
- }
- if (read (fd, *cert, st.st_size) != st.st_size)
+ log_debug (LOG_CRYPTO, 40, "x509_read_from_dir: reading certs from %s",
+ name);
+
+ dir = opendir (name);
+ if (!dir)
{
- log_print ("x509_cert_obtain: cert file ended early");
- free (*cert);
- res = 0;
- goto done;
+ log_error ("x509_read_from_dir: opendir (\"%s\") failed", name);
+ return 0;
}
- /*
- * XXX We assume IPv4 here and a certificate with an extension
- * type of subjectAltName at the end. This can go once the saved
- * certificate is only used with one host with a fixed IP address.
- */
- id = exchange->initiator ? exchange->id_i : exchange->id_r;
- id_len = exchange->initiator ? exchange->id_i_len : exchange->id_r_len;
+ strncpy (fullname, name, sizeof fullname - 1);
+ fullname[sizeof fullname - 1] = 0;
+ off = strlen (fullname);
+ size = sizeof fullname - off - 1;
- /* XXX We need our ID to set that in the cert. */
- if (id)
+ while ((file = readdir (dir)) != NULL)
{
- id += ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;
- id_len -= ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;
+ if (file->d_type != DT_REG && file->d_type != DT_LNK)
+ continue;
- /* Get offset into data structure where the IP is saved. */
- asn = *cert;
- id_cert_len = asn_get_data_len (0, &asn, &id_cert);
- asn = id_cert;
- id_cert_len = asn_get_data_len (0, &asn, &id_cert);
- id_cert += id_cert_len - 4;
- memcpy (id_cert, id, 4);
- }
+ log_debug (LOG_CRYPTO, 60, "x509_read_from_dir: reading certificate %s",
+ file->d_name);
- res = 1;
+ certh = BIO_new (BIO_s_file ());
+ if (!certh)
+ {
+ log_error ("x509_read_from_dir: BIO_new (BIO_s_file ()) failed");
+ continue;
+ }
- done:
- close (fd);
+ strncpy (fullname + off, file->d_name, size);
+ fullname[off + size] = 0;
- return res;
-}
+ if (BIO_read_filename (certh, fullname) == -1)
+ {
+ BIO_free (certh);
+ log_error ("x509_read_from_dir: BIO_read_filename () failed");
+ continue;
+ }
-/* Retrieve the public key from a X509 certificate. */
-int
-x509_cert_get_key (u_int8_t *asn, u_int32_t asnlen, void *blob)
-{
- struct rsa_public_key *key = blob;
- struct x509_certificate cert;
+ cert = PEM_read_bio_X509 (certh, NULL, NULL);
+ BIO_free (certh);
+ if (cert == NULL)
+ {
+ log_error ("x509_read_from_dir: PEM_read_bio_X509 (%s) failed",
+ file->d_name);
+ continue;
+ }
- if (!x509_decode_certificate (asn, asnlen, &cert))
- return 0;
+ if (!X509_STORE_add_cert (ctx, cert))
+ {
+ /*
+ * This is actually expected if we have several certificates only
+ * differing in subjectAltName, which is not an something that is
+ * strange. Consider multi-homed machines.
+ */
+ log_debug (LOG_CRYPTO, 50,
+ "x509_read_from_dir: X509_STORE_add_cert (%s) failed",
+ file->d_name);
+ }
- /* XXX Perhaps put into pkcs? */
- mpz_init_set (key->n, cert.key.n);
- mpz_init_set (key->e, cert.key.e);
+ if (hash && !x509_hash_enter (cert))
+ log_print ("x509_read_from_dir: X509_hash_enter (%s) failed",
+ file->d_name);
+ }
- x509_free_certificate (&cert);
+ closedir (dir);
return 1;
}
-/* Retrieve the public key from a X509 certificate. */
+/* Initialize our databases and load our own certificates. */
int
-x509_cert_get_subject (u_int8_t *asn, u_int32_t asnlen,
- u_int8_t **subject, u_int32_t *subjectlen)
+x509_cert_init (void)
{
- struct x509_certificate cert;
+ char *dirname;
- if (!x509_decode_certificate (asn, asnlen, &cert))
- return 0;
+ x509_hash_init ();
- if (!cert.extension.type || !cert.extension.val)
- goto fail;
+ /* Process client certificates we will accept. */
+ dirname = conf_get_str ("X509-certificates", "Cert-directory");
+ if (!dirname)
+ {
+ log_print ("x509_cert_init: no Cert-directory");
+ return 0;
+ }
- log_debug (LOG_CRYPTO, 60, "x509_cert_get_subject: Extension Type %s = %s",
- cert.extension.type,
- asn_parse_objectid (asn_ids, cert.extension.type));
+ x509_certs = X509_STORE_new ();
+ if (!x509_certs)
+ {
+ log_print ("x509_cert_init: creating new X509_STORE failed");
+ return 0;
+ }
- if (strcmp (ASN_ID_SUBJECT_ALT_NAME, cert.extension.type))
+ if (!x509_read_from_dir (x509_certs, dirname, 1))
{
- log_print ("x509_cert_get_subject: extension type != subjectAltName");
- goto fail;
+ log_print ("x509_cert_init: x509_read_from_dir failed");
+ return 0;
}
- /*
- * XXX Evil**3, due to lack of time the IP encoding of subjectAltName
- * is supposed to be: 0x30 0x06 0x087 0x04 aa bb cc dd, where the IPV4
- * IP number is aa.bb.cc.dd.
- */
- if (asn_get_len (cert.extension.val) != 8 || cert.extension.val[3] != 4)
+ /* Process CA certificates we will trust. */
+ dirname = conf_get_str ("X509-certificates", "CA-directory");
+ if (!dirname)
{
- log_print ("x509_cert_get_subject: "
- "subjectAltName uses unhandled encoding");
- goto fail;
+ log_print ("x509_cert_init: no CA-directory");
+ return 0;
}
- /* XXX IPV4 address. */
- *subject = malloc (sizeof (in_addr_t));
- if (!*subject)
+ x509_cas = X509_STORE_new ();
+ if (!x509_cas)
{
- log_error ("x509_cert_get_subject: malloc (%d) failed",
- sizeof (in_addr_t));
- goto fail;
+ log_print ("x509_cert_init: creating new X509_STORE failed");
+ return 0;
+ }
+
+ if (!x509_read_from_dir (x509_cas, dirname, 0))
+ {
+ log_print ("x509_cert_init: x509_read_from_dir failed");
+ return 0;
}
- *subjectlen = sizeof (in_addr_t);
- memcpy (*subject, cert.extension.val + 4, *subjectlen);
- x509_free_certificate (&cert);
return 1;
+}
- fail:
- x509_free_certificate (&cert);
- return 0;
+void *
+x509_cert_get (u_int8_t *asn, u_int32_t len)
+{
+ return x509_from_asn (asn, len);
}
-/*
- * Initalizes the struct x509_attribval from a AttributeValueAssertion.
- * XXX Error checking.
- */
-void
-x509_get_attribval (struct norm_type *obj, struct x509_attribval *a)
+int
+x509_cert_validate (void *scert)
{
- struct norm_type *tmp;
+ X509_STORE_CTX csc;
+ X509_NAME *issuer, *subject;
+ X509 *cert = (X509 *)scert;
+ EVP_PKEY *key;
+ int res;
- tmp = asn_decompose ("AttributeValueAssertion.AttributeType", obj);
- if (tmp && tmp->data)
- a->type = strdup ((char *)tmp->data);
+ /*
+ * Validate the peer certificate by checking with the CA certificates we
+ * trust.
+ */
+ X509_STORE_CTX_init (&csc, x509_cas, cert, NULL);
+ res = X509_verify_cert (&csc);
+ X509_STORE_CTX_cleanup (&csc);
- tmp = asn_decompose ("AttributeValueAssertion.AttributeValue", obj);
- if (tmp && tmp->data)
- a->val = strdup ((char *)tmp->data);
-}
+ /* Return if validation succeeded or self-signed certs are not accepted. */
+ if (res || !conf_get_str ("X509-certificates", "Accept-self-signed"))
+ return res;
-/* Set OBJ with values from A. XXX Error checking. */
-void
-x509_set_attribval (struct norm_type *obj, struct x509_attribval *a)
-{
- struct norm_type *tmp;
+ issuer = X509_get_issuer_name (cert);
+ subject = X509_get_subject_name (cert);
+
+ if (!issuer || !subject || X509_name_cmp (issuer, subject))
+ return 0;
+
+ key = X509_get_pubkey (cert);
+ if (!key)
+ return 0;
- tmp = asn_decompose ("AttributeValueAssertion.AttributeType", obj);
- tmp->data = strdup (a->type);
- tmp->len = strlen (tmp->data);
- tmp = asn_decompose ("AttributeValueAssertion.AttributeValue", obj);
- tmp->type = TAG_PRINTSTRING;
- tmp->data = strdup (a->val);
- tmp->len = strlen (tmp->data);
+ if (X509_verify (cert, key) == -1)
+ return 0;
+
+ return 1;
}
-void
-x509_free_attribval (struct x509_attribval *a)
+int
+x509_cert_insert (void *scert)
{
- if (a->type)
- free (a->type);
- if (a->val)
- free (a->val);
+ X509 *cert = X509_dup ((X509 *)scert);
+ int res;
+
+ if (!cert)
+ {
+ log_print ("x509_cert_insert: X509_dup failed");
+ return 0;
+ }
+
+ res = x509_hash_enter (cert);
+ if (!res)
+ X509_free (cert);
+
+ return res;
}
void
-x509_free_certificate (struct x509_certificate *cert)
+x509_cert_free (void *cert)
{
- pkcs_free_public_key (&cert->key);
- if (cert->signaturetype)
- free (cert->signaturetype);
- if (cert->start)
- free (cert->start);
- if (cert->end)
- free (cert->end);
-
- x509_free_attribval (&cert->issuer1);
- x509_free_attribval (&cert->issuer2);
- x509_free_attribval (&cert->subject1);
- x509_free_attribval (&cert->subject2);
- x509_free_attribval (&cert->extension);
+ X509_free ((X509 *)cert);
}
+/* Validate the BER Encoding of a RDNSequence in the CERT_REQ payload. */
int
-x509_decode_certificate (u_int8_t *asn, u_int32_t asnlen,
- struct x509_certificate *rcert)
+x509_certreq_validate (u_int8_t *asn, u_int32_t len)
{
- struct norm_type cert = SEQ ("cert", Certificate);
- struct norm_type *tmp;
- u_int8_t *data;
- u_int32_t datalen;
+ int res = 1;
+ /* struct norm_type name = SEQOF ("issuer", RDNSequence);
- /*
- * Get access to the inner Certificate.
- * XXX We don't know how to get at the CA's public key yet.
- */
- if (!x509_validate_signed (asn, asnlen, 0, &data, &datalen))
- return 0;
-
- memset (rcert, 0, sizeof *rcert);
+ if (!asn_template_clone (&name, 1)
+ || (asn = asn_decode_sequence (asn, len, &name)) == 0)
+ {
+ log_print ("x509_certreq_validate: can not decode 'acceptable CA' info");
+ res = 0;
+ }
+ asn_free (&name); */
- if (!asn_template_clone (&cert, 1)
- || !asn_decode_sequence (data, datalen, &cert))
- goto fail;
+ /* XXX - not supported directly in SSL - later */
- tmp = asn_decompose ("cert.subjectPublicKeyInfo.subjectPublicKey", &cert);
- if (!tmp || !tmp->data)
- goto fail;
- if (!pkcs_public_key_from_asn (&rcert->key, tmp->data + 1, tmp->len - 1))
- goto fail;
-
- tmp = asn_decompose ("cert.version", &cert);
- if (!tmp || !tmp->data)
- goto fail;
- rcert->version = mpz_get_ui (tmp->data);
+ return res;
+}
- tmp = asn_decompose ("cert.serialNumber", &cert);
- if (!tmp || !tmp->data)
- goto fail;
- rcert->serialnumber = mpz_get_ui (tmp->data);
+/* Decode the BER Encoding of a RDNSequence in the CERT_REQ payload. */
+void *
+x509_certreq_decode (u_int8_t *asn, u_int32_t len)
+{
+ /* XXX This needs to be done later.
+ struct norm_type aca = SEQOF ("aca", RDNSequence);
+ struct norm_type *tmp;
+ struct x509_aca naca, *ret;
- tmp = asn_decompose ("cert.signature.algorithm", &cert);
- if (!tmp || !tmp->data)
- goto fail;
- rcert->signaturetype = strdup ((char *)tmp->data);
+ if (!asn_template_clone (&aca, 1)
+ || (asn = asn_decode_sequence (asn, len, &aca)) == 0)
+ {
+ log_print ("x509_certreq_validate: can not decode 'acceptable CA' info");
+ goto fail;
+ }
+ memset (&naca, 0, sizeof (naca));
- tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName."
- "AttributeValueAssertion", &cert);
+ tmp = asn_decompose ("aca.RelativeDistinguishedName.AttributeValueAssertion",
+ &aca);
if (!tmp)
goto fail;
- x509_get_attribval (tmp, &rcert->issuer1);
+ x509_get_attribval (tmp, &naca.name1);
- tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName[1]."
- "AttributeValueAssertion", &cert);
+ tmp = asn_decompose ("aca.RelativeDistinguishedName[1]"
+ ".AttributeValueAssertion", &aca);
if (tmp)
- x509_get_attribval (tmp, &rcert->issuer2);
- else
- rcert->issuer2.type = 0;
-
- tmp = asn_decompose ("cert.subject.RelativeDistinguishedName."
- "AttributeValueAssertion", &cert);
- if (!tmp)
- goto fail;
- x509_get_attribval (tmp, &rcert->subject1);
+ x509_get_attribval (tmp, &naca.name2);
+
+ asn_free (&aca);
- tmp = asn_decompose ("cert.subject.RelativeDistinguishedName[1]."
- "AttributeValueAssertion", &cert);
- if (tmp)
- x509_get_attribval (tmp, &rcert->subject2);
+ ret = malloc (sizeof (struct x509_aca));
+ if (ret)
+ memcpy (ret, &naca, sizeof (struct x509_aca));
else
- rcert->subject2.type = 0;
-
- tmp = asn_decompose ("cert.validity.notBefore", &cert);
- if (!tmp || !tmp->data)
- goto fail;
- rcert->start = strdup ((char *)tmp->data);
- if (!rcert->start)
{
- log_error ("x509_decode_certificate: strdup(\"%s\") failed", tmp->data);
- goto fail;
- }
-
- tmp = asn_decompose ("cert.validity.notAfter", &cert);
- if (!tmp || !tmp->data)
- goto fail;
- rcert->end = strdup ((char *)tmp->data);
- if (!rcert->end)
- {
- log_error ("x509_decode_certificate: strdup(\"%s\") failed", tmp->data);
- goto fail;
+ log_error ("x509_certreq_decode: malloc (%d) failed",
+ sizeof (struct x509_aca));
+ x509_free_aca (&aca);
}
- /* For x509v3 there might be an extension, try to decode it. */
- tmp = asn_decompose ("cert.extension", &cert);
- if (tmp && tmp->data && rcert->version == 2)
- x509_decode_cert_extension (tmp->data, tmp->len, rcert);
-
- asn_free (&cert);
- return 1;
+ return ret;
fail:
- x509_free_certificate (rcert);
- asn_free (&cert);
+ asn_free (&aca); */
return 0;
}
-int
-x509_encode_certificate (struct x509_certificate *rcert,
- u_int8_t **asn, u_int32_t *asnlen)
+void
+x509_free_aca (void *blob)
{
- struct norm_type cert = SEQ ("cert", Certificate);
- struct norm_type *tmp;
- u_int8_t *data, *new_buf;
- mpz_t num;
- u_int8_t *tmpasn;
- u_int32_t tmpasnlen;
-
-
- if (!asn_template_clone (&cert, 1))
- goto fail;
+ struct x509_aca *aca = blob;
- if (rcert->extension.type && rcert->extension.val)
- {
- tmp = asn_decompose ("cert.extension", &cert);
- if (x509_encode_cert_extension (rcert, &tmpasn, &tmpasnlen))
- {
- tmp->data = tmpasn;
- tmp->len = tmpasnlen;
- }
- }
+ if (aca->name1.type)
+ free (aca->name1.type);
+ if (aca->name1.val)
+ free (aca->name1.val);
- tmp = asn_decompose ("cert.subjectPublicKeyInfo.algorithm.parameters",
- &cert);
- tmp->type = TAG_NULL;
- tmp = asn_decompose ("cert.subjectPublicKeyInfo.algorithm.algorithm",
- &cert);
- tmp->data = strdup (ASN_ID_RSAENCRYPTION);
- tmp->len = strlen (tmp->data);
+ if (aca->name2.type)
+ free (aca->name2.type);
+ if (aca->name2.val)
+ free (aca->name2.val);
+}
- tmp = asn_decompose ("cert.subjectPublicKeyInfo.subjectPublicKey", &cert);
- data = pkcs_public_key_to_asn (&rcert->key);
- if (!data)
- goto fail;
+X509 *
+x509_from_asn (u_char *asn, u_int len)
+{
+ BIO *certh;
+ X509 *scert = NULL;
- /* This is a BITSTRING, add 0 octet for padding. */
- tmp->len = asn_get_len (data);
- new_buf = realloc (data, tmp->len + 1);
- if (!new_buf)
+ certh = BIO_new (BIO_s_mem ());
+ if (!certh)
{
- log_error ("x509_encode_certificate: realloc (%p, %d) failed", data,
- tmp->len + 1);
- free (data);
- goto fail;
+ log_error ("X509_from_asn: BIO_new (BIO_s_mem ()) failed");
+ return NULL;
}
- data = new_buf;
- memmove (data + 1, data, tmp->len);
- data[0] = 0;
- tmp->data = data;
- tmp->len++;
-
- mpz_init (num);
- tmp = asn_decompose ("cert.version", &cert);
- mpz_set_ui (num, rcert->version);
- if (!pkcs_mpz_to_norm_type (tmp, num))
+
+ if (BIO_write (certh, asn, len) == -1)
{
- mpz_clear (num);
- goto fail;
+ log_error ("X509_from_asn: BIO_write failed\n");
+ goto end;
}
- tmp = asn_decompose ("cert.serialNumber", &cert);
- mpz_set_ui (num, rcert->serialnumber);
- if (!pkcs_mpz_to_norm_type (tmp, num))
+ scert = d2i_X509_bio (certh, NULL);
+ if (!scert)
{
- mpz_clear (num);
- goto fail;
+ log_print ("X509_from_asn: d2i_X509_bio failed\n");
+ goto end;
}
- mpz_clear (num);
-
- tmp = asn_decompose ("cert.signature.parameters", &cert);
- tmp->type = TAG_NULL;
- tmp = asn_decompose ("cert.signature.algorithm", &cert);
- tmp->data = strdup (rcert->signaturetype);
- tmp->len = strlen ((char *)tmp->data);
-
- tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName."
- "AttributeValueAssertion", &cert);
- x509_set_attribval (tmp, &rcert->issuer1);
- tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName[1]."
- "AttributeValueAssertion", &cert);
- x509_set_attribval (tmp, &rcert->issuer2);
-
- tmp = asn_decompose ("cert.subject.RelativeDistinguishedName."
- "AttributeValueAssertion", &cert);
- x509_set_attribval (tmp, &rcert->subject1);
- tmp = asn_decompose ("cert.subject.RelativeDistinguishedName[1]."
- "AttributeValueAssertion", &cert);
- x509_set_attribval (tmp, &rcert->subject2);
-
- tmp = asn_decompose ("cert.validity.notBefore", &cert);
- tmp->data = strdup (rcert->start);
- tmp->len = strlen ((char *)tmp->data);
-
- tmp = asn_decompose ("cert.validity.notAfter", &cert);
- tmp->data = strdup (rcert->end);
- tmp->len = strlen ((char *)tmp->data);
-
- *asn = asn_encode_sequence (&cert, 0);
- if (!*asn)
- goto fail;
-
- *asnlen = asn_get_len (*asn);
- asn_free (&cert);
- return 1;
-
- fail:
- asn_free (&cert);
- return 0;
+ end:
+ BIO_free (certh);
+ return scert;
}
/*
- * Decode an Extension to a X509 certificate.
- * XXX We ignore the critical boolean.
+ * Check that a certificate has a subjectAltName and that it matches our ID.
*/
-
int
-x509_decode_cert_extension (u_int8_t *asn, u_int32_t asnlen,
- struct x509_certificate *cert)
+x509_check_subjectaltname (u_char *id, u_int id_len, X509 *scert)
{
- struct norm_type *tmp;
- struct norm_type ex = SEQOF ("ex", Extensions);
+ u_int8_t *altname;
+ u_int32_t altlen;
+ int type, idtype, ret;
- /* Implicit tagging for extension. */
- ex.class = ADD_EXP (3, UNIVERSAL);
-
- if (!asn_template_clone (&ex, 1) || !asn_decode_sequence (asn, asnlen, &ex))
+ type = x509_cert_subjectaltname (scert, &altname, &altlen);
+ if (!type)
{
- asn_free (&ex);
+ log_print ("x509_check_subjectaltname: can't access subjectAltName");
return 0;
}
- tmp = asn_decompose ("ex.extension.extnValue", &ex);
- if (!tmp || !tmp->data || asn_get_len (tmp->data) != tmp->len)
- goto fail;
- cert->extension.val = malloc (tmp->len);
- if (cert->extension.val == 0)
- goto fail;
- memcpy (cert->extension.val, tmp->data, tmp->len);
+ /*
+ * Now that we have the X509 certicate in native form, get the
+ * subjectAltName extension and verify that it matches our ID.
+ */
- tmp = asn_decompose ("ex.extension.extnId", &ex);
- if (!tmp || !tmp->data)
- goto fail;
- cert->extension.type = strdup (tmp->data);
- if (!cert->extension.type)
+ /* XXX Get type of ID. */
+ idtype = id[0];
+ id += ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;
+ id_len -= ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;
+
+ ret = 0;
+ switch (idtype)
{
- free (cert->extension.val);
- cert->extension.val = 0;
- goto fail;
+ case IPSEC_ID_IPV4_ADDR:
+ if (type == X509v3_IPV4_ADDR)
+ ret = 1;
+ break;
+ case IPSEC_ID_FQDN:
+ if (type == X509v3_DNS_NAME)
+ ret = 1;
+ break;
+ case IPSEC_ID_USER_FQDN:
+ if (type == X509v3_RFC_NAME)
+ ret = 1;
+ break;
+ default:
+ ret = 0;
+ break;
}
- asn_free (&ex);
- return 1;
+ if (!ret)
+ {
+ log_debug (LOG_CRYPTO, 50,
+ "X509_check_subjectaltname: "
+ "our ID type does not match X509 cert ID type");
+ return 0;
+ }
- fail:
- asn_free (&ex);
- return 0;
+ if (altlen != id_len || memcmp (altname, id, id_len) != 0)
+ {
+ log_debug (LOG_CRYPTO, 50,
+ "X509_check_subjectaltname: "
+ "our ID does not match X509 cert ID");
+ return 0;
+ }
+
+ return 1;
}
-/*
- * Encode a Cert Extension.
- * XXX Only one extension per certificate.
- * XXX We tag everything as critical.
+/*
+ * Obtain a certificate from an acceptable CA.
+ * XXX We don't check if the certificate we find is from an accepted CA.
*/
int
-x509_encode_cert_extension (struct x509_certificate *cert,
- u_int8_t **asn, u_int32_t *asnlen)
+x509_cert_obtain (u_int8_t *id, size_t id_len, void *data, u_int8_t **cert,
+ u_int32_t *certlen)
{
- struct norm_type ex = SEQ ("ex", Extensions);
- struct norm_type *tmp;
- ex.class = ADD_EXP (3, UNIVERSAL);
-
- if (!asn_template_clone (&ex ,1))
- goto fail;
+ struct x509_aca *aca = data;
+ X509 *scert;
+ u_char *p;
- tmp = asn_decompose ("ex.extension.extnId", &ex);
- tmp->data = strdup (cert->extension.type);
- tmp->len = strlen (tmp->data);
+ if (aca)
+ log_debug (LOG_CRYPTO, 60,
+ "x509_cert_obtain: acceptable certificate authorities here");
- /* XXX We mark every extension as critical. */
- tmp = asn_decompose ("ex.extension.critical", &ex);
- tmp->data = malloc (1);
- if (!tmp->data)
+ /* We need our ID to find a certificate. */
+ if (!id)
{
- log_error ("x509_encode_cert_extension: malloc (1) failed");
- goto fail;
+ log_print ("X509_cert_obtain: ID is missing");
+ return 0;
}
- *(u_int8_t *)tmp->data = 0xff;
- tmp->len = 1;
- tmp = asn_decompose ("ex.extension.extnValue", &ex);
- tmp->data = malloc (asn_get_len (cert->extension.val));
- if (!tmp->data)
+ scert = x509_hash_find (id, id_len);
+ if (!scert)
+ return 0;
+
+ if (!x509_check_subjectaltname (id, id_len, scert))
{
- log_error ("x509_encode_cert_extension: malloc (%d) failed",
- asn_get_len (cert->extension.val));
- goto fail;
+ log_print ("X509_cert_obtain: subjectAltName does not match id");
+ free (*cert);
+ return 0;
}
- tmp->len = asn_get_len (cert->extension.val);
- memcpy (tmp->data, cert->extension.val, tmp->len);
- *asn = asn_encode_sequence (&ex, 0);
- if (!*asn)
- goto fail;
+ *certlen = i2d_X509 (scert, NULL);
+ p = *cert = malloc (*certlen);
+ if (!p)
+ {
+ log_error ("X509_cert_obtain: malloc (%d) failed", *certlen);
+ return 0;
+ }
+ *certlen = i2d_X509 (scert, &p);
- *asnlen = asn_get_len (*asn);
-
- asn_free (&ex);
return 1;
- fail:
- asn_free (&ex);
- return 0;
}
-/*
- * Checks the signature on an ASN.1 Signed Type. If the passed KEY is
- * NULL we just unwrap the inner object and return it.
- */
+/* Returns a pointer to the subjectAltName information of X509 certificate. */
int
-x509_validate_signed (u_int8_t *asn, u_int32_t asnlen,
- struct rsa_public_key *key, u_int8_t **data,
- u_int32_t *datalen)
+x509_cert_subjectaltname (X509 *scert, u_int8_t **altname, u_int32_t *len)
{
- struct norm_type sig = SEQ ("signed", Signed);
- struct norm_type digest = SEQ ("digest", DigestInfo);
- struct norm_type *tmp;
- struct hash *hash = 0;
- int res;
- u_int8_t *dec;
- u_int16_t declen;
- char *id;
+ X509_EXTENSION *subjectaltname;
+ u_int8_t *sandata;
+ int extpos;
+ int santype, sanlen;
- if (!asn_template_clone (&sig, 1))
- /* Failed, probably memory allocation, free what we got anyway. */
- goto fail;
-
- if (!asn_decode_sequence (asn, asnlen, &sig))
+ extpos = X509_get_ext_by_NID (scert, NID_subject_alt_name, -1);
+ if (extpos == -1)
{
- log_print ("x509_validate_signed: input data could not be decoded");
- goto fail;
+ log_print ("X509_cert_subjectaltname: "
+ "certificate does not contain subjectAltName");
+ return 0;
}
- tmp = asn_decompose ("signed.algorithm.algorithm", &sig);
+ subjectaltname = X509_get_ext (scert, extpos);
- if (strcmp ((char *)tmp->data, ASN_ID_MD5WITHRSAENC) == 0)
- hash = hash_get (HASH_MD5);
- else
+ if (!subjectaltname || !subjectaltname->value
+ || !subjectaltname->value->data || subjectaltname->value->length < 4)
{
- id = asn_parse_objectid (asn_ids, tmp->data);
- log_print ("x509_validate_signed: can not handle SigType %s",
- id ? id : tmp->data);
- goto fail;
+ log_print ("X509_check_subjectaltname: "
+ "invalid subjectaltname extension");
+ return 0;
}
- if (!hash)
- goto fail;
-
- tmp = asn_decompose ("signed.data", &sig);
-
- /* Hash the data. */
- hash->Init (hash->ctx);
- hash->Update (hash->ctx, tmp->data, tmp->len);
- hash->Final (hash->digest, hash->ctx);
-
- *data = tmp->data;
- *datalen = tmp->len;
+ /* SSL does not handle unknown ASN stuff well, do it by hand. */
+ sandata = subjectaltname->value->data;
+ santype = sandata[2] & 0x3f;
+ sanlen = sandata[3];
+ sandata += 4;
- /* Used to unwrap the SIGNED object around the Certificate. */
- if (!key)
+ if (sanlen + 4 != subjectaltname->value->length)
{
- asn_free (&sig);
- return 1;
+ log_print ("X509_check_subjectaltname: subjectaltname invalid length");
+ return 0;
}
+
+ *len = sanlen;
+ *altname = sandata;
- tmp = asn_decompose ("signed.encrypted", &sig);
+ return santype;
+}
- /*
- * tmp->data is a BITSTRING, the first octet in the BITSTRING gives
- * the padding bits at the end. Per definition there are no padding
- * bits at the end in this case, so just skip it.
- */
- if (!pkcs_rsa_decrypt (PKCS_PRIVATE, key, 0, tmp->data + 1, &dec, &declen))
- goto fail;
+int
+x509_cert_get_subject (void *scert, u_int8_t **id, u_int32_t *id_len)
+{
+ X509 *cert = scert;
+ int type;
+ u_int8_t *altname;
+ u_int32_t altlen;
- if (!asn_template_clone (&digest, 1)
- || !asn_decode_sequence (dec, declen, &digest))
- {
- asn_free (&digest);
- goto fail;
- }
- tmp = asn_decompose ("digest.digestAlgorithm.algorithm", &digest);
- if (strcmp (ASN_ID_MD5, (char *)tmp->data))
- {
- log_print ("x509_validate_signed: DigestAlgorithm is not MD5");
- res = 0;
- }
- else
+ type = x509_cert_subjectaltname (cert, &altname, &altlen);
+
+ switch (type)
{
- tmp = asn_decompose ("digest.digest", &digest);
- if (tmp->len != hash->hashsize
- || memcmp (tmp->data, hash->digest, tmp->len))
- {
- log_print ("x509_validate_signed: Digest does not match Data");
- res = 0;
- }
- else
- res = 1;
+ case X509v3_IPV4_ADDR:
+ {
+ char buf[ISAKMP_ID_DATA_OFF + 4];
+
+ /* XXX sizeof IPV4_ADDR, how any better? */
+ if (altlen != 4)
+ {
+ log_print ("x509_cert_get_subject: length != IP4addr: %d",
+ altlen);
+ return 0;
+ }
+
+ SET_ISAKMP_ID_TYPE (buf, IPSEC_ID_IPV4_ADDR);
+ SET_IPSEC_ID_PROTO (buf + ISAKMP_ID_DOI_DATA_OFF, 0);
+ SET_IPSEC_ID_PORT (buf + ISAKMP_ID_DOI_DATA_OFF, 0);
+ memcpy (buf + ISAKMP_ID_DATA_OFF, altname, altlen);
+
+ *id_len = ISAKMP_ID_DATA_OFF + 4 - ISAKMP_GEN_SZ;
+ *id = malloc (*id_len);
+ if (!*id)
+ {
+ log_print ("x509_cert_get_subject: malloc (%d) failed", *id_len);
+ return 0;
+ }
+ memcpy (*id, buf + ISAKMP_GEN_SZ, *id_len);
+ }
+ break;
+ default:
+ log_print ("x509_cert_get_subject: unsupported subjectAltName type: %d",
+ type);
+ return 0;
}
- asn_free (&digest);
- asn_free (&sig);
- return res;
-
- fail:
- asn_free (&sig);
- return 0;
+ return 1;
}
-/*
- * Create an ASN Signed Structure from the data passed in data
- * and return the result in asn.
- * At the moment the used hash is MD5, this is the only common
- * hash between us and X509.
- */
int
-x509_create_signed (u_int8_t *data, u_int32_t datalen,
- struct rsa_private_key *key, u_int8_t **asn,
- u_int32_t *asnlen)
+x509_cert_get_key (void *scert, void *keyp)
{
- struct norm_type digest = SEQ ("digest", DigestInfo);
- struct norm_type sig = SEQ ("signed", Signed);
- struct norm_type *tmp;
- struct hash *hash;
- u_int8_t *diginfo, *enc;
- u_int32_t enclen;
- int res = 0;
-
- /* Hash the Data. */
- hash = hash_get (HASH_MD5);
- hash->Init (hash->ctx);
- hash->Update (hash->ctx, data, datalen);
- hash->Final (hash->digest, hash->ctx);
+ X509 *cert = scert;
+ EVP_PKEY *key;
- if (!asn_template_clone (&digest, 1))
- goto fail;
+ key = X509_get_pubkey (cert);
- tmp = asn_decompose ("digest.digest", &digest);
- tmp->len = hash->hashsize;
- tmp->data = malloc (hash->hashsize);
- if (!tmp->data)
+ /* Check if we got the right key type */
+ if (key->type != EVP_PKEY_RSA)
{
- log_error ("x509_create_signed: malloc (%d) failed", hash->hashsize);
- goto fail;
+ log_print ("x509_cert_get_key: public key is not a RSA key");
+ X509_free (cert);
+ return 0;
}
- memcpy (tmp->data, hash->digest, hash->hashsize);
- tmp = asn_decompose ("digest.digestAlgorithm.parameters", &digest);
- tmp->type = TAG_NULL;
- tmp = asn_decompose ("digest.digestAlgorithm.algorithm", &digest);
- tmp->data = strdup (ASN_ID_MD5);
- tmp->len = strlen (tmp->data);
+ *(RSA **)keyp = RSAPublicKey_dup (key->pkey.rsa);
- /* ASN encode Digest Information. */
- diginfo = asn_encode_sequence (&digest, 0);
- if (!diginfo)
- goto fail;
-
- /* Encrypt the Digest Info with Private Key. */
- res = pkcs_rsa_encrypt (PKCS_PRIVATE, 0, key, diginfo, asn_get_len (diginfo),
- &enc, &enclen);
- free (diginfo);
- if (!res)
- goto fail;
- res = 0;
-
- if (!asn_template_clone (&sig, 1))
- goto fail2;
-
- tmp = asn_decompose ("signed.algorithm.parameters", &sig);
- tmp->type = TAG_NULL;
- tmp = asn_decompose ("signed.algorithm.algorithm", &sig);
- tmp->data = strdup (ASN_ID_MD5WITHRSAENC);
- tmp->len = strlen (tmp->data);
-
- /* The type is BITSTRING, i.e. first octet need to be zero. */
- tmp = asn_decompose ("signed.encrypted", &sig);
- tmp->data = malloc (enclen + 1);
- if (!tmp->data)
- {
- log_error ("x509_create_signed: malloc (%d) failed", enclen + 1);
- free (enc);
- goto fail2;
- }
- tmp->len = enclen + 1;
- memcpy (tmp->data + 1, enc, enclen);
- *(char *)tmp->data = 0;
- free (enc);
-
- tmp = asn_decompose ("signed.data", &sig);
- tmp->data = data;
- tmp->len = datalen;
-
- *asn = asn_encode_sequence (&sig, 0);
- if (!*asn)
- goto fail2;
- *asnlen = asn_get_len (*asn);
-
- /* This is the data we have been given, we can not free it in asn_free. */
- tmp->data = 0;
- res = 1; /* Successful. */
-
- fail2:
- asn_free (&sig);
- fail:
- asn_free (&digest);
- return res;
+ return *(RSA **)keyp == NULL ? 0 : 1;
}