summaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorNiklas Hallqvist <niklas@cvs.openbsd.org>1998-11-16 21:07:19 +0000
committerNiklas Hallqvist <niklas@cvs.openbsd.org>1998-11-16 21:07:19 +0000
commitd5283902fbf43a518a7037d5bb3bd4adc0e8fd90 (patch)
tree4cfdea7ed80757449a80454f73bec1194af7ce5e /sbin
parent8d3cbec8eba3b6d6bfa5ea62b1b99d18aa0173c2 (diff)
Reinstate X509 signature code except for RSA code
Diffstat (limited to 'sbin')
-rw-r--r--sbin/isakmpd/Makefile8
-rw-r--r--sbin/isakmpd/asn.c1155
-rw-r--r--sbin/isakmpd/asn_useful.c128
-rw-r--r--sbin/isakmpd/cert.c4
-rw-r--r--sbin/isakmpd/ike_auth.c278
-rw-r--r--sbin/isakmpd/pkcs.c370
-rw-r--r--sbin/isakmpd/x509.c844
7 files changed, 2778 insertions, 9 deletions
diff --git a/sbin/isakmpd/Makefile b/sbin/isakmpd/Makefile
index 05a9565e796..a1f83cbd9ef 100644
--- a/sbin/isakmpd/Makefile
+++ b/sbin/isakmpd/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.4 1998/11/16 12:30:59 niklas Exp $
+# $OpenBSD: Makefile,v 1.5 1998/11/16 21:07:13 niklas Exp $
#
# Copyright (c) 1998 Niklas Hallqvist. All rights reserved.
@@ -34,14 +34,14 @@
#
PROG= isakmpd
-SRCS= app.c attribute.c cert.c constants.c \
+SRCS= app.c asn.c asn_useful.c attribute.c cert.c constants.c \
conf.c cookie.c crypto.c dh.c doi.c exchange.c exchange_num.c \
field.c gmp_util.c hash.c if.c ike_auth.c ike_main_mode.c \
ike_quick_mode.c init.c ipsec.c ipsec_fld.c ipsec_num.c \
isakmpd.c isakmp_doi.c isakmp_fld.c isakmp_num.c log.c \
message.c math_2n.c math_ec2n.c math_group.c pf_encap.c \
- prf.c sa.c sysdep.c timer.c transport.c udp.c ui.c \
- util.c
+ pkcs.c prf.c sa.c sysdep.c timer.c transport.c udp.c ui.c \
+ util.c x509.c
CLEANFILES= exchange_num.c exchange_num.h ipsec_num.c ipsec_num.h \
isakmp_num.c isakmp_num.h ipsec_fld.c ipsec_fld.h \
isakmp_fld.c isakmp_fld.h
diff --git a/sbin/isakmpd/asn.c b/sbin/isakmpd/asn.c
new file mode 100644
index 00000000000..fb6af6cec83
--- /dev/null
+++ b/sbin/isakmpd/asn.c
@@ -0,0 +1,1155 @@
+/* $OpenBSD: asn.c,v 1.4 1998/11/16 21:07:14 niklas Exp $ */
+
+/*
+ * Copyright (c) 1998 Niels Provos. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ericsson Radio Systems.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gmp.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "asn.h"
+#include "gmp_util.h"
+
+struct asn_handler table[] = {
+ {TAG_INTEGER, asn_free_integer,
+ asn_get_encoded_len_integer, asn_decode_integer, asn_encode_integer},
+ {TAG_OBJECTID, asn_free_objectid,
+ asn_get_encoded_len_objectid, asn_decode_objectid, asn_encode_objectid},
+ {TAG_SEQUENCE, asn_free_sequence,
+ asn_get_encoded_len_sequence, asn_decode_sequence, asn_encode_sequence},
+ {TAG_SET, asn_free_sequence,
+ asn_get_encoded_len_sequence, asn_decode_sequence, asn_encode_sequence},
+ {TAG_UTCTIME, asn_free_string,
+ asn_get_encoded_len_string, asn_decode_string, asn_encode_string},
+ {TAG_BITSTRING, asn_free_string,
+ asn_get_encoded_len_string, asn_decode_string, asn_encode_string},
+ {TAG_OCTETSTRING, asn_free_string,
+ asn_get_encoded_len_string, asn_decode_string, asn_encode_string},
+ {TAG_BOOL, asn_free_string,
+ asn_get_encoded_len_string, asn_decode_string, asn_encode_string},
+ {TAG_PRINTSTRING, asn_free_string,
+ asn_get_encoded_len_string, asn_decode_string, asn_encode_string},
+ {TAG_RAW, asn_free_raw,
+ asn_get_encoded_len_raw, asn_decode_raw, asn_encode_raw},
+ {TAG_NULL, asn_free_null,
+ asn_get_encoded_len_null, asn_decode_null, asn_encode_null},
+ {TAG_ANY, asn_free_null,
+ NULL, asn_decode_any, NULL},
+ {TAG_STOP, NULL, NULL, NULL, NULL}
+};
+
+int
+asn_get_from_file (char *name, u_int8_t **asn, u_int32_t *asnlen)
+{
+ int fd, res = 0;
+ struct stat st;
+
+ if (stat (name, &st) == -1)
+ {
+ log_error ("asn_get_from_file: failed to state %s", name);
+ return 0;
+ }
+
+ *asnlen = st.st_size;
+
+ if ((fd = open (name, O_RDONLY)) == -1)
+ {
+ log_error ("asn_get_from_file: failed to open %s", name);
+ return 0;
+ }
+
+ if ((*asn = malloc (st.st_size)) == NULL)
+ {
+ log_print ("asn_get_from_file: out of memory");
+ res = 0;
+ goto done;
+ }
+
+ if (read (fd, *asn, st.st_size) != st.st_size ||
+ asn_get_len (*asn) != *asnlen)
+ {
+ log_print ("x509_asn_obtain: asn file ended early");
+ free (*asn);
+ res = 0;
+ goto done;
+ }
+
+ res = 1;
+
+ done:
+ close (fd);
+
+ return res;
+}
+
+struct norm_type *
+asn_template_clone (struct norm_type *obj, int constructed)
+{
+ struct norm_type *p;
+ u_int32_t i;
+
+ if (!constructed)
+ {
+ p = malloc (sizeof (struct norm_type));
+ if (p == NULL)
+ return NULL;
+
+ memcpy (p, obj, sizeof (struct norm_type));
+
+ obj = p;
+ }
+
+ if (obj->type != TAG_SEQUENCE && obj->type != TAG_SET)
+ {
+ obj->len = 0;
+ obj->data = NULL;
+ }
+ else if (obj->type == TAG_SEQUENCE || obj->type == TAG_SET)
+ {
+ p = obj;
+ obj = obj->data;
+ i = 0;
+ while (obj[i++].type != TAG_STOP);
+
+ p->data = malloc (i * sizeof (struct norm_type));
+ if (p->data == NULL)
+ return NULL;
+
+ memcpy (p->data, obj, i * sizeof (struct norm_type));
+ obj = p->data;
+
+ i = 0;
+ while (obj[i].type != TAG_STOP)
+ {
+ obj[i].len = 0;
+ if (asn_template_clone (&obj[i], 1) == NULL)
+ return NULL;
+
+ i++;
+ }
+ }
+
+ return obj;
+}
+
+/* Associates a human readable name to an OBJECT IDENTIFIER */
+
+char *
+asn_parse_objectid (struct asn_objectid *table, char *id)
+{
+ u_int32_t len = 0;
+ char *p = NULL;
+ static char buf[LINE_MAX];
+
+ if (id == NULL)
+ return NULL;
+
+ while (table->name != NULL)
+ {
+ if (!strcmp (table->objectid, id))
+ return table->name;
+ if (!strncmp (table->objectid, id, strlen (table->objectid)) &&
+ strlen (table->objectid) > len)
+ {
+ len = strlen (table->objectid);
+ p = table->name;
+ }
+
+ table++;
+ }
+
+ if (len == 0)
+ return NULL;
+
+ strncpy (buf, p, sizeof (buf) - 1);
+ buf[sizeof (buf) - 1] = 0;
+ strncat (buf + strlen (buf), id + len, sizeof (buf) -1 - strlen (buf));
+ buf[sizeof (buf) - 1] = 0;
+
+ return buf;
+}
+
+/* Retrieves the pointer to a data type referenced by the path name */
+
+struct norm_type *
+asn_decompose (char *path, struct norm_type *obj)
+{
+ char *p, *p2, *tmp;
+ int counter;
+
+ if (!strcasecmp (path, obj->name))
+ return obj->data;
+
+ p = path = strdup (path);
+ p2 = strsep (&p, ".");
+
+ if (strcasecmp (p2, obj->name) || p == NULL)
+ goto fail;
+
+ while (p != NULL)
+ {
+ obj = obj->data;
+ if (obj == NULL)
+ break;
+
+ p2 = strsep (&p, ".");
+
+ /*
+ * For SEQUENCE OF or SET OF, we want to be able to say
+ * AttributeValueAssertion[1] for the 2nd value.
+ */
+ tmp = strchr (p2, '[');
+ if (tmp != NULL)
+ {
+ counter = atoi (tmp+1);
+ *tmp = 0;
+ }
+ else
+ counter = 0;
+
+ /* Find the Tag */
+ while (obj->type != TAG_STOP)
+ {
+ if (!strcasecmp (p2, obj->name) && counter-- == 0)
+ break;
+ obj++;
+ }
+
+ if (obj->type == TAG_STOP)
+ goto fail;
+
+ if (p == NULL)
+ goto done;
+
+ if (obj->type != TAG_SEQUENCE && obj->type != TAG_SET)
+ goto fail;
+ }
+
+ done:
+ free (path);
+ return obj;
+
+ fail:
+ free (path);
+ return NULL;
+}
+
+/* Gets an entry from the ASN.1 tag switch table */
+
+struct asn_handler *
+asn_get (enum asn_tags type)
+{
+ struct asn_handler *h = table;
+
+ while (h->type != TAG_STOP)
+ if (h->type == type)
+ return h;
+ else
+ h++;
+
+ return NULL;
+}
+
+/*
+ * For the long form of BER encoding we need to know in how many
+ * octets the length can be encoded.
+ */
+
+u_int32_t
+asn_sizeinoctets (u_int32_t len)
+{
+ u_int32_t log = 0;
+
+ while (len)
+ {
+ log++;
+ len >>= 8;
+ }
+
+ return log;
+}
+
+u_int8_t *
+asn_format_header (struct norm_type *obj, u_int8_t *asn, u_int8_t **data)
+{
+ u_int8_t *buf = NULL, type;
+ u_int16_t len_off, len;
+ struct asn_handler *h;
+
+ h = asn_get (obj->type);
+ if (h == NULL)
+ return NULL;
+
+ if (asn != NULL)
+ buf = asn;
+
+ /* We only do low tag at the moment */
+ len_off = 1;
+
+ len = h->get_encoded_len (obj, &type);
+
+ if (buf == NULL && (buf = malloc (len)) == NULL)
+ return NULL;
+
+ if (type != ASN_LONG_FORM)
+ {
+ len -= len_off + 1;
+ buf[len_off] = len;
+
+ *data = buf + len_off + 1;
+ }
+ else
+ {
+ u_int16_t tmp;
+ int octets = asn_sizeinoctets (len);
+
+ len -= len_off + 1 + octets;
+ *data = buf + len_off + 1 + octets;
+
+ buf[len_off] = octets | ASN_LONG_FORM;
+
+ tmp = len;
+ while (--octets >= 0)
+ {
+ buf[len_off + 1 + octets] = tmp;
+ tmp >>= 8;
+ }
+ }
+
+ if (ISEXPLICIT(obj))
+ {
+ u_int8_t *erg;
+ /* Explicit tagging add an outer layer */
+ struct norm_type tmp = {obj->type, obj->class&0x3, NULL, 0, obj->data};
+
+ /* XXX - force the class to be CONTEXT */
+ buf[0] = GET_EXP(obj) | (((enum asn_classes)CONTEXT & 0x3) << 6) |
+ ASN_CONSTRUCTED;
+ erg = asn_format_header (&tmp, *data, data);
+
+ if (erg && (obj->type == TAG_SEQUENCE || obj->type == TAG_SET))
+ erg[0] |= ASN_CONSTRUCTED;
+ }
+ else
+ /* XXX low tag only */
+ buf[0] = obj->type | (obj->class << 6);
+
+ return buf;
+}
+
+u_int32_t
+asn_get_encoded_len (struct norm_type *obj, u_int32_t len, u_int8_t *type)
+{
+ u_int32_t len_off = 1;
+
+ if (len <= 127)
+ {
+ /* Short form */
+ len = len + 1 + len_off;
+ if (type != NULL)
+ *type = 0;
+ }
+ else
+ {
+ /* Long Form */
+ len = len + asn_sizeinoctets (len) + 1 + len_off;
+ if (type != NULL)
+ *type = ASN_LONG_FORM;
+ }
+
+ if (obj != NULL && ISEXPLICIT(obj))
+ len = asn_get_encoded_len (NULL, len, NULL);
+
+ return len;
+}
+
+/* Tries to decode an ANY tag, if we cant handle it we just raw encode it */
+
+u_int8_t *
+asn_decode_any (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj)
+{
+ struct asn_handler *h;
+ enum asn_tags type;
+
+ type = TAG_TYPE (asn);
+ if (type == TAG_SEQUENCE || type == TAG_SET)
+ type = TAG_RAW;
+
+ h = asn_get (type);
+ if (h == NULL)
+ {
+ type = TAG_RAW;
+ h = asn_get (type);
+ }
+
+ obj->type = type;
+ return h->decode (asn, asnlen, obj);
+}
+
+u_int32_t
+asn_get_encoded_len_integer (struct norm_type *obj, u_int8_t *type)
+{
+ u_int16_t len_off;
+ u_int32_t len = obj->len;
+ u_int32_t tmp;
+ mpz_t a;
+
+ /* XXX - We only do low tag at the moment */
+ len_off = 1;
+
+ obj->len = len = mpz_sizeinoctets ((mpz_ptr) obj->data);
+ mpz_init_set (a, (mpz_ptr) obj->data);
+
+ if (len > 1)
+ mpz_fdiv_q_2exp (a, a, (len - 1) << 3);
+
+ tmp = mpz_fdiv_r_ui (a, a, 256);
+ mpz_clear (a);
+
+ /*
+ * We only need to encode positive integers, ASN.1 defines
+ * negative integers to have the msb set, so if data[0] has
+ * msb set we need to introduce a zero octet.
+ */
+ if (tmp & 0x80)
+ len++;
+
+ return asn_get_encoded_len (obj, len, type);
+}
+
+/*
+ * Encode an integer value.
+ * Input = obj, output = asn or return value.
+ */
+
+u_int8_t *
+asn_encode_integer (struct norm_type *obj, u_int8_t *asn)
+{
+ u_int8_t *buf, *data;
+ u_int32_t len;
+
+ buf = asn_format_header (obj, asn, &data);
+
+ if (buf == NULL)
+ return NULL;
+
+ len = mpz_sizeinoctets ((mpz_ptr) obj->data);
+ mpz_getraw (data, (mpz_ptr) obj->data, len);
+
+ /* XXX - We only deal with unsigned integers at the moment */
+ if (data[0] & 0x80)
+ {
+ memmove (data + 1, data, len);
+ data[0] = 0;
+ }
+
+ return buf;
+}
+
+u_int8_t *
+asn_decode_integer (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj)
+{
+ u_int8_t *data;
+ u_int32_t len;
+
+ if (asnlen < asn_get_len (asn))
+ {
+ log_print ("asn_decode_integer: ASN.1 content is bigger than buffer");
+ return NULL;
+ }
+
+ len = asn_get_data_len (obj, &asn, &data);
+
+ if (TAG_TYPE(asn) != TAG_INTEGER)
+ {
+ log_print ("asn_decode_integer: expected tag type INTEGER, got %d",
+ TAG_TYPE(asn));
+ return NULL;
+ }
+
+ obj->data = malloc (sizeof (mpz_ptr));
+ if (obj->data == NULL)
+ {
+ log_print ("asn_decode_integer: out of memory.");
+ return NULL;
+ }
+
+ mpz_init ((mpz_ptr) obj->data);
+ mpz_setraw ((mpz_ptr) obj->data, data, len);
+
+ obj->len = len;
+
+ return data + len;
+}
+
+void
+asn_free_integer (struct norm_type *obj)
+{
+ if (obj->data != NULL)
+ {
+ mpz_clear ((mpz_ptr) obj->data);
+ free (obj->data);
+ }
+}
+
+
+u_int32_t
+asn_get_encoded_len_string (struct norm_type *obj, u_int8_t *type)
+{
+ return asn_get_encoded_len (obj, obj->len, type);
+}
+
+/*
+ * Encode a String
+ * Input = obj, output = asn or return value.
+ */
+
+u_int8_t *
+asn_encode_string (struct norm_type *obj, u_int8_t *asn)
+{
+ u_int8_t *buf, *data;
+
+ buf = asn_format_header (obj, asn, &data);
+
+ if (buf == NULL)
+ return NULL;
+
+ memcpy (data, obj->data, obj->len);
+
+ return buf;
+}
+
+u_int8_t *
+asn_decode_string (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj)
+{
+ u_int8_t *data;
+ u_int32_t len;
+
+ obj->len = len = asn_get_data_len (obj, &asn, &data);
+
+ if (TAG_TYPE(asn) != obj->type)
+ {
+ log_print ("asn_decode_string: expected tag type STRING(%d), got %d",
+ obj->type, TAG_TYPE(asn));
+ return NULL;
+ }
+
+ if (asnlen < asn_get_len (asn))
+ {
+ log_print ("asn_decode_string: ASN.1 content is bigger than buffer");
+ return NULL;
+ }
+
+ obj->data = malloc (obj->len + 1);
+ if (obj->data == NULL)
+ return NULL;
+ memcpy ((char *)obj->data, data, obj->len);
+ /*
+ * Encode a terminating '0', this is irrelevant for OCTET strings
+ * but nice for printable strings which do not include the terminating
+ * zero.
+ */
+ ((char *)obj->data)[obj->len] = 0;
+
+ return data + len;
+}
+
+void
+asn_free_string (struct norm_type *obj)
+{
+ if (obj->data != NULL)
+ free (obj->data);
+}
+
+
+u_int32_t
+asn_get_encoded_len_objectid (struct norm_type *obj, u_int8_t *type)
+{
+ u_int16_t len_off;
+ u_int32_t len;
+ u_int32_t tmp;
+ char *buf, *buf2;
+
+ /* XXX - We only do low tag at the moment */
+ len_off = 1;
+
+ /* The first two numbers are encoded together */
+ buf = obj->data;
+ tmp = strtol (buf, &buf2, 10);
+ buf = buf2;
+ tmp = strtol (buf, &buf2, 10);
+ buf = buf2;
+
+ len = 1;
+ while (*buf)
+ {
+ tmp = strtol (buf, &buf2, 10);
+ if (buf == buf2)
+ break;
+
+ buf = buf2;
+ do {
+ tmp >>= 7;
+ len++;
+ } while (tmp);
+ }
+
+ /* The first two ids are encoded as one octet */
+ obj->len = len - 1;
+
+ return asn_get_encoded_len (obj, len, type);
+}
+
+/*
+ * Encode an Object Identifier
+ * Input = obj, output = asn or return value.
+ */
+
+u_int8_t *
+asn_encode_objectid (struct norm_type *obj, u_int8_t *asn)
+{
+ u_int8_t *buf, *data;
+ char *enc, *enc2;
+ u_int32_t tmp, tmp2;
+ int flag = 0;
+
+ buf = asn_format_header (obj, asn, &data);
+
+ if (buf == NULL)
+ return NULL;
+
+ enc = obj->data;
+ while (*enc)
+ {
+ /* First two ids are encoded as one octet */
+ if (flag == 0)
+ {
+ tmp = strtol (enc, &enc2, 10);
+ if (enc == enc2)
+ return NULL;
+ enc = enc2;
+ tmp2 = strtol (enc, &enc2, 10) + 40 * tmp;
+ flag = 1;
+ }
+ else
+ tmp2 = strtol (enc, &enc2, 10);
+
+ if (enc == enc2)
+ break;
+
+ /* Reverse the digits to base-128 */
+ tmp = 0;
+ do {
+ tmp <<= 7;
+ tmp += tmp2 & 0x7f;
+ tmp2 >>= 7;
+ } while (tmp2);
+
+ enc = enc2;
+ do {
+ /* If the next octet still belongs to the data set msb */
+ *data++ = (tmp & 0x7f) | ( tmp > 127 ? 0x80 : 0);
+ tmp >>= 7;
+ } while (tmp);
+ }
+
+ return buf;
+}
+
+u_int8_t *
+asn_decode_objectid (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj)
+{
+ u_int8_t *data;
+ u_int32_t len, c, tmp;
+ int flag = 0;
+ void *new_buf;
+
+ len = asn_get_data_len (obj, &asn, &data);
+
+ if (TAG_TYPE(asn) != TAG_OBJECTID)
+ {
+ log_print ("asn_decode_objectid: expected tag type OBJECTID, got %d",
+ TAG_TYPE(asn));
+ return NULL;
+ }
+
+ if (asnlen < asn_get_len (asn))
+ {
+ log_print ("asn_decode_objectid: ASN.1 content is bigger than buffer");
+ return NULL;
+ }
+
+ obj->data = NULL;
+ obj->len = 0;
+ while (len > 0)
+ {
+ tmp = 0;
+ do {
+ tmp <<= 7;
+ tmp += *data & 0x7f;
+ } while (len-- > 0 && (*data++ & 0x80));
+
+ if (flag == 0)
+ c = snprintf (NULL, 0, "%d %d ", tmp/40, tmp % 40) + 1;
+ else
+ c = snprintf (NULL, 0, "%d ", tmp) + 1;
+
+ new_buf = realloc (obj->data, obj->len + c);
+ if (new_buf == NULL)
+ {
+ free (obj->data);
+ obj->data = NULL;
+ log_print ("asn_decode_objectid: out of memory.");
+ return NULL;
+ }
+ obj->data = new_buf;
+
+ if (flag == 0)
+ {
+ sprintf (obj->data + obj->len, "%d %d ", tmp/40, tmp % 40);
+ flag = 1;
+ }
+ else
+ sprintf (obj->data + obj->len, "%d ", tmp);
+
+ obj->len = strlen (obj->data);
+ }
+
+ if (obj->data != NULL)
+ ((char *)obj->data)[obj->len - 1] = 0;
+
+ return data;
+}
+
+void
+asn_free_objectid (struct norm_type *obj)
+{
+ if (obj->data != NULL)
+ free (obj->data);
+}
+
+
+u_int32_t
+asn_get_encoded_len_raw (struct norm_type *obj, u_int8_t *type)
+{
+ if (type != NULL)
+ {
+ if (obj->len > 127)
+ *type = ASN_LONG_FORM;
+ else
+ *type = 0;
+ }
+
+ return obj->len;
+}
+
+u_int8_t *
+asn_encode_raw (struct norm_type *obj, u_int8_t *asn)
+{
+ u_int8_t *buf = NULL;
+
+ if (obj->len == 0)
+ return asn;
+
+ if (asn != NULL)
+ buf = asn;
+
+ if (buf == NULL && (buf = malloc (obj->len)) == NULL)
+ return NULL;
+
+ memcpy (buf, obj->data, obj->len);
+
+ return buf;
+}
+
+u_int8_t *
+asn_decode_raw (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj)
+{
+ obj->len = asn_get_len (asn);
+ if (asnlen < obj->len)
+ {
+ log_print ("asn_decode_raw: ASN.1 content is bigger than buffer");
+ return NULL;
+ }
+
+ obj->data = malloc (obj->len);
+ if (obj->data == NULL)
+ {
+ log_print ("asn_decode_raw: out of memory");
+ return NULL;
+ }
+
+ memcpy (obj->data, asn, obj->len);
+
+ return asn + obj->len;
+}
+
+void
+asn_free_raw (struct norm_type *obj)
+{
+ if (obj->data != NULL)
+ free (obj->data);
+}
+
+u_int32_t
+asn_get_encoded_len_null (struct norm_type *obj, u_int8_t *type)
+{
+ return asn_get_encoded_len (obj, 0, type);
+}
+
+u_int8_t *
+asn_encode_null (struct norm_type *obj, u_int8_t *asn)
+{
+ u_int8_t *buf = NULL;
+
+ if (asn != NULL)
+ buf = asn;
+
+ if (buf == NULL && (buf = malloc (2)) == NULL)
+ return NULL;
+
+ buf[0] = obj->type;
+ buf[1] = 0;
+
+ return buf;
+}
+
+u_int8_t *
+asn_decode_null (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj)
+{
+ obj->data = NULL;
+ obj->len = 0;
+
+ return asn + asn_get_len (asn);
+}
+
+void
+asn_free_null (struct norm_type *obj)
+{
+ obj->data = NULL;
+}
+
+void
+asn_free (struct norm_type *obj)
+{
+ struct asn_handler *h = asn_get (obj->type);
+
+ if (h == NULL)
+ log_print ("asn_free: unkown ASN.1 type %d", obj->type);
+ else
+ h->free (obj);
+}
+
+/*
+ * Returns the whole length of the BER encoded ASN.1 object.
+ */
+
+u_int32_t
+asn_get_len (u_int8_t *asn)
+{
+ u_int32_t len;
+ u_int8_t *data;
+ struct norm_type tmp = {TAG_RAW, UNIVERSAL, NULL, 0, NULL};
+
+ len = asn_get_data_len (&tmp, &asn, &data);
+
+ if (asn == NULL)
+ return 0;
+
+ return (data - asn) + len;
+}
+
+/*
+ * Returns the length of the ASN content, and a pointer to the content
+ * data itself.
+ * For TAG_NULL the data length is zero, so we have to return an error
+ * in asn, asn will be NULL in case of error.
+ */
+
+u_int32_t
+asn_get_data_len (struct norm_type *obj, u_int8_t **asn, u_int8_t **data)
+{
+ u_int32_t len;
+ u_int16_t len_off = 1;
+
+ if (obj != NULL && ISEXPLICIT(obj))
+ {
+ struct norm_type tmp = {TAG_RAW, UNIVERSAL, NULL, 0, NULL};
+
+ if (TAG_TYPE(*asn) != GET_EXP(obj))
+ {
+ log_print ("asn_get_data_len: explict tagging was needed");
+ *asn = NULL;
+ return 0;
+ }
+
+ asn_get_data_len (&tmp, asn, data);
+ *asn = *data;
+ }
+
+ if ((*asn)[len_off] & ASN_LONG_FORM)
+ {
+ int i, octets = (*asn)[len_off] & 0x7f;
+
+ /* XXX - we only decode really small length */
+ if (octets > sizeof (len))
+ {
+ log_print ("asn_get_data_len: long form length %d exceeds "
+ "allowed maximum", octets);
+ *asn = NULL;
+ return 0;
+ }
+
+ for (len = 0, i = 0; i < octets; i++)
+ {
+ len = (len << 8) | (*asn)[len_off + 1 + i];
+ }
+
+ if (data != NULL)
+ *data = *asn + len_off + 1 + octets;
+ }
+ else
+ {
+ /* Short form */
+ len = (*asn)[len_off];
+
+ if (data != NULL)
+ *data = *asn + len_off + 1;
+ }
+
+ return len;
+}
+
+void
+asn_free_sequence (struct norm_type *obj)
+{
+ struct norm_type *in = obj->data;
+ struct asn_handler *h;
+
+ if (in == NULL)
+ return;
+
+ while (in->type != TAG_STOP)
+ {
+ h = asn_get (in->type);
+ if (h == NULL)
+ break;
+
+ h->free (in++);
+ }
+
+ free (obj->data);
+}
+
+u_int32_t
+asn_get_encoded_len_sequence (struct norm_type *seq, u_int8_t *type)
+{
+ u_int32_t len, i;
+ struct asn_handler *h;
+ struct norm_type *obj = (struct norm_type *) seq->data;
+
+ /* Get whole data length */
+ for (len = 0, i = 0; obj[i].type != TAG_STOP; i++)
+ {
+ h = asn_get (obj[i].type);
+ if (h == NULL)
+ {
+ log_print ("asn_encode_sequence: unkown type %d", obj[i].type);
+ break;
+ }
+ len += h->get_encoded_len (&obj[i], NULL);
+ }
+
+ return asn_get_encoded_len (seq, len, type);
+}
+
+u_int8_t *
+asn_encode_sequence (struct norm_type *seq, u_int8_t *asn)
+{
+ u_int32_t len;
+ u_int8_t *erg, *data;
+ struct norm_type *obj;
+ struct asn_handler *h;
+ int i;
+
+ if ((h = asn_get (seq->type)) == NULL)
+ return NULL;
+
+ obj = (struct norm_type *) seq->data;
+
+ erg = asn_format_header (seq, asn, &data);
+ if (erg == NULL)
+ return NULL;
+
+ for (i = 0, len = 0; obj[i].type != TAG_STOP; i++)
+ {
+ h = asn_get (obj[i].type);
+ if (h == NULL)
+ {
+ log_print ("asn_encode_sequence: unknown ASN.1 tag %d", obj[i].type);
+ return NULL;
+ }
+
+ /* A structure can be optional, indicated by data == NULL */
+ if (h->encode (&obj[i], data + len) == NULL && obj->data != NULL)
+ {
+ log_print ("asn_encode_sequence: encoding of %s failed",
+ obj[i].name);
+ return NULL;
+ }
+ len += h->get_encoded_len (&obj[i], NULL);
+ }
+
+ erg[0] |= ASN_CONSTRUCTED;
+
+ return erg;
+}
+
+u_int8_t *
+asn_decode_sequence (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj)
+{
+ u_int8_t *p, *data;
+ u_int32_t len, flags, objects;
+ struct asn_handler *h;
+ void *new_buf;
+
+ if (asnlen < asn_get_len (asn))
+ {
+ log_print ("asn_decode_sequence: ASN.1 content is bigger than buffer");
+ return NULL;
+ }
+
+ len = asn_get_data_len (obj, &asn, &data);
+
+ /* XXX - an empty sequence is that okay */
+ if (len == 0)
+ return data;
+
+ if (TAG_TYPE(asn) != obj->type)
+ {
+ log_print ("asn_decode_sequence: expected tag type SEQUENCE/SET, got %d",
+ TAG_TYPE(asn));
+ return NULL;
+ }
+
+ /* Handle dynamic sized sets and sequences */
+ flags = obj->flags;
+
+ if (flags & ASN_FLAG_ZEROORMORE)
+ {
+ struct norm_type stop_tag = {TAG_STOP};
+ struct norm_type *tmp;
+
+ /* Zero occurences */
+ if (len == 0)
+ {
+ asn_free (obj);
+ obj->data = NULL;
+ return data;
+ }
+
+ /* Count number of objects */
+ p = data;
+ objects = 0;
+ while (p < data + len)
+ {
+ objects++;
+ p += asn_get_len (p);
+ }
+ if (p != data + len)
+ {
+ log_print ("asn_decode_sequence: SEQ/SET OF too many elements");
+ return NULL;
+ }
+
+ /*
+ * Create new templates for dynamically added objects,
+ * the ASN.1 tags SEQUENCE OF and SET OF, specify an unknown
+ * number of elements.
+ */
+
+ new_buf = realloc (obj->data,
+ (objects+1) * sizeof (struct norm_type));
+ if (new_buf == NULL)
+ {
+ asn_free (obj);
+ obj->data = NULL;
+ log_print ("asn_decode_sequence: out of memory");
+ return NULL;
+ }
+ obj->data = new_buf;
+
+ tmp = obj->data;
+
+ /* Copy TAG_STOP */
+ memcpy (tmp + objects, &stop_tag, sizeof (struct norm_type));
+ while (objects-- > 1)
+ {
+ memcpy (tmp + objects, tmp, sizeof (struct norm_type));
+ if (asn_template_clone (tmp + objects, 1) == NULL)
+ return NULL;
+ }
+ }
+
+ obj = (struct norm_type *) obj->data;
+
+ p = data;
+ while (p < data + len)
+ {
+ if (obj->type == TAG_STOP)
+ break;
+ h = asn_get (obj->type);
+ if (h == NULL)
+ {
+ log_print ("asn_decode_sequence: unknown ASN.1 tag %d", obj->type);
+ return NULL;
+ }
+
+ if ((p = h->decode (p, (data - p) + len, obj++)) == NULL)
+ break;
+ }
+
+ if (p < data + len)
+ log_print ("asn_decode_sequence: ASN tag was not decoded completely");
+
+ if (p == NULL)
+ return NULL;
+
+ return data + len;
+}
diff --git a/sbin/isakmpd/asn_useful.c b/sbin/isakmpd/asn_useful.c
new file mode 100644
index 00000000000..636024eb56e
--- /dev/null
+++ b/sbin/isakmpd/asn_useful.c
@@ -0,0 +1,128 @@
+/* $OpenBSD: asn_useful.c,v 1.4 1998/11/16 21:07:15 niklas Exp $ */
+
+/*
+ * Copyright (c) 1998 Niels Provos. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ericsson Radio Systems.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+#include <sys/param.h>
+
+#include "asn.h"
+#include "asn_useful.h"
+
+struct norm_type AlgorithmIdentifier[] = {
+ {TAG_OBJECTID, UNIVERSAL, "algorithm", 0, NULL},
+ {TAG_ANY, UNIVERSAL, "parameters", 0, NULL},
+ {TAG_STOP, UNIVERSAL, NULL, 0, NULL}};
+
+struct norm_type Signed[] = {
+ {TAG_RAW, UNIVERSAL, "data", 0, NULL},
+ SEQ("algorithm", AlgorithmIdentifier),
+ {TAG_BITSTRING, UNIVERSAL, "encrypted", 0, NULL},
+ {TAG_STOP, UNIVERSAL, NULL, 0, NULL}};
+
+struct norm_type Validity[] = {
+ {TAG_UTCTIME, UNIVERSAL, "notBefore", 0, NULL},
+ {TAG_UTCTIME, UNIVERSAL, "notAfter", 0, NULL},
+ {TAG_STOP, UNIVERSAL, NULL, 0, NULL}};
+
+struct norm_type AttributeValueAssertion[] = {
+ {TAG_OBJECTID, UNIVERSAL, "AttributeType", 0, NULL},
+ {TAG_ANY, UNIVERSAL, "AttributeValue", 0, NULL},
+ {TAG_STOP, UNIVERSAL, NULL, 0, NULL}};
+
+struct norm_type RelativeDistinguishedName[] = {
+ SEQ ("AttributeValueAssertion", AttributeValueAssertion),
+ {TAG_STOP}};
+
+/*
+ * For decoding this structure is dynamically resized, we add two Names
+ * only for encoding purposes.
+ */
+struct norm_type RDNSequence[] = {
+ SETOF ("RelativeDistinguishedName", RelativeDistinguishedName),
+ SETOF ("RelativeDistinguishedName", RelativeDistinguishedName),
+ {TAG_STOP}};
+
+struct norm_type SubjectPublicKeyInfo[] = {
+ SEQ ("algorithm", AlgorithmIdentifier),
+ {TAG_BITSTRING, UNIVERSAL, "subjectPublicKey", 0, NULL},
+ {TAG_STOP}};
+
+struct norm_type Extension[] = {
+ {TAG_OBJECTID, UNIVERSAL, "extnId", 0, NULL},
+ {TAG_BOOL, UNIVERSAL, "critical", 0, NULL},
+ {TAG_OCTETSTRING, UNIVERSAL, "extnValue", 0, NULL},
+ {TAG_STOP}};
+
+struct norm_type Extensions[] = {
+ SEQ ("extension", Extension),
+ {TAG_STOP}};
+
+struct norm_type Certificate[] = {
+ /* We need to add an explicit tag, HACK XXX */
+ {TAG_INTEGER, ADD_EXP(0, UNIVERSAL), "version", 0, NULL},
+ {TAG_INTEGER, UNIVERSAL, "serialNumber", 0, NULL},
+ SEQ ("signature", AlgorithmIdentifier),
+ SEQOF ("issuer", RDNSequence),
+ SEQ ("validity", Validity),
+ SEQOF ("subject", RDNSequence),
+ SEQ ("subjectPublicKeyInfo", SubjectPublicKeyInfo),
+ {TAG_RAW, UNIVERSAL, "extension", 0, NULL},
+ {TAG_STOP}};
+
+struct norm_type DigestInfo[] = {
+ SEQ ("digestAlgorithm", AlgorithmIdentifier),
+ {TAG_OCTETSTRING, UNIVERSAL, "digest", 0, NULL},
+ {TAG_STOP}};
+
+struct asn_objectid asn_ids[] = {
+ {"AttributeType", ASN_ID_ATTRIBUTE_TYPE},
+ {"CountryName", ASN_ID_COUNTRY_NAME},
+ {"LocalityName", ASN_ID_LOCALITY_NAME},
+ {"StateOrProvinceName", ASN_ID_STATE_NAME},
+ {"OrganizationName", ASN_ID_ORGANIZATION_NAME},
+ {"OrganizationUnitName", ASN_ID_ORGUNIT_NAME},
+ {"CommonUnitName", ASN_ID_COMMONUNIT_NAME},
+ {"pkcs-1", ASN_ID_PKCS},
+ {"rsaEncryption", ASN_ID_RSAENCRYPTION},
+ {"md2WithRSAEncryption", ASN_ID_MD2WITHRSAENC},
+ {"md4WithRSAEncryption", ASN_ID_MD4WITHRSAENC},
+ {"md5WithRSAEncryption", ASN_ID_MD5WITHRSAENC},
+ {"md2", ASN_ID_MD2},
+ {"md4", ASN_ID_MD4},
+ {"md5", ASN_ID_MD5},
+ {"emailAddress", ASN_ID_EMAILADDRESS},
+ {"id-ce", ASN_ID_CE},
+ {"subjectAltName", ASN_ID_SUBJECT_ALT_NAME},
+ {"issuerAltName", ASN_ID_ISSUER_ALT_NAME},
+ {"basicConstraints", ASN_ID_BASIC_CONSTRAINTS},
+ {NULL, NULL} };
diff --git a/sbin/isakmpd/cert.c b/sbin/isakmpd/cert.c
index 5577ac3c01f..8e973d0f180 100644
--- a/sbin/isakmpd/cert.c
+++ b/sbin/isakmpd/cert.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cert.c,v 1.5 1998/11/16 12:30:45 niklas Exp $ */
+/* $OpenBSD: cert.c,v 1.6 1998/11/16 21:07:16 niklas Exp $ */
/*
* Copyright (c) 1998 Niels Provos. All rights reserved.
@@ -42,11 +42,9 @@
#include "x509.h"
struct cert_handler cert_handler[] = {
-#if 0
{ISAKMP_CERTENC_X509_SIG,
x509_certreq_validate, x509_certreq_decode, x509_free_aca,
x509_cert_obtain, x509_cert_get_key, x509_cert_get_subject}
-#endif
};
struct cert_handler *
diff --git a/sbin/isakmpd/ike_auth.c b/sbin/isakmpd/ike_auth.c
index e961c862cc1..07481799a53 100644
--- a/sbin/isakmpd/ike_auth.c
+++ b/sbin/isakmpd/ike_auth.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ike_auth.c,v 1.5 1998/11/15 01:13:27 niklas Exp $ */
+/* $OpenBSD: ike_auth.c,v 1.6 1998/11/16 21:07:16 niklas Exp $ */
/*
* Copyright (c) 1998 Niklas Hallqvist. All rights reserved.
@@ -52,11 +52,14 @@
#include "pkcs.h"
#include "prf.h"
+static u_int8_t *enc_gen_skeyid (struct exchange *, size_t *);
static u_int8_t *pre_shared_gen_skeyid (struct exchange *, size_t *);
static u_int8_t *sig_gen_skeyid (struct exchange *, size_t *);
static int pre_shared_decode_hash (struct message *);
+static int rsa_sig_decode_hash (struct message *);
static int pre_shared_encode_hash (struct message *);
+static int rsa_sig_encode_hash (struct message *);
static int ike_auth_hash (struct exchange *, u_int8_t *);
@@ -65,7 +68,12 @@ static struct ike_auth ike_auth[] = {
pre_shared_encode_hash},
{ IKE_AUTH_DSS, sig_gen_skeyid, pre_shared_decode_hash,
pre_shared_encode_hash},
- /* XXX Here should be hooks to code patented in the US. */
+ { IKE_AUTH_RSA_SIG, sig_gen_skeyid, rsa_sig_decode_hash,
+ rsa_sig_encode_hash},
+ { IKE_AUTH_RSA_ENC, enc_gen_skeyid, pre_shared_decode_hash,
+ pre_shared_encode_hash},
+ { IKE_AUTH_RSA_ENC_REV, enc_gen_skeyid, pre_shared_decode_hash,
+ pre_shared_encode_hash},
};
struct ike_auth *
@@ -150,6 +158,42 @@ sig_gen_skeyid (struct exchange *exchange, size_t *sz)
return skeyid;
}
+/*
+ * Both standard and revised RSA encryption authentication uses this SKEYID
+ * computation.
+ */
+static u_int8_t *
+enc_gen_skeyid (struct exchange *exchange, size_t *sz)
+{
+ struct prf *prf;
+ struct ipsec_exch *ie = exchange->data;
+ struct hash *hash = ie->hash;
+ u_int8_t *skeyid;
+
+ hash->Init (hash->ctx);
+ hash->Update (hash->ctx, exchange->nonce_i, exchange->nonce_i_len);
+ hash->Update (hash->ctx, exchange->nonce_r, exchange->nonce_r_len);
+ hash->Final (hash->digest, hash->ctx);
+ prf = prf_alloc (ie->prf_type, hash->type, hash->digest, *sz);
+ if (!prf)
+ return 0;
+
+ *sz = prf->blocksize;
+ skeyid = malloc (*sz);
+ if (!skeyid)
+ {
+ prf_free (prf);
+ return 0;
+ }
+
+ prf->Init (prf->prfctx);
+ prf->Update (prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN);
+ prf->Final (skeyid, prf->prfctx);
+ prf_free (prf);
+
+ return skeyid;
+}
+
static int
pre_shared_decode_hash (struct message *msg)
{
@@ -187,6 +231,120 @@ pre_shared_decode_hash (struct message *msg)
return 0;
}
+/*
+ * Decrypt the HASH in SIG, we already need a parsed ID payload
+ */
+
+static int
+rsa_sig_decode_hash (struct message *msg)
+{
+ struct cert_handler *handler;
+ struct exchange *exchange = msg->exchange;
+ struct ipsec_exch *ie = exchange->data;
+ struct payload *p;
+ struct rsa_public_key key;
+ size_t hashsize = ie->hash->hashsize;
+ char header[80];
+ int initiator = exchange->initiator;
+ u_int8_t **hash_p, *id_cert, *id;
+ u_int16_t len;
+ u_int32_t id_cert_len;
+ size_t id_len;
+
+ /* Choose the right fields to fill-in. */
+ hash_p = initiator ? &ie->hash_r : &ie->hash_i;
+ id = initiator ? exchange->id_r : exchange->id_i;
+ id_len = initiator ? exchange->id_r_len : exchange->id_i_len;
+
+ if (id == NULL || id_len == 0)
+ {
+ log_print ("rsa_sig_decode_hash: no ID in sa");
+ return -1;
+ }
+
+ /* Just bother with the ID data field. */
+ id += ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;
+ id_len -= ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;
+
+ p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_CERT]);
+ if (!p)
+ return -1;
+
+ if ((handler = cert_get (GET_ISAKMP_CERT_ENCODING(p->p))) == NULL)
+ {
+ log_print ("rsa_sig_decode_hash: no handler for CERT encoding");
+ return -1;
+ }
+
+ /* XXX - this assumes IPv4 here */
+ if (!handler->cert_get_subject (p->p + ISAKMP_CERT_DATA_OFF,
+ GET_ISAKMP_GEN_LENGTH(p->p) -
+ ISAKMP_CERT_DATA_OFF,
+ &id_cert, &id_cert_len))
+ {
+ log_print ("rsa_sig_decode_hash: can not get subject from CERT");
+ return -1;
+ }
+
+ if (id_cert_len != id_len || memcmp (id, id_cert, id_len))
+ {
+ log_print ("rsa_sig_decode_hash: CERT subject does not match ID");
+ free (id_cert);
+ return -1;
+ }
+ free (id_cert);
+
+ if (!handler->cert_get_key (p->p + ISAKMP_CERT_DATA_OFF,
+ GET_ISAKMP_GEN_LENGTH(p->p) -
+ ISAKMP_CERT_DATA_OFF,
+ &key))
+ {
+ log_print ("rsa_sig_decode_hash: decoding payload CERT failed");
+ return -1;
+ }
+
+ p->flags |= PL_MARK;
+
+ p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SIG]);
+ if (!p)
+ {
+ pkcs_free_public_key (&key);
+ return -1;
+ }
+
+ /* Check that the sig is of the correct size. */
+ if (GET_ISAKMP_GEN_LENGTH (p->p) - ISAKMP_SIG_SZ != mpz_sizeinoctets (key.n))
+ {
+ pkcs_free_public_key (&key);
+ log_print ("rsa_sig_decode_hash: SIG payload length does not match "
+ "public key");
+ return -1;
+ }
+
+ if (!pkcs_rsa_decrypt (PKCS_PRIVATE, key.n, key.e,
+ p->p + ISAKMP_SIG_DATA_OFF, hash_p, &len))
+ {
+ pkcs_free_public_key (&key);
+ return -1;
+ }
+
+ pkcs_free_public_key (&key);
+
+ if (len != hashsize)
+ {
+ free (*hash_p);
+ *hash_p = NULL;
+ return -1;
+ }
+
+ snprintf (header, 80, "rsa_sig_decode_hash: HASH_%c", initiator ? 'R' : 'I');
+ log_debug_buf (LOG_MISC, 80, header, *hash_p, hashsize);
+
+ p->flags |= PL_MARK;
+
+ return 0;
+}
+
static int
pre_shared_encode_hash (struct message *msg)
{
@@ -226,6 +384,122 @@ pre_shared_encode_hash (struct message *msg)
return 0;
}
+
+/* Encrypt the HASH into a SIG type */
+
+static int
+rsa_sig_encode_hash (struct message *msg)
+{
+ struct exchange *exchange = msg->exchange;
+ struct ipsec_exch *ie = exchange->data;
+ size_t hashsize = ie->hash->hashsize;
+ struct cert_handler *handler;
+ struct rsa_private_key key;
+ char header[80];
+ int initiator = exchange->initiator;
+ u_int8_t *buf, *asn, *data;
+ u_int32_t asnlen, datalen;
+ char *keyfile;
+
+ /* XXX - this needs to be configureable */
+ handler = cert_get (ISAKMP_CERTENC_X509_SIG);
+ if (handler == NULL)
+ {
+ /* XXX - Log? */
+ return -1;
+ }
+ /* XXX - implicitly uses exchange->id_{i,r} */
+ if (!handler->cert_obtain (exchange, NULL, &data, &datalen))
+ {
+ /* XXX - Log? */
+ return -1;
+ }
+
+ buf = realloc (data, ISAKMP_CERT_SZ + datalen);
+ if (buf == NULL)
+ {
+ /* XXX Log? */
+ free (data);
+ return -1;
+ }
+ memmove (buf + ISAKMP_CERT_SZ, buf, datalen);
+ SET_ISAKMP_CERT_ENCODING (buf, ISAKMP_CERTENC_X509_SIG);
+ if (message_add_payload (msg, ISAKMP_PAYLOAD_CERT, buf,
+ ISAKMP_CERT_SZ + datalen, 1))
+ {
+ /* XXX Log? */
+ free (buf);
+ return -1;
+ }
+
+ /* XXX - do we want to store our files in ASN.1 ? */
+ keyfile = conf_get_str ("rsa_sig", "privkey");
+ if (!asn_get_from_file (keyfile, &asn, &asnlen))
+ {
+ /* XXX Log? */
+ return -1;
+ }
+
+ if (!pkcs_private_key_from_asn (&key, asn, asnlen))
+ {
+ /* XXX Log? */
+ free (asn);
+ return -1;
+ }
+ free (asn);
+
+ /* XXX - hashsize is not necessarily prf->blocksize */
+ buf = malloc (hashsize);
+ if (!buf)
+ {
+ /* XXX Log? */
+ pkcs_free_private_key (&key);
+ return -1;
+ }
+
+ if (ike_auth_hash (exchange, buf) == -1)
+ {
+ /* XXX Log? */
+ free (buf);
+ pkcs_free_private_key (&key);
+ return -1;
+ }
+
+ snprintf (header, 80, "rsa_sig_encode_hash: HASH_%c", initiator ? 'I' : 'R');
+ log_debug_buf (LOG_MISC, 80, header, buf, hashsize);
+
+ if (!pkcs_rsa_encrypt (PKCS_PRIVATE, key.n, key.e, buf, hashsize,
+ &data, &datalen))
+ {
+ free (buf);
+ pkcs_free_private_key (&key);
+ return -1;
+ }
+ pkcs_free_private_key (&key);
+ free (buf);
+
+ buf = realloc (data, ISAKMP_SIG_SZ + datalen);
+ if (!buf)
+ {
+ /* XXX Log? */
+ free (data);
+ return -1;
+ }
+ memmove (buf + ISAKMP_SIG_SZ, buf, datalen);
+
+ snprintf (header, 80, "rsa_sig_encode_hash: SIG_%c", initiator ? 'I' : 'R');
+ log_debug_buf (LOG_MISC, 80, header, buf + ISAKMP_SIG_DATA_OFF, datalen);
+ if (message_add_payload (msg, ISAKMP_PAYLOAD_SIG, buf,
+ ISAKMP_SIG_SZ + datalen, 1))
+ {
+ /* XXX Log? */
+ free (buf);
+ return -1;
+ }
+
+ return 0;
+}
+
int
ike_auth_hash (struct exchange *exchange, u_int8_t *buf)
{
diff --git a/sbin/isakmpd/pkcs.c b/sbin/isakmpd/pkcs.c
new file mode 100644
index 00000000000..80f4aaf9349
--- /dev/null
+++ b/sbin/isakmpd/pkcs.c
@@ -0,0 +1,370 @@
+/* $OpenBSD: pkcs.c,v 1.4 1998/11/16 21:07:17 niklas Exp $ */
+
+/*
+ * Copyright (c) 1998 Niels Provos. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ericsson Radio Systems.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <gmp.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gmp_util.h"
+#include "log.h"
+#include "sysdep.h"
+#include "asn.h"
+#include "asn_useful.h"
+#include "pkcs.h"
+
+struct norm_type RSAPublicKey[] = {
+ { TAG_INTEGER, UNIVERSAL, "modulus", 0, NULL}, /* modulus */
+ { TAG_INTEGER, UNIVERSAL, "publicExponent", 0, NULL}, /* public exponent */
+ { TAG_STOP, 0, NULL, 0, NULL}
+};
+
+struct norm_type RSAPrivateKey[] = {
+ { TAG_INTEGER, UNIVERSAL, "version", 1, "\0"}, /* version */
+ { TAG_INTEGER, UNIVERSAL, "modulus", 0, NULL}, /* modulus */
+ { TAG_INTEGER, UNIVERSAL, "publicExponent", 0, NULL}, /* public exponent */
+ { TAG_INTEGER, UNIVERSAL, "privateExponent", 0, NULL},/* private exponent */
+ { TAG_INTEGER, UNIVERSAL, "prime1", 0, NULL}, /* p */
+ { TAG_INTEGER, UNIVERSAL, "prime2", 0, NULL}, /* q */
+ { TAG_INTEGER, UNIVERSAL, "exponent1", 0, NULL}, /* d mod (p-1) */
+ { TAG_INTEGER, UNIVERSAL, "exponent2", 0, NULL}, /* d mod (q-1) */
+ { TAG_INTEGER, UNIVERSAL, "coefficient", 0, NULL}, /* inv. of q mod p */
+ { TAG_STOP, 0, NULL, 0, NULL}
+};
+
+/*
+ * Fill in the data field in struct norm_type with the octet data
+ * from n.
+ */
+
+int
+pkcs_mpz_to_norm_type (struct norm_type *obj, mpz_ptr n)
+{
+ obj->len = sizeof (mpz_ptr);
+ if ((obj->data = malloc (obj->len)) == NULL)
+ return 0;
+
+ mpz_init_set ((mpz_ptr) obj->data, n);
+
+ return 1;
+}
+
+/*
+ * Given the modulus and the public key, return an BER ASN.1 encoded
+ * PKCS#1 compliant RSAPublicKey object.
+ */
+
+u_int8_t *
+pkcs_public_key_to_asn (struct rsa_public_key *pub)
+{
+ u_int8_t *erg;
+ struct norm_type *key, seq = {TAG_SEQUENCE, UNIVERSAL, NULL, 0, NULL};
+
+ seq.data = &RSAPublicKey;
+ asn_template_clone (&seq, 1);
+ key = seq.data;
+ if (key == NULL)
+ return NULL;
+
+ if (!pkcs_mpz_to_norm_type (&key[0], pub->n))
+ {
+ free (key);
+ return NULL;
+ }
+
+ if (!pkcs_mpz_to_norm_type (&key[1], pub->e))
+ {
+ free (key[0].data);
+ free (key);
+ return NULL;
+ }
+
+ erg = asn_encode_sequence (&seq, NULL);
+
+ asn_free (&seq);
+
+ return erg;
+}
+
+/*
+ * Initalizes and Set's a Public Key Structure from an ASN BER encoded
+ * Public Key.
+ */
+
+int
+pkcs_public_key_from_asn (struct rsa_public_key *pub, u_int8_t *asn,
+ u_int32_t len)
+{
+ struct norm_type *key, seq = {TAG_SEQUENCE, UNIVERSAL, NULL, 0, NULL};
+
+ mpz_init (pub->n);
+ mpz_init (pub->e);
+
+ seq.data = RSAPublicKey;
+ asn_template_clone (&seq, 1);
+
+ if (seq.data == NULL)
+ return 0;
+
+ if (asn_decode_sequence (asn, len, &seq) == NULL)
+ {
+ asn_free (&seq);
+ return 0;
+ }
+
+ key = seq.data;
+ mpz_set (pub->n, (mpz_ptr) key[0].data);
+ mpz_set (pub->e, (mpz_ptr) key[1].data);
+
+ asn_free (&seq);
+
+ return 1;
+}
+
+void
+pkcs_free_public_key (struct rsa_public_key *pub)
+{
+ mpz_clear (pub->n);
+ mpz_clear (pub->e);
+}
+
+/*
+ * Get ASN.1 representation of PrivateKey.
+ * XXX - not sure if we need this.
+ */
+
+u_int8_t *
+pkcs_private_key_to_asn (struct rsa_private_key *priv)
+{
+ mpz_t d1, d2, qinv;
+ struct norm_type *key, seq = {TAG_SEQUENCE, UNIVERSAL, NULL, 0, NULL};
+ u_int8_t *erg = NULL;
+
+ seq.data = RSAPrivateKey;
+ asn_template_clone (&seq, 1);
+ key = seq.data;
+ if (key == NULL)
+ return NULL;
+
+ mpz_init (d1);
+ mpz_sub_ui (d1, priv->p, 1);
+ mpz_mod (d1, priv->d, d1);
+
+ mpz_init (d2);
+ mpz_sub_ui (d2, priv->q, 1);
+ mpz_mod (d2, priv->d, d2);
+
+ mpz_init (qinv);
+ mpz_invert (qinv, priv->q, priv->p);
+
+ if (!pkcs_mpz_to_norm_type (&key[1], priv->n))
+ goto done;
+
+ if (!pkcs_mpz_to_norm_type (&key[2], priv->e))
+ goto done;
+
+ if (!pkcs_mpz_to_norm_type (&key[3], priv->d))
+ goto done;
+
+ if (!pkcs_mpz_to_norm_type (&key[4], priv->p))
+ goto done;
+
+ if (!pkcs_mpz_to_norm_type (&key[5], priv->q))
+ goto done;
+
+ if (!pkcs_mpz_to_norm_type (&key[6], d1))
+ goto done;
+
+ if (!pkcs_mpz_to_norm_type (&key[7], d2))
+ goto done;
+
+ if (!pkcs_mpz_to_norm_type (&key[8], qinv))
+ goto done;
+
+ mpz_set_ui (d1, 0);
+
+ if (!pkcs_mpz_to_norm_type (&key[0], d1))
+ goto done;
+
+ erg = asn_encode_sequence (&seq, NULL);
+
+ done:
+ asn_free (&seq);
+
+ mpz_clear (d1);
+ mpz_clear (d2);
+ mpz_clear (qinv);
+
+ return erg;
+}
+
+/*
+ * Initalizes and Set's a Private Key Structure from an ASN BER encoded
+ * Private Key.
+ */
+
+int
+pkcs_private_key_from_asn (struct rsa_private_key *priv, u_int8_t *asn,
+ u_int32_t len)
+{
+ struct norm_type *key, seq = {TAG_SEQUENCE, UNIVERSAL, NULL, 0, NULL};
+ u_int8_t *erg;
+
+ mpz_init (priv->n);
+ mpz_init (priv->p);
+ mpz_init (priv->q);
+ mpz_init (priv->e);
+ mpz_init (priv->d);
+
+ seq.data = RSAPrivateKey;
+ asn_template_clone (&seq, 1);
+ if (seq.data == NULL)
+ return 0;
+
+ if (!(erg = asn_decode_sequence (asn, len, &seq)))
+ goto done;
+
+ key = seq.data;
+ if (mpz_cmp_ui ((mpz_ptr) key[0].data, 0))
+ {
+ log_print ("pkcs_set_private_key: version too high");
+ erg = 0;
+ goto done;
+ }
+
+ mpz_set (priv->n, key[1].data);
+ mpz_set (priv->e, key[2].data);
+ mpz_set (priv->d, key[3].data);
+ mpz_set (priv->p, key[4].data);
+ mpz_set (priv->q, key[5].data);
+
+ done:
+ asn_free (&seq);
+
+ return erg == NULL ? 0 : 1;
+}
+
+void
+pkcs_free_private_key (struct rsa_private_key *priv)
+{
+ mpz_clear (priv->n);
+ mpz_clear (priv->e);
+ mpz_clear (priv->d);
+ mpz_clear (priv->p);
+ mpz_clear (priv->q);
+}
+
+/*
+ * Creates a PKCS#1 block with data and then uses the private
+ * exponent to do RSA encryption, returned is an allocated buffer
+ * with the encryption result.
+ *
+ * XXX CRIPPLED in the OpenBSD version as RSA is patented in the US.
+ */
+int
+pkcs_rsa_encrypt (int art, mpz_ptr n, mpz_ptr e, u_int8_t *data, u_int32_t len,
+ u_int8_t **out, u_int32_t *outlen)
+{
+ /* XXX Always fail until we interface legal (in the US) RSA code. */
+ return 0;
+}
+
+/*
+ * Private Key Decryption, the 'in'-buffer is being destroyed
+ *
+ * XXX CRIPPLED in the OpenBSD version as RSA is patented in the US.
+ */
+int
+pkcs_rsa_decrypt (int art, mpz_ptr n, mpz_ptr d, u_int8_t *in,
+ u_int8_t **out, u_int16_t *outlen)
+{
+ /* XXX Always fail until we interface legal (in the US) RSA code. */
+ return 0;
+}
+
+/*
+ * Generates a keypair suitable to be used for RSA. No checks are done
+ * on the generated key material. The following criteria might be
+ * enforced: p and q chosen randomly, |p-q| should be large, (p+1), (q+1),
+ * (p-1), (q-1) should have a large prime factor to be resistant e.g.
+ * against Pollard p-1 and Pollard p+1 factoring algorithms.
+ * For p-1 and q-1 the large prime factor itself - 1 should have a large
+ * prime factor.
+ *
+ * XXX CRIPPLED in the OpenBSD version as RSA is patented in the US.
+ */
+int
+pkcs_generate_rsa_keypair (struct rsa_public_key *pubk,
+ struct rsa_private_key *seck, u_int32_t bits)
+{
+ /* XXX Always fail until we interface legal (in the US) RSA code. */
+ return 0;
+}
+
+/* Generate a random prime with at most bits significant bits */
+
+int
+pkcs_generate_prime (mpz_ptr p, u_int32_t bits)
+{
+ u_int32_t tmp, i;
+
+ mpz_set_ui (p, 0);
+ i = tmp = 0;
+ while (bits > 0)
+ {
+ tmp = sysdep_random();
+
+ if (i++ == 0)
+ {
+ if (bits & 0x1f)
+ tmp &= (1 << (bits & 0x1f)) - 1;
+ tmp |= 1 << ((bits - 1) & 0x1f);
+ }
+
+ mpz_mul_2exp (p, p, 32);
+ mpz_add_ui (p, p, tmp);
+
+ bits -= (bits & 0x1f ? bits & 0x1f : 32);
+ }
+
+ /* Make p odd */
+ mpz_setbit (p, 0);
+
+ /* Iterate as long as p is not a probable prime */
+ while (!mpz_probab_prime_p (p, 50))
+ mpz_add_ui (p, p, 2);
+
+ return 1;
+}
diff --git a/sbin/isakmpd/x509.c b/sbin/isakmpd/x509.c
new file mode 100644
index 00000000000..bc6ccc7c2ca
--- /dev/null
+++ b/sbin/isakmpd/x509.c
@@ -0,0 +1,844 @@
+/* $OpenBSD: x509.c,v 1.4 1998/11/16 21:07:18 niklas Exp $ */
+
+/*
+ * Copyright (c) 1998 Niels Provos. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ericsson Radio Systems.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <gmp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "conf.h"
+#include "exchange.h"
+#include "hash.h"
+#include "ike_auth.h"
+#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 */
+
+/* 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;
+
+ if (asn_template_clone (&name, 1) == NULL ||
+ (asn = asn_decode_sequence (asn, len, &name)) == NULL)
+ {
+ log_print ("x509_certreq_validate: can not decode 'acceptable CA' info");
+ res = 0;
+ }
+ asn_free (&name);
+
+ 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)
+{
+ struct norm_type aca = SEQOF ("aca", RDNSequence);
+ struct norm_type *tmp;
+ struct x509_aca naca, *ret;
+
+ if (asn_template_clone (&aca, 1) == NULL ||
+ (asn = asn_decode_sequence (asn, len, &aca)) == NULL)
+ {
+ log_print ("x509_certreq_validate: can not decode 'acceptable CA' info");
+ goto fail;
+ }
+ memset (&naca, 0, sizeof (naca));
+
+ tmp = asn_decompose ("aca.RelativeDistinguishedName.AttributeValueAssertion", &aca);
+ if (tmp == NULL)
+ goto fail;
+ x509_get_attribval (tmp, &naca.name1);
+
+ tmp = asn_decompose ("aca.RelativeDistinguishedName[1].AttributeValueAssertion", &aca);
+ if (tmp != NULL)
+ x509_get_attribval (tmp, &naca.name2);
+
+ asn_free (&aca);
+
+ if ((ret = malloc (sizeof (struct x509_aca))) != NULL)
+ memcpy (ret, &naca, sizeof (struct x509_aca));
+ else
+ x509_free_aca (&aca);
+
+ return ret;
+
+ fail:
+ asn_free (&aca);
+ return NULL;
+}
+
+void
+x509_free_aca (void *blob)
+{
+ struct x509_aca *aca = blob;
+
+ if (aca->name1.type != NULL)
+ free (aca->name1.type);
+ if (aca->name1.val != NULL)
+ free (aca->name1.val);
+
+ if (aca->name2.type != NULL)
+ free (aca->name2.type);
+ if (aca->name2.val != NULL)
+ free (aca->name2.val);
+}
+
+/*
+ * 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)
+{
+ struct x509_aca *aca = data;
+ struct ipsec_exch *ie = exchange->data;
+ char *certfile;
+ int fd, res = 0;
+ struct stat st;
+
+ if (aca != NULL)
+ 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);
+
+ /* XXX - this needs to be changed - but how else shoud I know */
+ switch (ie->ike_auth->id)
+ {
+ case IKE_AUTH_RSA_SIG:
+ if ((certfile = conf_get_str ("rsa_sig", "cert")) == NULL)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+
+ if (stat (certfile, &st) == -1)
+ {
+ log_error ("x509_cert_obtain: failed to state %s", certfile);
+ return 0;
+ }
+
+ *certlen = st.st_size;
+
+ if ((fd = open (certfile, O_RDONLY)) == -1)
+ {
+ log_error ("x509_cert_obtain: failed to open %s", certfile);
+ return 0;
+ }
+
+ if ((*cert = malloc (st.st_size)) == NULL)
+ {
+ log_print ("x509_cert_obtain: out of memory");
+ res = 0;
+ goto done;
+ }
+
+ if (read (fd, *cert, st.st_size) != st.st_size)
+ {
+ log_print ("x509_cert_obtain: cert file ended early");
+ free (*cert);
+ res = 0;
+ goto done;
+ }
+
+ {
+ /*
+ * XXX - assumes IPv4 here, assumes 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
+ */
+ u_int8_t *id_cert, *asn, *id;
+ size_t id_len;
+ u_int32_t id_cert_len;
+
+ /* XXX - assumes IPv4 */
+ id = exchange->initiator ? exchange->id_i : exchange->id_r;
+ id_len = exchange->initiator ? exchange->id_i_len : exchange->id_r_len;
+
+ /* XXX - we need our ID to set that in the cert */
+ if (id != NULL)
+ {
+ /* XXX - get to address ? */
+ id += 4; id_len -= 4;
+
+ /* Get offset into data structure where the IP is saved */
+ asn = *cert;
+ id_cert_len = asn_get_data_len (NULL, &asn, &id_cert);
+ asn = id_cert;
+ id_cert_len = asn_get_data_len (NULL, &asn, &id_cert);
+ id_cert += id_cert_len - 4;
+ memcpy (id_cert, id, 4);
+ }
+ }
+
+ res = 1;
+
+ done:
+ close (fd);
+
+ return res;
+}
+
+/* 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;
+
+ if (!x509_decode_certificate (asn, asnlen, &cert))
+ return 0;
+
+ /* XXX - perhaps put into pkcs ? */
+ mpz_init_set (key->n, cert.key.n);
+ mpz_init_set (key->e, cert.key.e);
+
+ x509_free_certificate (&cert);
+
+ return 1;
+}
+
+/*
+ * Retrieve the public key from a X509 Certificate
+ * XXX - look at XXX below.
+ */
+
+int
+x509_cert_get_subject (u_int8_t *asn, u_int32_t asnlen,
+ u_int8_t **subject, u_int32_t *subjectlen)
+{
+ struct x509_certificate cert;
+
+ if (!x509_decode_certificate (asn, asnlen, &cert))
+ return 0;
+
+ if (cert.extension.type == NULL || cert.extension.val == NULL)
+ goto fail;
+
+ log_debug (LOG_CRYPTO, 60, "x509_cert_get_subject: Extension Type %s = %s",
+ cert.extension.type,
+ asn_parse_objectid (asn_ids, cert.extension.type));
+
+ if (strcmp (ASN_ID_SUBJECT_ALT_NAME, cert.extension.type))
+ {
+ log_print ("x509_cert_get_subject: extension type != subjectAltName");
+ goto fail;
+ }
+
+ /*
+ * 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)
+ {
+ log_print ("x509_cert_get_subject: subjectAltName uses "
+ "unhandled encoding");
+ goto fail;
+ }
+
+ /* XXX - 4 bytes for IPV4 address */
+ *subject = malloc (4);
+ if (*subject == NULL)
+ {
+ log_print ("x509_cert_get_subject: out of memory");
+ goto fail;
+ }
+ *subjectlen = 4;
+ memcpy (*subject, cert.extension.val + 4, *subjectlen);
+
+ x509_free_certificate (&cert);
+
+ return 1;
+
+ fail:
+ x509_free_certificate (&cert);
+ return 0;
+}
+
+/* Initalizes the struct x509_attribval from a AtributeValueAssertion */
+
+void
+x509_get_attribval (struct norm_type *obj, struct x509_attribval *a)
+{
+ struct norm_type *tmp;
+
+ tmp = asn_decompose ("AttributeValueAssertion.AttributeType", obj);
+ if (tmp != NULL && tmp->data != NULL)
+ a->type = strdup ((char *)tmp->data);
+
+ tmp = asn_decompose ("AttributeValueAssertion.AttributeValue", obj);
+ if (tmp != NULL && tmp->data != NULL)
+ a->val = strdup ((char *)tmp->data);
+}
+
+/* Set's norm_type with values from x509_attribval */
+
+void
+x509_set_attribval (struct norm_type *obj, struct x509_attribval *a)
+{
+ struct norm_type *tmp;
+
+ 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);
+}
+
+void
+x509_free_attribval (struct x509_attribval *a)
+{
+ if (a->type != NULL)
+ free (a->type);
+ if (a->val != NULL)
+ free (a->val);
+}
+
+void
+x509_free_certificate (struct x509_certificate *cert)
+{
+ pkcs_free_public_key (&cert->key);
+ if (cert->signaturetype != NULL)
+ free (cert->signaturetype);
+ if (cert->start != NULL)
+ free (cert->start);
+ if (cert->end != NULL)
+ 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);
+}
+
+int
+x509_decode_certificate (u_int8_t *asn, u_int32_t asnlen,
+ struct x509_certificate *rcert)
+{
+ struct norm_type cert = SEQ ("cert", Certificate);
+ struct norm_type *tmp;
+ u_int8_t *data;
+ u_int32_t datalen;
+
+ /* Get access to the inner Certificate */
+ if (!x509_validate_signed (asn, asnlen, NULL, &data, &datalen))
+ return 0;
+
+ memset (rcert, 0, sizeof (*rcert));
+
+ if (asn_template_clone (&cert, 1) == NULL ||
+ asn_decode_sequence (data, datalen, &cert) == NULL)
+ goto fail;
+
+ tmp = asn_decompose ("cert.subjectPublicKeyInfo.subjectPublicKey", &cert);
+ if (!pkcs_public_key_from_asn (&rcert->key, tmp->data + 1, tmp->len - 1))
+ goto fail;
+
+ tmp = asn_decompose ("cert.version", &cert);
+ rcert->version = mpz_get_ui (tmp->data);
+ tmp = asn_decompose ("cert.serialNumber", &cert);
+ rcert->serialnumber = mpz_get_ui (tmp->data);
+ tmp = asn_decompose ("cert.signature.algorithm", &cert);
+ rcert->signaturetype = strdup ((char *)tmp->data);
+
+ tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName."
+ "AttributeValueAssertion", &cert);
+ x509_get_attribval (tmp, &rcert->issuer1);
+ tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName[1]."
+ "AttributeValueAssertion", &cert);
+ if (tmp != NULL)
+ x509_get_attribval (tmp, &rcert->issuer2);
+ else
+ rcert->issuer2.type = NULL;
+
+ tmp = asn_decompose ("cert.subject.RelativeDistinguishedName."
+ "AttributeValueAssertion", &cert);
+ x509_get_attribval (tmp, &rcert->subject1);
+ tmp = asn_decompose ("cert.subject.RelativeDistinguishedName[1]."
+ "AttributeValueAssertion", &cert);
+ if (tmp != NULL)
+ x509_get_attribval (tmp, &rcert->subject2);
+ else
+ rcert->subject2.type = NULL;
+
+ tmp = asn_decompose ("cert.validity.notBefore", &cert);
+ rcert->start = strdup ((char *)tmp->data);
+ tmp = asn_decompose ("cert.validity.notAfter", &cert);
+ rcert->end = strdup ((char *)tmp->data);
+
+ /* 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;
+
+ fail:
+ x509_free_certificate (rcert);
+ asn_free (&cert);
+ return 0;
+}
+
+int
+x509_encode_certificate (struct x509_certificate *rcert,
+ u_int8_t **asn, u_int32_t *asnlen)
+{
+ struct norm_type cert = SEQ ("cert", Certificate);
+ struct norm_type *tmp;
+ u_int8_t *data, *new_buf;
+ mpz_t num;
+
+ if (asn_template_clone (&cert, 1) == NULL)
+ goto fail;
+
+ if (rcert->extension.type != NULL && rcert->extension.val != NULL)
+ {
+ u_int8_t *asn;
+ u_int32_t asnlen;
+
+ tmp = asn_decompose ("cert.extension", &cert);
+ if (x509_encode_cert_extension (rcert, &asn, &asnlen))
+ {
+ tmp->data = asn;
+ tmp->len = asnlen;
+ }
+ }
+
+ 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);
+
+ tmp = asn_decompose ("cert.subjectPublicKeyInfo.subjectPublicKey", &cert);
+ data = pkcs_public_key_to_asn (&rcert->key);
+ if (data == NULL)
+ goto fail;
+
+ /* This is a BIT STRING add 0 octet for padding */
+ tmp->len = asn_get_len (data);
+ new_buf = realloc (data, tmp->len + 1);
+ if (new_buf == NULL)
+ {
+ free (data);
+ goto fail;
+ }
+ 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))
+ {
+ mpz_clear (num);
+ goto fail;
+ }
+
+ tmp = asn_decompose ("cert.serialNumber", &cert);
+ mpz_set_ui (num, rcert->serialnumber);
+ if (!pkcs_mpz_to_norm_type (tmp, num))
+ {
+ mpz_clear (num);
+ goto fail;
+ }
+ 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, NULL);
+ if (*asn == NULL)
+ goto fail;
+
+ *asnlen = asn_get_len (*asn);
+
+ asn_free (&cert);
+ return 1;
+
+ fail:
+ asn_free (&cert);
+ return 0;
+}
+
+/*
+ * Decode an Extension to a X509 certificate.
+ * XXX - We ignore the critical boolean
+ */
+
+int
+x509_decode_cert_extension (u_int8_t *asn, u_int32_t asnlen,
+ struct x509_certificate *cert)
+{
+ struct norm_type *tmp;
+ struct norm_type ex = SEQOF ("ex", Extensions);
+
+ /* Implicit tagging for extension */
+ ex.class = ADD_EXP (3, UNIVERSAL);
+
+ if (asn_template_clone (&ex, 1) == NULL ||
+ asn_decode_sequence (asn, asnlen, &ex) == NULL)
+ {
+ asn_free (&ex);
+ return 0;
+ }
+
+ tmp = asn_decompose ("ex.extension.extnValue", &ex);
+ if (!tmp || tmp->data == NULL || 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);
+
+ tmp = asn_decompose ("ex.extension.extnId", &ex);
+ if (!tmp || tmp->data == NULL)
+ goto fail;
+ cert->extension.type = strdup (tmp->data);
+ if (cert->extension.type == NULL)
+ {
+ free (cert->extension.val);
+ cert->extension.val = NULL;
+ goto fail;
+ }
+
+ asn_free (&ex);
+ return 1;
+
+ fail:
+ asn_free (&ex);
+ return 0;
+}
+
+/*
+ * Encode a Cert Extension - XXX - only one extension per certificate
+ * XXX - We tag everything as critical
+ */
+
+int
+x509_encode_cert_extension (struct x509_certificate *cert,
+ u_int8_t **asn, u_int32_t *asnlen)
+{
+ struct norm_type ex = SEQ ("ex", Extensions);
+ struct norm_type *tmp;
+ ex.class = ADD_EXP (3, UNIVERSAL);
+
+ if (asn_template_clone (&ex ,1) == NULL)
+ goto fail;
+
+ tmp = asn_decompose ("ex.extension.extnId", &ex);
+ tmp->data = strdup (cert->extension.type);
+ tmp->len = strlen (tmp->data);
+
+ /* XXX - we mark every extension as critical */
+ tmp = asn_decompose ("ex.extension.critical", &ex);
+ if ((tmp->data = malloc (1)) == NULL)
+ goto fail;
+ *(u_int8_t *)tmp->data = 0xFF;
+ tmp->len = 1;
+
+ tmp = asn_decompose ("ex.extension.extnValue", &ex);
+ if ((tmp->data = malloc (asn_get_len (cert->extension.val))) == NULL)
+ goto fail;
+ tmp->len = asn_get_len (cert->extension.val);
+ memcpy (tmp->data, cert->extension.val, tmp->len);
+
+ *asn = asn_encode_sequence (&ex, NULL);
+ if (*asn == NULL)
+ goto fail;
+
+ *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.
+ */
+
+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)
+{
+ struct norm_type sig = SEQ("signed", Signed);
+ struct norm_type digest = SEQ("digest", DigestInfo);
+ struct norm_type *tmp;
+ struct hash *hash = NULL;
+ int res;
+ u_int8_t *dec;
+ u_int16_t declen;
+
+ if (asn_template_clone (&sig, 1) == NULL)
+ /* Failed, probably memory allocation, free what we got anyway */
+ goto fail;
+
+ if (asn_decode_sequence (asn, asnlen, &sig) == NULL)
+ {
+ log_print ("x509_validate_signed: input data could not be decoded");
+ goto fail;
+ }
+
+ tmp = asn_decompose ("signed.algorithm.algorithm", &sig);
+
+ if (!strcmp ((char *)tmp->data, ASN_ID_MD5WITHRSAENC))
+ {
+ hash = hash_get (HASH_MD5);
+ }
+ else
+ {
+ char *id = asn_parse_objectid (asn_ids, tmp->data);
+ log_print ("x509_validate_signed: can not handle SigType %s",
+ id == NULL ? tmp->data : id);
+ goto fail;
+ }
+
+ if (hash == NULL)
+ 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;
+
+ /* Used to unwrap the SIGNED object around the Certificate */
+ if (key == NULL)
+ {
+ asn_free (&sig);
+ return 1;
+ }
+
+ tmp = asn_decompose ("signed.encrypted", &sig);
+ /*
+ * tmp->data is a BIT STRING, the first octet in the BIT STRING 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->n, key->e, tmp->data + 1,
+ &dec, &declen))
+ goto fail;
+
+ if (asn_template_clone (&digest, 1) == NULL ||
+ asn_decode_sequence (dec, declen, &digest) == NULL)
+ {
+ 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
+ {
+ 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;
+ }
+
+ asn_free (&digest);
+ asn_free (&sig);
+ return res;
+
+ fail:
+ asn_free (&sig);
+ return 0;
+}
+
+/*
+ * 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)
+{
+ 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);
+
+ if (asn_template_clone (&digest, 1) == NULL)
+ goto fail;
+
+ tmp = asn_decompose ("digest.digest", &digest);
+ tmp->len = hash->hashsize;
+ tmp->data = malloc (hash->hashsize);
+ if (tmp->data == NULL)
+ goto fail;
+ 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);
+
+ /* ASN encode Digest Information */
+ if ((diginfo = asn_encode_sequence (&digest, NULL)) == NULL)
+ goto fail;
+
+ /* Encrypt the Digest Info with Private Key */
+ res = pkcs_rsa_encrypt (PKCS_PRIVATE, key->n, key->d, diginfo,
+ asn_get_len (diginfo), &enc, &enclen);
+ free (diginfo);
+ if (!res)
+ goto fail;
+ res = 0;
+
+ if (asn_template_clone (&sig, 1) == NULL)
+ 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 BITSTING, i.e. first octet need to be zero */
+ tmp = asn_decompose ("signed.encrypted", &sig);
+ tmp->data = malloc (enclen + 1);
+ if (tmp->data == NULL)
+ {
+ 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, NULL);
+ if (*asn == NULL)
+ 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 = NULL;
+ res = 1; /* Successfull */
+ fail2:
+ asn_free (&sig);
+ fail:
+ asn_free (&digest);
+ return res;
+}