diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1998-11-16 21:07:19 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1998-11-16 21:07:19 +0000 |
commit | d5283902fbf43a518a7037d5bb3bd4adc0e8fd90 (patch) | |
tree | 4cfdea7ed80757449a80454f73bec1194af7ce5e /sbin/isakmpd | |
parent | 8d3cbec8eba3b6d6bfa5ea62b1b99d18aa0173c2 (diff) |
Reinstate X509 signature code except for RSA code
Diffstat (limited to 'sbin/isakmpd')
-rw-r--r-- | sbin/isakmpd/Makefile | 8 | ||||
-rw-r--r-- | sbin/isakmpd/asn.c | 1155 | ||||
-rw-r--r-- | sbin/isakmpd/asn_useful.c | 128 | ||||
-rw-r--r-- | sbin/isakmpd/cert.c | 4 | ||||
-rw-r--r-- | sbin/isakmpd/ike_auth.c | 278 | ||||
-rw-r--r-- | sbin/isakmpd/pkcs.c | 370 | ||||
-rw-r--r-- | sbin/isakmpd/x509.c | 844 |
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; +} |