summaryrefslogtreecommitdiff
path: root/keyexchange/isakmpd-20041012/x509.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyexchange/isakmpd-20041012/x509.c')
-rw-r--r--keyexchange/isakmpd-20041012/x509.c1439
1 files changed, 0 insertions, 1439 deletions
diff --git a/keyexchange/isakmpd-20041012/x509.c b/keyexchange/isakmpd-20041012/x509.c
deleted file mode 100644
index 0897557..0000000
--- a/keyexchange/isakmpd-20041012/x509.c
+++ /dev/null
@@ -1,1439 +0,0 @@
-/* $OpenBSD: x509.c,v 1.95 2004/08/10 19:21:01 deraadt Exp $ */
-/* $EOM: x509.c,v 1.54 2001/01/16 18:42:16 ho Exp $ */
-
-/*
- * Copyright (c) 1998, 1999 Niels Provos. All rights reserved.
- * Copyright (c) 1999, 2000, 2001 Niklas Hallqvist. All rights reserved.
- * Copyright (c) 1999, 2000, 2001 Angelos D. Keromytis. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
- */
-
-/*
- * This code was written under funding by Ericsson Radio Systems.
- */
-
-#ifdef USE_X509
-
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#ifdef USE_POLICY
-#include <regex.h>
-#include <keynote.h>
-#endif /* USE_POLICY */
-
-#include "sysdep.h"
-
-#include "cert.h"
-#include "conf.h"
-#include "exchange.h"
-#include "hash.h"
-#include "ike_auth.h"
-#include "ipsec.h"
-#include "log.h"
-#include "math_mp.h"
-#include "monitor.h"
-#include "policy.h"
-#include "sa.h"
-#include "util.h"
-#include "x509.h"
-
-static u_int16_t x509_hash(u_int8_t *, size_t);
-static void x509_hash_init(void);
-static X509 *x509_hash_find(u_int8_t *, size_t);
-static int x509_hash_enter(X509 *);
-
-/*
- * X509_STOREs do not support subjectAltNames, so we have to build
- * our own hash table.
- */
-
-/*
- * 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 = 0;
-static X509_STORE *x509_cas = 0;
-
-/* Initial number of bits used as hash. */
-#define INITIAL_BUCKET_BITS 6
-
-struct x509_hash {
- LIST_ENTRY(x509_hash) link;
-
- X509 *cert;
-};
-
-static LIST_HEAD(x509_list, x509_hash) *x509_tab = 0;
-
-/* Works both as a maximum index and a mask. */
-static int bucket_mask;
-
-#ifdef USE_POLICY
-/*
- * Given an X509 certificate, create a KeyNote assertion where
- * Issuer/Subject -> Authorizer/Licensees.
- * XXX RSA-specific.
- */
-int
-x509_generate_kn(int id, X509 *cert)
-{
- char *fmt = "Authorizer: \"rsa-hex:%s\"\nLicensees: \"rsa-hex:%s"
- "\"\nConditions: %s >= \"%s\" && %s <= \"%s\";\n";
- char *ikey, *skey, *buf, isname[256], subname[256];
- char *fmt2 = "Authorizer: \"DN:%s\"\nLicensees: \"DN:%s\"\n"
- "Conditions: %s >= \"%s\" && %s <= \"%s\";\n";
- X509_NAME *issuer, *subject;
- struct keynote_deckey dc;
- X509_STORE_CTX csc;
- X509_OBJECT obj;
- X509 *icert;
- RSA *key;
- time_t tt;
- char before[15], after[15], *timecomp, *timecomp2;
- ASN1_TIME *tm;
- int i, buf_len;
-
- LOG_DBG((LOG_POLICY, 90,
- "x509_generate_kn: generating KeyNote policy for certificate %p",
- cert));
-
- issuer = X509_get_issuer_name(cert);
- subject = X509_get_subject_name(cert);
-
- /* Missing or self-signed, ignore cert but don't report failure. */
- if (!issuer || !subject || !X509_name_cmp(issuer, subject))
- return 1;
-
- if (!x509_cert_get_key(cert, &key)) {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: failed to get public key from cert"));
- return 0;
- }
- dc.dec_algorithm = KEYNOTE_ALGORITHM_RSA;
- dc.dec_key = key;
- ikey = kn_encode_key(&dc, INTERNAL_ENC_PKCS1, ENCODING_HEX,
- KEYNOTE_PUBLIC_KEY);
- if (keynote_errno == ERROR_MEMORY) {
- log_print("x509_generate_kn: failed to get memory for "
- "public key");
- RSA_free(key);
- LOG_DBG((LOG_POLICY, 30, "x509_generate_kn: cannot get "
- "subject key"));
- return 0;
- }
- if (!ikey) {
- RSA_free(key);
- LOG_DBG((LOG_POLICY, 30, "x509_generate_kn: cannot get "
- "subject key"));
- return 0;
- }
- RSA_free(key);
-
- /* Now find issuer's certificate so we can get the public key. */
- X509_STORE_CTX_init(&csc, x509_cas, cert, NULL);
- if (X509_STORE_get_by_subject(&csc, X509_LU_X509, issuer, &obj) !=
- X509_LU_X509) {
- X509_STORE_CTX_cleanup(&csc);
- X509_STORE_CTX_init(&csc, x509_certs, cert, NULL);
- if (X509_STORE_get_by_subject(&csc, X509_LU_X509, issuer, &obj)
- != X509_LU_X509) {
- X509_STORE_CTX_cleanup(&csc);
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: no certificate found for "
- "issuer"));
- return 0;
- }
- }
- X509_STORE_CTX_cleanup(&csc);
- icert = obj.data.x509;
-
- if (icert == NULL) {
- LOG_DBG((LOG_POLICY, 30, "x509_generate_kn: "
- "missing certificates, cannot construct X509 chain"));
- free(ikey);
- return 0;
- }
- if (!x509_cert_get_key(icert, &key)) {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: failed to get public key from cert"));
- free(ikey);
- return 0;
- }
- X509_OBJECT_free_contents(&obj);
-
- dc.dec_algorithm = KEYNOTE_ALGORITHM_RSA;
- dc.dec_key = key;
- skey = kn_encode_key(&dc, INTERNAL_ENC_PKCS1, ENCODING_HEX,
- KEYNOTE_PUBLIC_KEY);
- if (keynote_errno == ERROR_MEMORY) {
- log_error("x509_generate_kn: failed to get memory for public "
- "key");
- free(ikey);
- RSA_free(key);
- LOG_DBG((LOG_POLICY, 30, "x509_generate_kn: cannot get issuer "
- "key"));
- return 0;
- }
- if (!skey) {
- free(ikey);
- RSA_free(key);
- LOG_DBG((LOG_POLICY, 30, "x509_generate_kn: cannot get issuer "
- "key"));
- return 0;
- }
- RSA_free(key);
-
- buf_len = strlen(fmt) + strlen(ikey) + strlen(skey) + 56;
- buf = calloc(buf_len, sizeof(char));
- buf_len *= sizeof(char);
- if (!buf) {
- log_error("x509_generate_kn: "
- "failed to allocate memory for KeyNote credential");
- free(ikey);
- free(skey);
- return 0;
- }
- if (((tm = X509_get_notBefore(cert)) == NULL) ||
- (tm->type != V_ASN1_UTCTIME &&
- tm->type != V_ASN1_GENERALIZEDTIME)) {
- tt = time(0);
- strftime(before, 14, "%Y%m%d%H%M%S", localtime(&tt));
- timecomp = "LocalTimeOfDay";
- } else {
- if (tm->data[tm->length - 1] == 'Z') {
- timecomp = "GMTTimeOfDay";
- i = tm->length - 2;
- } else {
- timecomp = "LocalTimeOfDay";
- i = tm->length - 1;
- }
-
- for (; i >= 0; i--) {
- if (tm->data[i] < '0' || tm->data[i] > '9') {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: invalid data in "
- "NotValidBefore time field"));
- free(ikey);
- free(skey);
- free(buf);
- return 0;
- }
- }
-
- if (tm->type == V_ASN1_UTCTIME) {
- if ((tm->length < 10) || (tm->length > 13)) {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: invalid length "
- "of NotValidBefore time field (%d)",
- tm->length));
- free(ikey);
- free(skey);
- free(buf);
- return 0;
- }
- /* Validity checks. */
- if ((tm->data[2] != '0' && tm->data[2] != '1') ||
- (tm->data[2] == '0' && tm->data[3] == '0') ||
- (tm->data[2] == '1' && tm->data[3] > '2') ||
- (tm->data[4] > '3') ||
- (tm->data[4] == '0' && tm->data[5] == '0') ||
- (tm->data[4] == '3' && tm->data[5] > '1') ||
- (tm->data[6] > '2') ||
- (tm->data[6] == '2' && tm->data[7] > '3') ||
- (tm->data[8] > '5')) {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: invalid value in "
- "NotValidBefore time field"));
- free(ikey);
- free(skey);
- free(buf);
- return 0;
- }
- /* Stupid UTC tricks. */
- if (tm->data[0] < '5')
- snprintf(before, sizeof before, "20%s",
- tm->data);
- else
- snprintf(before, sizeof before, "19%s",
- tm->data);
- } else { /* V_ASN1_GENERICTIME */
- if ((tm->length < 12) || (tm->length > 15)) {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: invalid length of "
- "NotValidBefore time field (%d)",
- tm->length));
- free(ikey);
- free(skey);
- free(buf);
- return 0;
- }
- /* Validity checks. */
- if ((tm->data[4] != '0' && tm->data[4] != '1') ||
- (tm->data[4] == '0' && tm->data[5] == '0') ||
- (tm->data[4] == '1' && tm->data[5] > '2') ||
- (tm->data[6] > '3') ||
- (tm->data[6] == '0' && tm->data[7] == '0') ||
- (tm->data[6] == '3' && tm->data[7] > '1') ||
- (tm->data[8] > '2') ||
- (tm->data[8] == '2' && tm->data[9] > '3') ||
- (tm->data[10] > '5')) {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: invalid value in "
- "NotValidBefore time field"));
- free(ikey);
- free(skey);
- free(buf);
- return 0;
- }
- snprintf(before, sizeof before, "%s", tm->data);
- }
-
- /* Fix missing seconds. */
- if (tm->length < 12) {
- before[12] = '0';
- before[13] = '0';
- }
- /* This will overwrite trailing 'Z'. */
- before[14] = '\0';
- }
-
- tm = X509_get_notAfter(cert);
- if (tm == NULL &&
- (tm->type != V_ASN1_UTCTIME &&
- tm->type != V_ASN1_GENERALIZEDTIME)) {
- tt = time(0);
- strftime(after, 14, "%Y%m%d%H%M%S", localtime(&tt));
- timecomp2 = "LocalTimeOfDay";
- } else {
- if (tm->data[tm->length - 1] == 'Z') {
- timecomp2 = "GMTTimeOfDay";
- i = tm->length - 2;
- } else {
- timecomp2 = "LocalTimeOfDay";
- i = tm->length - 1;
- }
-
- for (; i >= 0; i--) {
- if (tm->data[i] < '0' || tm->data[i] > '9') {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: invalid data in "
- "NotValidAfter time field"));
- free(ikey);
- free(skey);
- free(buf);
- return 0;
- }
- }
-
- if (tm->type == V_ASN1_UTCTIME) {
- if ((tm->length < 10) || (tm->length > 13)) {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: invalid length of "
- "NotValidAfter time field (%d)",
- tm->length));
- free(ikey);
- free(skey);
- free(buf);
- return 0;
- }
- /* Validity checks. */
- if ((tm->data[2] != '0' && tm->data[2] != '1') ||
- (tm->data[2] == '0' && tm->data[3] == '0') ||
- (tm->data[2] == '1' && tm->data[3] > '2') ||
- (tm->data[4] > '3') ||
- (tm->data[4] == '0' && tm->data[5] == '0') ||
- (tm->data[4] == '3' && tm->data[5] > '1') ||
- (tm->data[6] > '2') ||
- (tm->data[6] == '2' && tm->data[7] > '3') ||
- (tm->data[8] > '5')) {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: invalid value in "
- "NotValidAfter time field"));
- free(ikey);
- free(skey);
- free(buf);
- return 0;
- }
- /* Stupid UTC tricks. */
- if (tm->data[0] < '5')
- snprintf(after, sizeof after, "20%s",
- tm->data);
- else
- snprintf(after, sizeof after, "19%s",
- tm->data);
- } else { /* V_ASN1_GENERICTIME */
- if ((tm->length < 12) || (tm->length > 15)) {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: invalid length of "
- "NotValidAfter time field (%d)",
- tm->length));
- free(ikey);
- free(skey);
- free(buf);
- return 0;
- }
- /* Validity checks. */
- if ((tm->data[4] != '0' && tm->data[4] != '1') ||
- (tm->data[4] == '0' && tm->data[5] == '0') ||
- (tm->data[4] == '1' && tm->data[5] > '2') ||
- (tm->data[6] > '3') ||
- (tm->data[6] == '0' && tm->data[7] == '0') ||
- (tm->data[6] == '3' && tm->data[7] > '1') ||
- (tm->data[8] > '2') ||
- (tm->data[8] == '2' && tm->data[9] > '3') ||
- (tm->data[10] > '5')) {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: invalid value in "
- "NotValidAfter time field"));
- free(ikey);
- free(skey);
- free(buf);
- return 0;
- }
- snprintf(after, sizeof after, "%s", tm->data);
- }
-
- /* Fix missing seconds. */
- if (tm->length < 12) {
- after[12] = '0';
- after[13] = '0';
- }
- after[14] = '\0'; /* This will overwrite trailing 'Z' */
- }
-
- snprintf(buf, buf_len, fmt, skey, ikey, timecomp, before, timecomp2,
- after);
- free(ikey);
- free(skey);
-
- if (kn_add_assertion(id, buf, strlen(buf), ASSERT_FLAG_LOCAL) == -1) {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: failed to add new KeyNote credential"));
- free(buf);
- return 0;
- }
- /* We could print the assertion here, but log_print() truncates... */
- LOG_DBG((LOG_POLICY, 60, "x509_generate_kn: added credential"));
-
- free(buf);
-
- if (!X509_NAME_oneline(issuer, isname, 256)) {
- LOG_DBG((LOG_POLICY, 50,
- "x509_generate_kn: "
- "X509_NAME_oneline (issuer, ...) failed"));
- return 0;
- }
- if (!X509_NAME_oneline(subject, subname, 256)) {
- LOG_DBG((LOG_POLICY, 50,
- "x509_generate_kn: "
- "X509_NAME_oneline (subject, ...) failed"));
- return 0;
- }
- buf_len = strlen(fmt2) + strlen(isname) + strlen(subname) + 56;
- buf = malloc(buf_len);
- if (!buf) {
- log_error("x509_generate_kn: malloc (%d) failed", buf_len);
- return 0;
- }
- snprintf(buf, buf_len, fmt2, isname, subname, timecomp, before,
- timecomp2, after);
-
- if (kn_add_assertion(id, buf, strlen(buf), ASSERT_FLAG_LOCAL) == -1) {
- LOG_DBG((LOG_POLICY, 30,
- "x509_generate_kn: failed to add new KeyNote credential"));
- free(buf);
- return 0;
- }
- LOG_DBG((LOG_POLICY, 80, "x509_generate_kn: added credential:\n%s",
- buf));
-
- free(buf);
- return 1;
-}
-#endif /* USE_POLICY */
-
-static u_int16_t
-x509_hash(u_int8_t *id, size_t len)
-{
- u_int16_t bucket = 0;
- size_t i;
-
- /* XXX We might resize if we are crossing a certain threshold. */
- for (i = 4; i < (len & ~1); i += 2) {
- /* Doing it this way avoids alignment problems. */
- bucket ^= (id[i] + 1) * (id[i + 1] + 257);
- }
- /* Hash in the last character of odd length IDs too. */
- if (i < len)
- bucket ^= (id[i] + 1) * (id[i] + 257);
-
- bucket &= bucket_mask;
- return bucket;
-}
-
-static void
-x509_hash_init(void)
-{
- struct x509_hash *certh;
- int i;
-
- bucket_mask = (1 << INITIAL_BUCKET_BITS) - 1;
-
- /* If reinitializing, free existing entries. */
- if (x509_tab) {
- for (i = 0; i <= bucket_mask; i++)
- for (certh = LIST_FIRST(&x509_tab[i]); certh;
- certh = LIST_FIRST(&x509_tab[i])) {
- LIST_REMOVE(certh, link);
- free(certh);
- }
- free(x509_tab);
- }
- x509_tab = malloc((bucket_mask + 1) * sizeof(struct x509_list));
- if (!x509_tab)
- log_fatal("x509_hash_init: malloc (%lu) failed",
- (bucket_mask + 1) *
- (unsigned long)sizeof(struct x509_list));
- for (i = 0; i <= bucket_mask; i++) {
- LIST_INIT(&x509_tab[i]);
- }
-}
-
-/* Lookup a certificate by an ID blob. */
-static X509 *
-x509_hash_find(u_int8_t *id, size_t len)
-{
- struct x509_hash *cert;
- u_int8_t **cid;
- u_int32_t *clen;
- int n, i, id_found;
-
- for (cert = LIST_FIRST(&x509_tab[x509_hash(id, len)]); cert;
- cert = LIST_NEXT(cert, link)) {
- if (!x509_cert_get_subjects(cert->cert, &n, &cid, &clen))
- continue;
-
- id_found = 0;
- for (i = 0; i < n; i++) {
- LOG_DBG_BUF((LOG_CRYPTO, 70, "cert_cmp", id, len));
- LOG_DBG_BUF((LOG_CRYPTO, 70, "cert_cmp", cid[i],
- clen[i]));
- /*
- * XXX This identity predicate needs to be
- * understood.
- */
- if (clen[i] == len && id[0] == cid[i][0] &&
- memcmp(id + 4, cid[i] + 4, len - 4) == 0) {
- id_found++;
- break;
- }
- }
- cert_free_subjects(n, cid, clen);
- if (!id_found)
- continue;
-
- LOG_DBG((LOG_CRYPTO, 70, "x509_hash_find: return X509 %p",
- cert->cert));
- return cert->cert;
- }
-
- LOG_DBG((LOG_CRYPTO, 70,
- "x509_hash_find: no certificate matched query"));
- return 0;
-}
-
-static int
-x509_hash_enter(X509 *cert)
-{
- u_int16_t bucket = 0;
- u_int8_t **id;
- u_int32_t *len;
- struct x509_hash *certh;
- int n, i;
-
- if (!x509_cert_get_subjects(cert, &n, &id, &len)) {
- log_print("x509_hash_enter: cannot retrieve subjects");
- return 0;
- }
- for (i = 0; i < n; i++) {
- certh = calloc(1, sizeof *certh);
- if (!certh) {
- cert_free_subjects(n, id, len);
- log_error("x509_hash_enter: calloc (1, %lu) failed",
- (unsigned long)sizeof *certh);
- return 0;
- }
- certh->cert = cert;
-
- bucket = x509_hash(id[i], len[i]);
-
- LIST_INSERT_HEAD(&x509_tab[bucket], certh, link);
- LOG_DBG((LOG_CRYPTO, 70,
- "x509_hash_enter: cert %p added to bucket %d",
- cert, bucket));
- }
- cert_free_subjects(n, id, len);
-
- return 1;
-}
-
-/* X509 Certificate Handling functions. */
-
-int
-x509_read_from_dir(X509_STORE *ctx, char *name, int hash)
-{
- struct dirent *file;
-#if defined (USE_PRIVSEP)
- struct monitor_dirents *dir;
-#else
- DIR *dir;
-#endif
- FILE *certfp;
- X509 *cert;
- struct stat sb;
- char fullname[PATH_MAX];
- int fd, off, size;
-
- if (strlen(name) >= sizeof fullname - 1) {
- log_print("x509_read_from_dir: directory name too long");
- return 0;
- }
- LOG_DBG((LOG_CRYPTO, 40, "x509_read_from_dir: reading certs from %s",
- name));
-
- dir = monitor_opendir(name);
- if (!dir) {
- LOG_DBG((LOG_CRYPTO, 10,
- "x509_read_from_dir: opendir (\"%s\") failed: %s",
- name, strerror(errno)));
- return 0;
- }
- strlcpy(fullname, name, sizeof fullname);
- off = strlen(fullname);
- size = sizeof fullname - off;
-
- while ((file = monitor_readdir(dir)) != NULL) {
- strlcpy(fullname + off, file->d_name, size);
-
- if (file->d_type != DT_UNKNOWN) {
- if (file->d_type != DT_REG && file->d_type != DT_LNK)
- continue;
- }
-
- LOG_DBG((LOG_CRYPTO, 60,
- "x509_read_from_dir: reading certificate %s",
- file->d_name));
-
- if ((fd = monitor_open(fullname, O_RDONLY, 0)) == -1) {
- log_error("x509_read_from_dir: monitor_open"
- "(\"%s\", O_RDONLY, 0) failed", fullname);
- continue;
- }
-
- if (fstat(fd, &sb) == -1) {
- log_error("x509_read_from_dir: fstat failed");
- close(fd);
- continue;
- }
-
- if (!(sb.st_mode & S_IFREG)) {
- close(fd);
- continue;
- }
-
- if ((certfp = fdopen(fd, "r")) == NULL) {
- log_error("x509_read_from_dir: fdopen failed");
- close(fd);
- continue;
- }
-
-#if SSLEAY_VERSION_NUMBER >= 0x00904100L
- cert = PEM_read_X509(certfp, NULL, NULL, NULL);
-#else
- cert = PEM_read_X509(certfp, NULL, NULL);
-#endif
- fclose(certfp);
-
- if (cert == NULL) {
- log_print("x509_read_from_dir: PEM_read_bio_X509 "
- "failed for %s", file->d_name);
- continue;
- }
- 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_DBG((LOG_CRYPTO, 50,
- "x509_read_from_dir: X509_STORE_add_cert failed "
- "for %s", file->d_name));
- }
- if (hash)
- if (!x509_hash_enter(cert))
- log_print("x509_read_from_dir: "
- "x509_hash_enter (%s) failed",
- file->d_name);
- }
-
- monitor_closedir(dir);
-
- return 1;
-}
-
-/* XXX share code with x509_read_from_dir() ? */
-int
-x509_read_crls_from_dir(X509_STORE *ctx, char *name)
-{
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L
- struct dirent *file;
-#if defined (USE_PRIVSEP)
- struct monitor_dirents *dir;
-#else
- DIR *dir;
-#endif
- FILE *crlfp;
- X509_CRL *crl;
- struct stat sb;
- char fullname[PATH_MAX];
- int fd, off, size;
-
- if (strlen(name) >= sizeof fullname - 1) {
- log_print("x509_read_crls_from_dir: directory name too long");
- return 0;
- }
- LOG_DBG((LOG_CRYPTO, 40, "x509_read_crls_from_dir: reading CRLs "
- "from %s", name));
-
- dir = monitor_opendir(name);
- if (!dir) {
- LOG_DBG((LOG_CRYPTO, 10, "x509_read_crls_from_dir: opendir "
- "(\"%s\") failed: %s", name, strerror(errno)));
- return 0;
- }
- strlcpy(fullname, name, sizeof fullname);
- off = strlen(fullname);
- size = sizeof fullname - off;
-
- while ((file = monitor_readdir(dir)) != NULL) {
- strlcpy(fullname + off, file->d_name, size);
-
- if (file->d_type != DT_UNKNOWN) {
- if (file->d_type != DT_REG && file->d_type != DT_LNK)
- continue;
- }
-
- LOG_DBG((LOG_CRYPTO, 60, "x509_read_crls_from_dir: reading "
- "CRL %s", file->d_name));
-
- if ((fd = monitor_open(fullname, O_RDONLY, 0)) == -1) {
- log_error("x509_read_crls_from_dir: monitor_open"
- "(\"%s\", O_RDONLY, 0) failed", fullname);
- continue;
- }
-
- if (fstat(fd, &sb) == -1) {
- log_error("x509_read_crls_from_dir: fstat failed");
- close(fd);
- continue;
- }
-
- if (!(sb.st_mode & S_IFREG)) {
- close(fd);
- continue;
- }
-
- if ((crlfp = fdopen(fd, "r")) == NULL) {
- log_error("x509_read_crls_from_dir: fdopen failed");
- close(fd);
- continue;
- }
-
- crl = PEM_read_X509_CRL(crlfp, NULL, NULL, NULL);
-
- fclose(crlfp);
-
- if (crl == NULL) {
- log_print("x509_read_crls_from_dir: "
- "PEM_read_X509_CRL failed for %s",
- file->d_name);
- continue;
- }
- if (!X509_STORE_add_crl(ctx, crl)) {
- LOG_DBG((LOG_CRYPTO, 50, "x509_read_crls_from_dir: "
- "X509_STORE_add_crl failed for %s", file->d_name));
- continue;
- }
- /*
- * XXX This is to make x509_cert_validate set this (and
- * XXX another) flag when validating certificates. Currently,
- * XXX OpenSSL defaults to reject an otherwise valid
- * XXX certificate (chain) if these flags are set but there
- * XXX are no CRLs to check. The current workaround is to only
- * XXX set the flags if we actually loaded some CRL data.
- */
- X509_STORE_set_flags(ctx, X509_V_FLAG_CRL_CHECK);
- }
-
- monitor_closedir(dir);
-#endif /* OPENSSL_VERSION_NUMBER >= 0x00907000L */
-
- return 1;
-}
-
-/* Initialize our databases and load our own certificates. */
-int
-x509_cert_init(void)
-{
- char *dirname;
-
- x509_hash_init();
-
- /* Process CA certificates we will trust. */
- dirname = conf_get_str("X509-certificates", "CA-directory");
- if (!dirname) {
- log_print("x509_cert_init: no CA-directory");
- return 0;
- }
- /* Free if already initialized. */
- if (x509_cas)
- X509_STORE_free(x509_cas);
-
- x509_cas = X509_STORE_new();
- if (!x509_cas) {
- 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;
- }
- /* 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;
- }
- /* Free if already initialized. */
- if (x509_certs)
- X509_STORE_free(x509_certs);
-
- x509_certs = X509_STORE_new();
- if (!x509_certs) {
- log_print("x509_cert_init: creating new X509_STORE failed");
- return 0;
- }
- if (!x509_read_from_dir(x509_certs, dirname, 1)) {
- log_print("x509_cert_init: x509_read_from_dir failed");
- return 0;
- }
- return 1;
-}
-
-int
-x509_crl_init(void)
-{
- /*
- * XXX I'm not sure if the method to use CRLs in certificate validation
- * is valid for OpenSSL versions prior to 0.9.7. For now, simply do not
- * support it.
- */
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L
- char *dirname;
- dirname = conf_get_str("X509-certificates", "CRL-directory");
- if (!dirname) {
- log_print("x509_crl_init: no CRL-directory");
- return 0;
- }
- if (!x509_read_crls_from_dir(x509_cas, dirname)) {
- LOG_DBG((LOG_MISC, 10,
- "x509_crl_init: x509_read_crls_from_dir failed"));
- return 0;
- }
-#else
- LOG_DBG((LOG_CRYPTO, 10, "x509_crl_init: CRL support only "
- "with OpenSSL v0.9.7 or later"));
-#endif
-
- return 1;
-}
-
-void *
-x509_cert_get(u_int8_t *asn, u_int32_t len)
-{
- return x509_from_asn(asn, len);
-}
-
-int
-x509_cert_validate(void *scert)
-{
- X509_STORE_CTX csc;
- X509_NAME *issuer, *subject;
- X509 *cert = (X509 *) scert;
- EVP_PKEY *key;
- int res, err;
-
- /*
- * Validate the peer certificate by checking with the CA certificates
- * we trust.
- */
- X509_STORE_CTX_init(&csc, x509_cas, cert, NULL);
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L
- /* XXX See comment in x509_read_crls_from_dir. */
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
- if (x509_cas->param->flags & X509_V_FLAG_CRL_CHECK) {
-#else
- if (x509_cas->flags & X509_V_FLAG_CRL_CHECK) {
-#endif
- X509_STORE_CTX_set_flags(&csc, X509_V_FLAG_CRL_CHECK);
- X509_STORE_CTX_set_flags(&csc, X509_V_FLAG_CRL_CHECK_ALL);
- }
-#endif
- res = X509_verify_cert(&csc);
- err = csc.error;
- X509_STORE_CTX_cleanup(&csc);
-
- /*
- * Return if validation succeeded or self-signed certs are not
- * accepted.
- *
- * XXX X509_verify_cert seems to return -1 if the validation should be
- * retried somehow. We take this as an error and give up.
- */
- if (res > 0)
- return 1;
- else if (res < 0 ||
- (res == 0 && err != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)) {
- if (err)
- log_print("x509_cert_validate: %.100s",
- X509_verify_cert_error_string(err));
- return 0;
- } else if (!conf_get_str("X509-certificates", "Accept-self-signed")) {
- if (err)
- log_print("x509_cert_validate: %.100s",
- X509_verify_cert_error_string(err));
- return 0;
- }
- 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) {
- log_print("x509_cert_validate: could not get public key from "
- "self-signed cert");
- return 0;
- }
- if (X509_verify(cert, key) == -1) {
- log_print("x509_cert_validate: self-signed cert is bad");
- return 0;
- }
- return 1;
-}
-
-int
-x509_cert_insert(int id, void *scert)
-{
- X509 *cert;
- int res;
-
- cert = X509_dup((X509 *)scert);
- if (!cert) {
- log_print("x509_cert_insert: X509_dup failed");
- return 0;
- }
-#ifdef USE_POLICY
- if (x509_generate_kn(id, cert) == 0) {
- LOG_DBG((LOG_POLICY, 50,
- "x509_cert_insert: x509_generate_kn failed"));
- X509_free(cert);
- return 0;
- }
-#endif /* USE_POLICY */
-
- res = x509_hash_enter(cert);
- if (!res)
- X509_free(cert);
-
- return res;
-}
-
-static struct x509_hash *
-x509_hash_lookup(X509 *cert)
-{
- struct x509_hash *certh;
- int i;
-
- for (i = 0; i <= bucket_mask; i++)
- for (certh = LIST_FIRST(&x509_tab[i]); certh;
- certh = LIST_NEXT(certh, link))
- if (certh->cert == cert)
- return certh;
- return 0;
-}
-
-void
-x509_cert_free(void *cert)
-{
- struct x509_hash *certh = x509_hash_lookup((X509 *) cert);
-
- if (certh)
- LIST_REMOVE(certh, link);
- X509_free((X509 *) cert);
-}
-
-/* Validate the BER Encoding of a RDNSequence in the CERT_REQ payload. */
-int
-x509_certreq_validate(u_int8_t *asn, u_int32_t len)
-{
- int res = 1;
-#if 0
- struct norm_type name = SEQOF("issuer", RDNSequence);
-
- 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);
-#endif
-
- /* XXX - not supported directly in SSL - later. */
-
- return res;
-}
-
-/* Decode the BER Encoding of a RDNSequence in the CERT_REQ payload. */
-void *
-x509_certreq_decode(u_int8_t *asn, u_int32_t len)
-{
-#if 0
- /* XXX This needs to be done later. */
- struct norm_type aca = SEQOF("aca", RDNSequence);
- struct norm_type *tmp;
- struct x509_aca naca, *ret;
-
- if (!asn_template_clone(&aca, 1) ||
- (asn = asn_decode_sequence(asn, len, &aca)) == 0) {
- log_print("x509_certreq_decode: can not decode 'acceptable "
- "CA' info");
- goto fail;
- }
- memset(&naca, 0, sizeof(naca));
-
- tmp = asn_decompose("aca.RelativeDistinguishedName."
- "AttributeValueAssertion", &aca);
- if (!tmp)
- goto fail;
- x509_get_attribval(tmp, &naca.name1);
-
- tmp = asn_decompose("aca.RelativeDistinguishedName[1]"
- ".AttributeValueAssertion", &aca);
- if (tmp)
- x509_get_attribval(tmp, &naca.name2);
-
- asn_free(&aca);
-
- ret = malloc(sizeof(struct x509_aca));
- if (ret)
- memcpy(ret, &naca, sizeof(struct x509_aca));
- else {
- log_error("x509_certreq_decode: malloc (%lu) failed",
- (unsigned long) sizeof(struct x509_aca));
- x509_free_aca(&aca);
- }
-
- return ret;
-
-fail:
- asn_free(&aca);
-#endif
- return 0;
-}
-
-void
-x509_free_aca(void *blob)
-{
- struct x509_aca *aca = blob;
-
- if (aca->name1.type)
- free(aca->name1.type);
- if (aca->name1.val)
- free(aca->name1.val);
-
- if (aca->name2.type)
- free(aca->name2.type);
- if (aca->name2.val)
- free(aca->name2.val);
-}
-
-X509 *
-x509_from_asn(u_char *asn, u_int len)
-{
- BIO *certh;
- X509 *scert = 0;
-
- certh = BIO_new(BIO_s_mem());
- if (!certh) {
- log_error("x509_from_asn: BIO_new (BIO_s_mem ()) failed");
- return 0;
- }
- if (BIO_write(certh, asn, len) == -1) {
- log_error("x509_from_asn: BIO_write failed\n");
- goto end;
- }
- scert = d2i_X509_bio(certh, NULL);
- if (!scert) {
- log_print("x509_from_asn: d2i_X509_bio failed\n");
- goto end;
- }
-end:
- BIO_free(certh);
- return scert;
-}
-
-/*
- * Obtain a certificate from an acceptable CA.
- * XXX We don't check if the certificate we find is from an accepted CA.
- */
-int
-x509_cert_obtain(u_int8_t *id, size_t id_len, void *data, u_int8_t **cert,
- u_int32_t *certlen)
-{
- struct x509_aca *aca = data;
- X509 *scert;
-
- if (aca)
- LOG_DBG((LOG_CRYPTO, 60, "x509_cert_obtain: "
- "acceptable certificate authorities here"));
-
- /* We need our ID to find a certificate. */
- if (!id) {
- log_print("x509_cert_obtain: ID is missing");
- return 0;
- }
- scert = x509_hash_find(id, id_len);
- if (!scert)
- return 0;
-
- x509_serialize(scert, cert, certlen);
- if (!*cert)
- return 0;
- return 1;
-}
-
-/* Returns a pointer to the subjectAltName information of X509 certificate. */
-int
-x509_cert_subjectaltname(X509 *scert, u_int8_t **altname, u_int32_t *len)
-{
- X509_EXTENSION *subjectaltname;
- u_int8_t *sandata;
- int extpos, santype, sanlen;
-
- extpos = X509_get_ext_by_NID(scert, NID_subject_alt_name, -1);
- if (extpos == -1) {
- log_print("x509_cert_subjectaltname: "
- "certificate does not contain subjectAltName");
- return 0;
- }
- subjectaltname = X509_get_ext(scert, extpos);
-
- if (!subjectaltname || !subjectaltname->value ||
- !subjectaltname->value->data ||
- subjectaltname->value->length < 4) {
- log_print("x509_cert_subjectaltname: invalid "
- "subjectaltname extension");
- return 0;
- }
- /* 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;
-
- if (sanlen + 4 != subjectaltname->value->length) {
- log_print("x509_cert_subjectaltname: subjectaltname invalid "
- "length");
- return 0;
- }
- *len = sanlen;
- *altname = sandata;
- return santype;
-}
-
-int
-x509_cert_get_subjects(void *scert, int *cnt, u_int8_t ***id,
- u_int32_t **id_len)
-{
- X509 *cert = scert;
- X509_NAME *subject;
- int type;
- u_int8_t *altname;
- u_int32_t altlen;
- u_int8_t *buf = 0;
- unsigned char *ubuf;
- int i;
-
- *id = 0;
- *id_len = 0;
-
- /*
- * XXX There can be a collection of subjectAltNames, but for now I
- * only return the subjectName and a single subjectAltName, if
- * present.
- */
- type = x509_cert_subjectaltname(cert, &altname, &altlen);
- if (!type) {
- *cnt = 1;
- altlen = 0;
- } else
- *cnt = 2;
-
- *id = calloc(*cnt, sizeof **id);
- if (!*id) {
- log_print("x509_cert_get_subject: malloc (%lu) failed",
- *cnt * (unsigned long)sizeof **id);
- goto fail;
- }
- *id_len = malloc(*cnt * sizeof **id_len);
- if (!*id_len) {
- log_print("x509_cert_get_subject: malloc (%lu) failed",
- *cnt * (unsigned long)sizeof **id_len);
- goto fail;
- }
- /* Stash the subjectName into the first slot. */
- subject = X509_get_subject_name(cert);
- if (!subject)
- goto fail;
-
- (*id_len)[0] =
- ISAKMP_ID_DATA_OFF + i2d_X509_NAME(subject, NULL) -
- ISAKMP_GEN_SZ;
- (*id)[0] = malloc((*id_len)[0]);
- if (!(*id)[0]) {
- log_print("x509_cert_get_subject: malloc (%d) failed",
- (*id_len)[0]);
- goto fail;
- }
- SET_ISAKMP_ID_TYPE((*id)[0] - ISAKMP_GEN_SZ, IPSEC_ID_DER_ASN1_DN);
- ubuf = (*id)[0] + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;
- i2d_X509_NAME(subject, &ubuf);
-
- if (altlen) {
- /* Stash the subjectAltName into the second slot. */
- buf = malloc(altlen + ISAKMP_ID_DATA_OFF);
- if (!buf) {
- log_print("x509_cert_get_subject: malloc (%d) failed",
- altlen + ISAKMP_ID_DATA_OFF);
- goto fail;
- }
- switch (type) {
- case X509v3_DNS_NAME:
- SET_ISAKMP_ID_TYPE(buf, IPSEC_ID_FQDN);
- break;
-
- case X509v3_RFC_NAME:
- SET_ISAKMP_ID_TYPE(buf, IPSEC_ID_USER_FQDN);
- break;
-
- case X509v3_IP_ADDR:
- /*
- * XXX I dislike the numeric constants, but I don't
- * know what we should use otherwise.
- */
- switch (altlen) {
- case 4:
- SET_ISAKMP_ID_TYPE(buf, IPSEC_ID_IPV4_ADDR);
- break;
-
- case 16:
- SET_ISAKMP_ID_TYPE(buf, IPSEC_ID_IPV6_ADDR);
- break;
-
- default:
- log_print("x509_cert_get_subject: invalid "
- "subjectAltName IPaddress length %d ",
- altlen);
- goto fail;
- }
- break;
- }
-
- 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)[1] = ISAKMP_ID_DATA_OFF + altlen - ISAKMP_GEN_SZ;
- (*id)[1] = malloc((*id_len)[1]);
- if (!(*id)[1]) {
- log_print("x509_cert_get_subject: malloc (%d) failed",
- (*id_len)[1]);
- goto fail;
- }
- memcpy((*id)[1], buf + ISAKMP_GEN_SZ, (*id_len)[1]);
-
- free(buf);
- buf = 0;
- }
- return 1;
-
-fail:
- for (i = 0; i < *cnt; i++)
- if ((*id)[i])
- free((*id)[i]);
- if (*id)
- free(*id);
- if (*id_len)
- free(*id_len);
- if (buf)
- free(buf);
- return 0;
-}
-
-int
-x509_cert_get_key(void *scert, void *keyp)
-{
- X509 *cert = scert;
- EVP_PKEY *key;
-
- key = X509_get_pubkey(cert);
-
- /* Check if we got the right key type. */
- if (key->type != EVP_PKEY_RSA) {
- log_print("x509_cert_get_key: public key is not a RSA key");
- X509_free(cert);
- return 0;
- }
- *(RSA **)keyp = RSAPublicKey_dup(key->pkey.rsa);
-
- return *(RSA **)keyp == NULL ? 0 : 1;
-}
-
-void *
-x509_cert_dup(void *scert)
-{
- return X509_dup(scert);
-}
-
-void
-x509_serialize(void *scert, u_int8_t **data, u_int32_t *datalen)
-{
- u_int8_t *p;
-
- *datalen = i2d_X509((X509 *)scert, NULL);
- *data = p = malloc(*datalen);
- if (!p) {
- log_error("x509_serialize: malloc (%d) failed", *datalen);
- return;
- }
- *datalen = i2d_X509((X509 *)scert, &p);
-}
-
-/* From cert to printable */
-char *
-x509_printable(void *cert)
-{
- char *s;
- u_int8_t *data;
- u_int32_t datalen, i;
-
- x509_serialize(cert, &data, &datalen);
- if (!data)
- return 0;
-
- s = malloc(datalen * 2 + 1);
- if (!s) {
- free(data);
- log_error("x509_printable: malloc (%d) failed",
- datalen * 2 + 1);
- return 0;
- }
- for (i = 0; i < datalen; i++)
- snprintf(s + (2 * i), 2 * (datalen - i) + 1, "%02x", data[i]);
- free(data);
- return s;
-}
-
-/* From printable to cert */
-void *
-x509_from_printable(char *cert)
-{
- u_int8_t *buf;
- int plen, ret;
- void *foo;
-
- plen = (strlen(cert) + 1) / 2;
- buf = malloc(plen);
- if (!buf) {
- log_error("x509_from_printable: malloc (%d) failed", plen);
- return 0;
- }
- ret = hex2raw(cert, buf, plen);
- if (ret == -1) {
- free(buf);
- log_print("x509_from_printable: badly formatted cert");
- return 0;
- }
- foo = x509_cert_get(buf, plen);
- free(buf);
- if (!foo)
- log_print("x509_from_printable: "
- "could not retrieve certificate");
- return foo;
-}
-
-char *
-x509_DN_string(u_int8_t *asn1, size_t sz)
-{
- X509_NAME *name;
- u_int8_t *p = asn1;
- char buf[256]; /* XXX Just a guess at a maximum length. */
-
- name = d2i_X509_NAME(NULL, &p, sz);
- if (!name) {
- log_print("x509_DN_string: d2i_X509_NAME failed");
- return 0;
- }
- if (!X509_NAME_oneline(name, buf, sizeof buf - 1)) {
- log_print("x509_DN_string: X509_NAME_oneline failed");
- X509_NAME_free(name);
- return 0;
- }
- X509_NAME_free(name);
- buf[sizeof buf - 1] = '\0';
- return strdup(buf);
-}
-#endif /* USE_X509 */