diff options
Diffstat (limited to 'sbin/isakmpd/x509.c')
-rw-r--r-- | sbin/isakmpd/x509.c | 1155 |
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; } |