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, 1439 insertions, 0 deletions
diff --git a/keyexchange/isakmpd-20041012/x509.c b/keyexchange/isakmpd-20041012/x509.c
new file mode 100644
index 0000000..0897557
--- /dev/null
+++ b/keyexchange/isakmpd-20041012/x509.c
@@ -0,0 +1,1439 @@
+/* $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 */