diff options
-rw-r--r-- | sbin/isakmpd/DESIGN-NOTES | 16 | ||||
-rw-r--r-- | sbin/isakmpd/Makefile | 11 | ||||
-rw-r--r-- | sbin/isakmpd/README.PKI | 59 | ||||
-rw-r--r-- | sbin/isakmpd/cert.c | 28 | ||||
-rw-r--r-- | sbin/isakmpd/cert.h | 28 | ||||
-rw-r--r-- | sbin/isakmpd/exchange.c | 20 | ||||
-rw-r--r-- | sbin/isakmpd/exchange.h | 12 | ||||
-rw-r--r-- | sbin/isakmpd/ike_auth.c | 313 | ||||
-rw-r--r-- | sbin/isakmpd/ike_phase_1.c | 6 | ||||
-rw-r--r-- | sbin/isakmpd/init.c | 8 | ||||
-rw-r--r-- | sbin/isakmpd/isakmpd.conf.5 | 27 | ||||
-rw-r--r-- | sbin/isakmpd/regress/Makefile | 8 | ||||
-rw-r--r-- | sbin/isakmpd/regress/rsakeygen/Makefile | 41 | ||||
-rw-r--r-- | sbin/isakmpd/regress/rsakeygen/rsakeygen.c | 68 | ||||
-rw-r--r-- | sbin/isakmpd/regress/x509/Makefile | 44 | ||||
-rw-r--r-- | sbin/isakmpd/regress/x509/x509test.c | 191 | ||||
-rw-r--r-- | sbin/isakmpd/samples/VPN-east.conf | 14 | ||||
-rw-r--r-- | sbin/isakmpd/samples/VPN-west.conf | 14 | ||||
-rw-r--r-- | sbin/isakmpd/samples/singlehost-east.conf | 41 | ||||
-rw-r--r-- | sbin/isakmpd/samples/singlehost-west.conf | 43 | ||||
-rw-r--r-- | sbin/isakmpd/sysdep/openbsd/Makefile.sysdep | 10 | ||||
-rw-r--r-- | sbin/isakmpd/x509.c | 1155 | ||||
-rw-r--r-- | sbin/isakmpd/x509.h | 64 |
23 files changed, 1262 insertions, 959 deletions
diff --git a/sbin/isakmpd/DESIGN-NOTES b/sbin/isakmpd/DESIGN-NOTES index c4bb8934062..5937064de16 100644 --- a/sbin/isakmpd/DESIGN-NOTES +++ b/sbin/isakmpd/DESIGN-NOTES @@ -1,5 +1,5 @@ -$OpenBSD: DESIGN-NOTES,v 1.12 1999/07/07 22:14:56 niklas Exp $ -$EOM: DESIGN-NOTES,v 1.45 1999/06/15 11:33:37 niklas Exp $ +$OpenBSD: DESIGN-NOTES,v 1.13 1999/07/17 21:54:39 niklas Exp $ +$EOM: DESIGN-NOTES,v 1.46 1999/07/17 20:44:07 niklas Exp $ General coding conventions -------------------------- @@ -334,6 +334,18 @@ In quick mode the identities are implicitly the IP addresses of the peers, which must mean the IP addresses actually used for the ISAKMP tunnel. Otherwise we today support IPV4_ADDR & IPV4_ADDR_SUBNET as ID types. +X509-Certificates +----------------- +To use RSA Signature mode you are required to generate certificates. +This can be done with ssleay, see man ssl. But the X509 certificates +require a subjectAltname extension that can either be an IPV4 address, +a User-FQDN or just FQDN. ssleay can not create those extension, +insead use the x509test program in regress/x509 to modify an existing +certificate. It will insert the subjectAltname extension and sign it +with the provided private Key. The resulting certificate then needs +to be stored in the directory pointed to by "Certs-directory" in +section "X509-certificates". + License to use -------------- /* diff --git a/sbin/isakmpd/Makefile b/sbin/isakmpd/Makefile index e14fef3bfca..caefedb7810 100644 --- a/sbin/isakmpd/Makefile +++ b/sbin/isakmpd/Makefile @@ -1,5 +1,5 @@ -# $OpenBSD: Makefile,v 1.18 1999/07/07 22:07:33 niklas Exp $ -# $EOM: Makefile,v 1.54 1999/06/07 00:00:49 ho Exp $ +# $OpenBSD: Makefile,v 1.19 1999/07/17 21:54:39 niklas Exp $ +# $EOM: Makefile,v 1.55 1999/07/17 20:44:07 niklas Exp $ # # Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved. @@ -50,14 +50,14 @@ OS= openbsd PROG= isakmpd BINDIR?= /sbin -SRCS= app.c asn.c asn_useful.c attribute.c cert.c connection.c \ +SRCS= app.c attribute.c cert.c connection.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_aggressive.c ike_main_mode.c ike_phase_1.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 \ - pkcs.c prf.c sa.c sysdep.c timer.c transport.c udp.c ui.c \ + prf.c sa.c sysdep.c timer.c transport.c udp.c ui.c \ util.c x509.c GENERATED= exchange_num.h ipsec_fld.h ipsec_num.h isakmp_fld.h \ @@ -111,8 +111,9 @@ DEBUG= -g # strcpy (_y_, _x_); \ # })' +SUBDIR= apps .if !make(install) -SUBDIR= regress +SUBDIR+= regress .endif .include "sysdep/${OS}/Makefile.sysdep" diff --git a/sbin/isakmpd/README.PKI b/sbin/isakmpd/README.PKI new file mode 100644 index 00000000000..0021525487e --- /dev/null +++ b/sbin/isakmpd/README.PKI @@ -0,0 +1,59 @@ +$OpenBSD: README.PKI,v 1.1 1999/07/17 21:54:39 niklas Exp $ + +1 Create your own CA as root. + + ssleay genrsa -out /etc/ssl/private/ca.key 1024 + ssleay req -new -key /etc/ssl/private/ca.key \ + -out /etc/ssl/private/ca.csr + + You are now being asked to enter information that will be incorporated + into your certificate request. What you are about to enter is what is + called a Distinguished Name or a DN. There are quite a few fields but + you can leave some blank. For some fields there will be a default + value, if you enter '.', the field will be left blank. + + ssleay x509 -req -days 365 -in /etc/ssl/private/ca.csr \ + -signkey /etc/ssl/private/ca.key \ + -out /etc/ssl/ca.crt + +2 Create keys and certificates for your isakmpd peers. This step as well + as the next one, needs to be done for every peer. Furthermore the + last step will need to be done once for each ID you want the peer + to have. The 10.0.0.1 below symbolizes that ID, and should be + changed for each invocation. You will be asked for a DN for each + run too. See to encode the ID in the common name too, so it gets + unique. + + mkdir -p /etc/isakmpd/private /etc/isakmpd/ca /etc/isakmpd/certs + chmod 600 /etc/isakmpd/private + ssleay genrsa -out /etc/isakmpd/private/local.key 1024 + ssleay req -new -key /etc/isakmpd/private/local.key \ + -out /etc/isakmpd/private/10.0.0.1.csr + + Now take these certificate signing requests to your CA and process + them like below. You have to add some extensions to the certificate + in order to make it usable for isakmpd, which is why you will need + to run certpatch. Replace 10.0.0.1 with the IP-address which isakmpd + will be using for identity. + + ssleay x509 -req -days 365 -in 10.0.0.1.csr -CA /etc/ssl/ca.crt \ + -CAkey /etc/ssl/private/ca.key -CAcreateserial \ + -out 10.0.0.1.crt + certpatch -i 10.0.0.1 -k /etc/ssl/private/ca.key \ + 10.0.0.1.crt 10.0.0.1.crt + + Put the certificate (the file ending in .crt) in /etc/isakmpd/certs/ + on your local system. Also carry over the CA cert /etc/ssl/ca.crt + and put it in /etc/isakmpd/ca/. + +3 See to that your config files will point out the directories where + you keep certificates. I.e. add something like this to + /etc/isakmp/conf: + + # Certificates stored in PEM format + [X509-certificates] + CA-directory= /etc/isakmpd/ca/ + Cert-directory= /etc/isakmpd/certs/ + + [RSA_SIG] + PRIVKEY= /etc/isakmpd/private/local.key diff --git a/sbin/isakmpd/cert.c b/sbin/isakmpd/cert.c index 3836bb8375e..c5e88c8155d 100644 --- a/sbin/isakmpd/cert.c +++ b/sbin/isakmpd/cert.c @@ -1,8 +1,8 @@ -/* $OpenBSD: cert.c,v 1.9 1999/04/19 19:57:29 niklas Exp $ */ -/* $EOM: cert.c,v 1.10 1999/04/18 15:17:23 niklas Exp $ */ +/* $OpenBSD: cert.c,v 1.10 1999/07/17 21:54:39 niklas Exp $ */ +/* $EOM: cert.c,v 1.11 1999/07/17 20:44:09 niklas Exp $ */ /* - * Copyright (c) 1998 Niels Provos. All rights reserved. + * Copyright (c) 1998, 1999 Niels Provos. All rights reserved. * Copyright (c) 1999 Niklas Hallqvist. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,9 +36,12 @@ */ #include <sys/param.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <ssl/ssl.h> + #include "sysdep.h" #include "cert.h" @@ -49,11 +52,30 @@ struct cert_handler cert_handler[] = { { ISAKMP_CERTENC_X509_SIG, + x509_cert_init, x509_cert_get, x509_cert_validate, + x509_cert_insert, x509_cert_free, x509_certreq_validate, x509_certreq_decode, x509_free_aca, x509_cert_obtain, x509_cert_get_key, x509_cert_get_subject } }; +/* Initialize all certificate handlers */ + +int +cert_init (void) +{ + int i, err = 1; + + /* Add all algorithms know by SSL */ + SSLeay_add_all_algorithms (); + + for (i = 0; i < sizeof cert_handler / sizeof cert_handler[0]; i++) + if (cert_handler[i].cert_init && !(*cert_handler[i].cert_init) ()) + err = 0; + + return err; +} + struct cert_handler * cert_get (u_int16_t id) { diff --git a/sbin/isakmpd/cert.h b/sbin/isakmpd/cert.h index c60909c6446..f083c7827be 100644 --- a/sbin/isakmpd/cert.h +++ b/sbin/isakmpd/cert.h @@ -1,8 +1,8 @@ -/* $OpenBSD: cert.h,v 1.3 1998/11/17 11:10:08 niklas Exp $ */ -/* $EOM: cert.h,v 1.5 1998/08/21 13:47:51 provos Exp $ */ +/* $OpenBSD: cert.h,v 1.4 1999/07/17 21:54:39 niklas Exp $ */ +/* $EOM: cert.h,v 1.6 1999/07/17 20:44:09 niklas Exp $ */ /* - * Copyright (c) 1998 Niels Provos. All rights reserved. + * Copyright (c) 1998, 1999 Niels Provos. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -41,16 +41,29 @@ #include <sys/types.h> #include <sys/queue.h> -struct exchange; +/* + * CERT handler for each kind of certificate: + * + * cert_init - Initialize CERT handler - called only once + * cert_get - Get a certificate in internal representation from raw data + * cert_validate - validated a certificate, if it returns != 0 we can use it. + * cert_insert - inserts cert into memory storage, we can retrieve with + * cert_obtain. + */ struct cert_handler { u_int16_t id; /* ISAKMP Cert Encoding ID */ + int (*cert_init) (void); + void *(*cert_get) (u_int8_t *, u_int32_t); + int (*cert_validate) (void *); + int (*cert_insert) (void *); + void (*cert_free) (void *); int (*certreq_validate) (u_int8_t *, u_int32_t); void *(*certreq_decode) (u_int8_t *, u_int32_t); void (*free_aca) (void *); - int (*cert_obtain) (struct exchange *, void *, u_int8_t **, u_int32_t *); - int (*cert_get_key) (u_int8_t *, u_int32_t, void *); - int (*cert_get_subject) (u_int8_t *, u_int32_t, u_int8_t **, u_int32_t *); + int (*cert_obtain) (u_int8_t *, size_t, void *, u_int8_t **, u_int32_t *); + int (*cert_get_key) (void *, void *); + int (*cert_get_subject) (void *, u_int8_t **, u_int32_t *); }; /* the acceptable authority of cert request */ @@ -65,5 +78,6 @@ struct certreq_aca { struct cert_handler *cert_get (u_int16_t); struct certreq_aca *certreq_decode (u_int16_t, u_int8_t *, u_int32_t); +int cert_init (void); #endif /* _CERT_H_ */ diff --git a/sbin/isakmpd/exchange.c b/sbin/isakmpd/exchange.c index cdc1ae59f05..a6d47e9580c 100644 --- a/sbin/isakmpd/exchange.c +++ b/sbin/isakmpd/exchange.c @@ -1,5 +1,5 @@ -/* $OpenBSD: exchange.c,v 1.22 1999/07/07 22:05:06 niklas Exp $ */ -/* $EOM: exchange.c,v 1.108 1999/06/06 17:48:30 ho Exp $ */ +/* $OpenBSD: exchange.c,v 1.23 1999/07/17 21:54:39 niklas Exp $ */ +/* $EOM: exchange.c,v 1.109 1999/07/17 20:44:09 niklas Exp $ */ /* * Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved. @@ -1140,6 +1140,7 @@ exchange_free_aux (void *v_exch) { struct exchange *exchange = v_exch; struct sa *sa, *next_sa; + struct cert_handler *handler; log_debug (LOG_EXCHANGE, 80, "exchange_free_aux: freeing exchange %p", exchange); @@ -1166,6 +1167,14 @@ exchange_free_aux (void *v_exch) free (exchange->data); if (exchange->name) free (exchange->name); + if (exchange->recv_cert) + { + handler = cert_get (exchange->recv_certtype); + if (handler) + handler->cert_free (exchange->recv_cert); + else if (exchange->recv_certtype == ISAKMP_CERTENC_NONE) + free (exchange->recv_cert); + } exchange_free_aca_list (exchange); LIST_REMOVE (exchange, link); @@ -1430,12 +1439,17 @@ exchange_add_certs (struct message *msg) struct certreq_aca *aca; u_int8_t *cert; u_int32_t certlen; + u_int8_t *id; + size_t id_len; + + id = exchange->initiator ? exchange->id_r : exchange->id_i; + id_len = exchange->initiator ? exchange->id_r_len : exchange->id_i_len; for (aca = TAILQ_FIRST (&exchange->aca_list); aca; aca = TAILQ_NEXT (aca, link)) { /* XXX? If we can not satisfy a CERTREQ we drop the message */ - if (!aca->handler->cert_obtain (exchange, aca->data, &cert, &certlen)) + if (!aca->handler->cert_obtain (id, id_len, aca->data, &cert, &certlen)) { log_print ("exchange_add_certs: could not obtain cert for a type %d " "cert request", aca->id); diff --git a/sbin/isakmpd/exchange.h b/sbin/isakmpd/exchange.h index 3ef5d6e609a..ff5fe84f7d7 100644 --- a/sbin/isakmpd/exchange.h +++ b/sbin/isakmpd/exchange.h @@ -1,5 +1,5 @@ -/* $OpenBSD: exchange.h,v 1.12 1999/07/07 22:09:53 niklas Exp $ */ -/* $EOM: exchange.h,v 1.25 1999/06/07 00:02:11 ho Exp $ */ +/* $OpenBSD: exchange.h,v 1.13 1999/07/17 21:54:39 niklas Exp $ */ +/* $EOM: exchange.h,v 1.26 1999/07/17 20:44:10 niklas Exp $ */ /* * Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved. @@ -150,6 +150,14 @@ struct exchange { int key_length; struct keystate *keystate; + /* + * Received certificate - used to verify signatures on packet, + * stored here for later policy processing. + * a type of ISAKMP_CERTENC_NONE implies pre-shared key. + */ + int recv_certtype, recv_certlen; + void *recv_cert; + /* XXX This is no longer necessary, it is covered by policy. */ /* Acceptable authorities for cert requests */ diff --git a/sbin/isakmpd/ike_auth.c b/sbin/isakmpd/ike_auth.c index d2c48e575b3..5b9859e309c 100644 --- a/sbin/isakmpd/ike_auth.c +++ b/sbin/isakmpd/ike_auth.c @@ -1,8 +1,9 @@ -/* $OpenBSD: ike_auth.c,v 1.14 1999/07/07 22:04:08 niklas Exp $ */ -/* $EOM: ike_auth.c,v 1.31 1999/06/10 13:29:02 niklas Exp $ */ +/* $OpenBSD: ike_auth.c,v 1.15 1999/07/17 21:54:39 niklas Exp $ */ +/* $EOM: ike_auth.c,v 1.32 1999/07/17 20:44:10 niklas Exp $ */ /* * Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved. + * Copyright (c) 1999 Niels Provos. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,9 +41,12 @@ #include <stdlib.h> #include <string.h> +#include <ssl/evp.h> +#include <ssl/pem.h> +#include <ssl/x509.h> + #include "sysdep.h" -#include "asn.h" #include "cert.h" #include "conf.h" #include "constants.h" @@ -55,7 +59,6 @@ #include "ipsec_doi.h" #include "log.h" #include "message.h" -#include "pkcs.h" #include "prf.h" #include "transport.h" #include "util.h" @@ -109,39 +112,79 @@ ike_auth_get (u_int16_t id) * Find and decode the configured key (pre-shared or public) for the * peer denoted by ID. Stash the len in KEYLEN. */ -static char * -ike_auth_get_key (char *id, size_t *keylen) +static void * +ike_auth_get_key (int type, char *id, size_t *keylen) { char *key, *buf; + char *keyfile; + BIO *keyh; + RSA *rsakey; - /* Get the pre-shared key for our peer. */ - key = conf_get_str (id, "Authentication"); - if (!key) + switch (type) { - log_print ("ike_auth_get_key: no key found for peer \"%s\"", id); - return 0; - } + case IKE_AUTH_PRE_SHARED: + /* Get the pre-shared key for our peer. */ + key = conf_get_str (id, "Authentication"); + if (!key) + { + log_print ("ike_auth_get_key: no key found for peer \"%s\"", id); + return 0; + } + + /* If the key starts with 0x it is in hex format. */ + if (strncasecmp (key, "0x", 2) == 0) + { + *keylen = (strlen (key) - 1) / 2; + buf = malloc (*keylen); + if (!buf) + { + log_print ("ike_auth_get_key: malloc (%d) failed", *keylen); + return 0; + } + if (hex2raw (key + 2, buf, *keylen)) + { + free (buf); + log_print ("ike_auth_get_key: invalid hex key %s", key); + return 0; + } + key = buf; + } + else + *keylen = strlen (key); + break; - /* If the key starts with 0x it is in hex format. */ - if (strncasecmp (key, "0x", 2) == 0) - { - *keylen = (strlen (key) - 1) / 2; - buf = malloc (*keylen); - if (!buf) + case IKE_AUTH_RSA_SIG: + keyfile = conf_get_str ("RSA_sig", "privkey"); + + if ((keyh = BIO_new (BIO_s_file ())) == NULL) { - log_print ("ike_auth_get_key: malloc (%d) failed", *keylen); + log_print ("ike_auth_get_key: BIO_new() failed"); return 0; } - if (hex2raw (key + 2, buf, *keylen)) + if (BIO_read_filename (keyh, keyfile) == -1) { - free (buf); - log_print ("ike_auth_get_key: invalid hex key %s", key); + log_print ("ike_auth_get_key: BIO_read_filename(%s) failed", + keyfile); + BIO_free (keyh); return 0; } - key = buf; + + rsakey = PEM_read_bio_RSAPrivateKey (keyh, NULL, NULL); + if (!rsakey) + { + log_print ("ike_auth_get_key: PEM_read_bio_RSAPrivateKey failed", + keyfile); + BIO_free (keyh); + return 0; + } + + BIO_free (keyh); + return rsakey; + + default: + log_print ("ike_auth_get_key: unknown key type %d", type); + return 0; } - else - *keylen = strlen (key); return key; } @@ -157,10 +200,20 @@ pre_shared_gen_skeyid (struct exchange *exchange, size_t *sz) size_t keylen; /* Get the pre-shared key for our peer. */ - key = ike_auth_get_key (exchange->name, &keylen); + key = ike_auth_get_key (IKE_AUTH_PRE_SHARED, exchange->name, &keylen); if (!key) return 0; + /* Store the secret key for later policy processing */ + exchange->recv_cert = malloc (keylen); + if (!exchange->recv_cert) + { + log_error ("pre_shared_gen_skeyid: malloc (%d) failed", keylen); + return 0; + } + exchange->recv_certlen = keylen; + exchange->recv_certtype = ISAKMP_CERTENC_NONE; + prf = prf_alloc (ie->prf_type, ie->hash->type, key, keylen); if (buf) free (buf); @@ -310,12 +363,15 @@ rsa_sig_decode_hash (struct message *msg) struct exchange *exchange = msg->exchange; struct ipsec_exch *ie = exchange->data; struct payload *p; - struct rsa_public_key key; + void *cert; + u_int8_t *rawcert; + u_int32_t rawlen; + RSA *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; + int len; u_int32_t id_cert_len; size_t id_len; int found = 0; @@ -327,14 +383,50 @@ rsa_sig_decode_hash (struct message *msg) if (!id || id_len == 0) { - log_print ("rsa_sig_decode_hash: no ID in sa"); + log_print ("rsa_sig_decode_hash: ID is missing"); + return -1; + } + + /* XXX This needs to be configureable. */ + handler = cert_get (ISAKMP_CERTENC_X509_SIG); + if (!handler) + { + log_print ("rsa_sig_decode_hash: " + "cert_get (ISAKMP_CERTENC_X509_SIG) failed"); 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; + /* Obtain a certificate from our certificate storage */ + if (handler->cert_obtain (id, id_len, 0, &rawcert, &rawlen)) + { + cert = handler->cert_get (rawcert, rawlen); + if (!cert) + log_debug (LOG_CRYPTO, 50, + "rsa_sig_decode_hash: certificate malformed"); + else + { + if (!handler->cert_get_key (cert, &key)) + { + log_print ("rsa_sig_decode_hash: decoding certificate failed"); + handler->cert_free (cert); + } + else + { + found++; + log_debug (LOG_CRYPTO, 40, + "rsa_sig_decode_hash: using cert from X509_STORE"); + exchange->recv_cert = cert; + exchange->recv_certtype = handler->id; + } + } + free (rawcert); + } + /* + * Walk over potential CERT payloads in this message. + * XXX I believe this is the wrong spot for this. CERTs can appear + * anytime. + */ for (p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_CERT]); p; p = TAILQ_NEXT (p, link)) { @@ -354,32 +446,51 @@ rsa_sig_decode_hash (struct message *msg) continue; } - 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)) + cert = handler->cert_get (p->p + ISAKMP_CERT_DATA_OFF, + GET_ISAKMP_GEN_LENGTH (p->p) + - ISAKMP_CERT_DATA_OFF); + if (!cert) { + log_print ("rsa_sig_decode_hash: can not get data from CERT"); + continue; + } + + if (!handler->cert_validate (cert)) + { + handler->cert_free (cert); + log_print ("rsa_sig_decode_hash: received CERT can't be validated"); + continue; + } + + if (!handler->cert_get_subject (cert, &id_cert, &id_cert_len)) + { + handler->cert_free (cert); log_print ("rsa_sig_decode_hash: can not get subject from CERT"); continue; } if (id_cert_len != id_len || memcmp (id, id_cert, id_len) != 0) { + handler->cert_free (cert); log_print ("rsa_sig_decode_hash: CERT subject does not match ID"); free (id_cert); continue; } 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)) + if (!handler->cert_get_key (cert, &key)) { + handler->cert_free (cert); log_print ("rsa_sig_decode_hash: decoding payload CERT failed"); continue; } + /* We validated the cert, cache it for later use. */ + handler->cert_insert (cert); + + exchange->recv_cert = cert; + exchange->recv_certtype = GET_ISAKMP_CERT_ENCODING (p->p); + found++; } @@ -387,7 +498,7 @@ rsa_sig_decode_hash (struct message *msg) if (!found) { #ifdef notyet - rawkey = ike_auth_get_key (exchange->name, &keylen); + rawkey = ike_auth_get_key (IKE_AUTH_RSA_SIG, exchange->name, &keylen); if (!rawkey) { log_print ("rsa_sig_decode_hash: no public key found"); @@ -403,32 +514,44 @@ rsa_sig_decode_hash (struct message *msg) if (!p) { log_print ("rsa_sig_decode_hash: missing signature payload"); - pkcs_free_public_key (&key); + RSA_free (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)) + len = GET_ISAKMP_GEN_LENGTH (p->p) - ISAKMP_SIG_SZ; + if (len != RSA_size (key)) { - pkcs_free_public_key (&key); + RSA_free (key); log_print ("rsa_sig_decode_hash: " "SIG payload length does not match public key"); return -1; } - if (!pkcs_rsa_decrypt (PKCS_PRIVATE, &key, 0, p->p + ISAKMP_SIG_DATA_OFF, - hash_p, &len)) + *hash_p = malloc (len); + if (!*hash_p) { - pkcs_free_public_key (&key); + RSA_free (key); + log_error ("rsa_sig_decode_hash: malloc (%d) failed", len); return -1; } - pkcs_free_public_key (&key); + len = RSA_public_decrypt (len, p->p + ISAKMP_SIG_DATA_OFF, *hash_p, key, + RSA_PKCS1_PADDING); + if (len == -1) + { + RSA_free (key); + log_print ("rsa_sig_decode_hash: RSA_public_decrypt () failed"); + return -1; + } + + RSA_free (key); if (len != hashsize) { free (*hash_p); *hash_p = 0; + log_print ("rsa_sig_decode_hash: len %d != hashsize %d", len, hashsize); return -1; } @@ -472,87 +595,93 @@ rsa_sig_encode_hash (struct message *msg) struct ipsec_exch *ie = exchange->data; size_t hashsize = ie->hash->hashsize; struct cert_handler *handler; - struct rsa_private_key key; + RSA *key; char header[80]; int initiator = exchange->initiator; - u_int8_t *buf, *asn, *data; - u_int32_t asnlen, datalen; - char *keyfile; + u_int8_t *buf, *data; + u_int32_t datalen; + u_int8_t *id; + size_t id_len; + + id = initiator ? exchange->id_i : exchange->id_r; + id_len = initiator ? exchange->id_i_len : exchange->id_r_len; /* XXX This needs to be configureable. */ handler = cert_get (ISAKMP_CERTENC_X509_SIG); if (!handler) { - /* XXX Log? */ - return -1; - } - /* XXX Implicitly uses exchange->id_{i,r}. */ - if (!handler->cert_obtain (exchange, 0, &data, &datalen)) - { - /* XXX Log? */ + log_print ("rsa_sig_decode_hash: " + "cert_get(ISAKMP_CERTENC_X509_SIG) failed"); return -1; } - buf = realloc (data, ISAKMP_CERT_SZ + datalen); - if (!buf) - { - log_error ("rsa_sig_encode_hash: realloc (%p, %d) failed", data, - ISAKMP_CERT_SZ + datalen); - 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)) + /* Find a certificate with subjectAltName = id. */ + if (handler->cert_obtain (id, id_len, 0, &data, &datalen)) { - free (buf); - return -1; + buf = realloc (data, ISAKMP_CERT_SZ + datalen); + if (!buf) + { + log_error ("rsa_sig_encode_hash: realloc (%p, %d) failed", data, + ISAKMP_CERT_SZ + datalen); + 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)) + { + free (buf); + return -1; + } } + else + log_debug (LOG_MISC, 10, "rsa_sig_decode_hash: no certificate to send"); - /* 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)) + key = ike_auth_get_key (IKE_AUTH_RSA_SIG, exchange->name, NULL); + if (key == NULL) { - /* XXX Log? */ - free (asn); + log_error ("rsa_sig_encode_hash: could not get private key"); return -1; } - free (asn); - /* XXX hashsize is not necessarily prf->blocksize */ + /* XXX hashsize is not necessarily prf->blocksize. */ buf = malloc (hashsize); if (!buf) { log_error ("rsa_sig_encode_hash: malloc (%d) failed", hashsize); - pkcs_free_private_key (&key); + RSA_free (key); return -1; } if (ike_auth_hash (exchange, buf) == -1) { free (buf); - pkcs_free_private_key (&key); + RSA_free (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, 0, &key, buf, hashsize, &data, - &datalen)) + data = malloc (RSA_size (key)); + if (!data) { + log_error ("rsa_sig_encode_hash: malloc (%d) failed", RSA_size (key)); + RSA_free (key); + return -1; + } + + datalen = RSA_private_encrypt (hashsize, buf, data, key, RSA_PKCS1_PADDING); + if (datalen == -1) + { + log_error ("rsa_sig_encode_hash: RSA_private_encrypt () failed"); free (buf); - pkcs_free_private_key (&key); + RSA_free (key); return -1; } - pkcs_free_private_key (&key); + + RSA_free (key); free (buf); buf = realloc (data, ISAKMP_SIG_SZ + datalen); diff --git a/sbin/isakmpd/ike_phase_1.c b/sbin/isakmpd/ike_phase_1.c index f605a77d96e..13c1f1656d5 100644 --- a/sbin/isakmpd/ike_phase_1.c +++ b/sbin/isakmpd/ike_phase_1.c @@ -1,5 +1,5 @@ -/* $OpenBSD: ike_phase_1.c,v 1.5 1999/07/16 02:01:59 niklas Exp $ */ -/* $EOM: ike_phase_1.c,v 1.6 1999/07/16 02:01:52 niklas Exp $ */ +/* $OpenBSD: ike_phase_1.c,v 1.6 1999/07/17 21:54:39 niklas Exp $ */ +/* $EOM: ike_phase_1.c,v 1.7 1999/07/17 20:44:10 niklas Exp $ */ /* * Copyright (c) 1999 Niklas Hallqvist. All rights reserved. @@ -925,7 +925,7 @@ ike_phase_1_recv_AUTH (struct message *msg) /* The decoded hash will be in ie->hash_r or ie->hash_i */ if (ie->ike_auth->decode_hash (msg)) { - message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); + message_drop (msg, ISAKMP_NOTIFY_INVALID_ID_INFORMATION, 0, 1, 0); return -1; } diff --git a/sbin/isakmpd/init.c b/sbin/isakmpd/init.c index bfcb5d97868..94d7a22917e 100644 --- a/sbin/isakmpd/init.c +++ b/sbin/isakmpd/init.c @@ -1,5 +1,5 @@ -/* $OpenBSD: init.c,v 1.8 1999/07/07 22:09:54 niklas Exp $ */ -/* $EOM: init.c,v 1.15 1999/06/07 00:02:14 ho Exp $ */ +/* $OpenBSD: init.c,v 1.9 1999/07/17 21:54:39 niklas Exp $ */ +/* $EOM: init.c,v 1.16 1999/07/17 20:44:10 niklas Exp $ */ /* * Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved. @@ -39,6 +39,7 @@ #include "sysdep.h" #include "app.h" +#include "cert.h" #include "conf.h" #include "connection.h" #include "cookie.h" @@ -74,6 +75,9 @@ init () connection_init (); cookie_init (); + /* Depends on conf_init having run */ + cert_init (); + sa_init (); transport_init (); udp_init (); diff --git a/sbin/isakmpd/isakmpd.conf.5 b/sbin/isakmpd/isakmpd.conf.5 index 8ce40029b2b..0060d151e23 100644 --- a/sbin/isakmpd/isakmpd.conf.5 +++ b/sbin/isakmpd/isakmpd.conf.5 @@ -1,5 +1,5 @@ -.\" $OpenBSD: isakmpd.conf.5,v 1.21 1999/07/09 13:35:49 aaron Exp $ -.\" $EOM: isakmpd.conf.5,v 1.26 1999/07/07 19:17:31 niklas Exp $ +.\" $OpenBSD: isakmpd.conf.5,v 1.22 1999/07/17 21:54:39 niklas Exp $ +.\" $EOM: isakmpd.conf.5,v 1.27 1999/07/17 20:44:11 niklas Exp $ .\" .\" Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved. .\" @@ -130,6 +130,20 @@ at <IPSec-connection> below. Currently only the Local-ID and Remote-ID tags are looked at in those sections, as they are matched against the IDs given by the initiator. .El +.It Em X509-Certificates +.Bl -tag -width 12n +.It Em Ca-directory +A directory containing PEM certificates of certification authorities +that we trust to sign other certificates. +.It Em Cert-directory +A directory containing PEM certificates that we trust to be valid. +These certificates are used in preference to those passed in messages and +are required to have a SubjectAltName extension. +.It Em Accept-self-signed +If this tag is defined, whatever the value is, certificates that +do not originate from a trusted CA but are self-signed will be +accepted. +.El .El .Ss Referred-to sections .Bl -hang -width 12n @@ -389,6 +403,11 @@ DOI= IPSEC EXCHANGE_TYPE= ID_PROT Transforms= 3DES-SHA +# Certificates stored in PEM format +[X509-certificates] +CA-directory= /etc/isakmpd/ca/ +Cert-directory= /etc/isakmpd/certs/ + # Main mode transforms ###################### @@ -644,9 +663,7 @@ LIFE_TYPE= KILOBYTES LIFE_DURATION= 4608000,4096000:8192000 [RSA_SIG] -CERT= /etc/isakmpd_cert -PRIVKEY= /etc/isakmpd_key -PUBKEY= /etc/isakmpd_key.pub +PRIVKEY= /etc/isakmpd/private/local.key .Ed .Sh SEE ALSO .Xr ipsec 4 , diff --git a/sbin/isakmpd/regress/Makefile b/sbin/isakmpd/regress/Makefile index 0253e40ec24..23c845ae88d 100644 --- a/sbin/isakmpd/regress/Makefile +++ b/sbin/isakmpd/regress/Makefile @@ -1,8 +1,8 @@ -# $OpenBSD: Makefile,v 1.6 1999/02/26 03:28:59 niklas Exp $ -# $EOM: Makefile,v 1.7 1998/08/11 20:32:01 provos Exp $ +# $OpenBSD: Makefile,v 1.7 1999/07/17 21:54:38 niklas Exp $ +# $EOM: Makefile,v 1.8 1999/07/17 20:44:13 niklas Exp $ # -# Copyright (c) 1998 Niklas Hallqvist. All rights reserved. +# Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -34,6 +34,6 @@ # This code was written under funding by Ericsson Radio Systems. # -SUBDIR= asn b2n crypto dh ec2n exchange group hmac pkcs prf rsakeygen x509 +SUBDIR= b2n crypto dh ec2n exchange group hmac prf rsakeygen x509 .include <bsd.subdir.mk> diff --git a/sbin/isakmpd/regress/rsakeygen/Makefile b/sbin/isakmpd/regress/rsakeygen/Makefile index 92f7a4a10f6..b9868e57767 100644 --- a/sbin/isakmpd/regress/rsakeygen/Makefile +++ b/sbin/isakmpd/regress/rsakeygen/Makefile @@ -1,16 +1,49 @@ -# $OpenBSD: Makefile,v 1.5 1999/03/02 15:27:36 niklas Exp $ -# $EOM: Makefile,v 1.3 1999/02/25 15:12:01 niklas Exp $ +# $OpenBSD: Makefile,v 1.6 1999/07/17 21:54:38 niklas Exp $ +# $EOM: Makefile,v 1.4 1999/07/17 20:44:13 niklas Exp $ + +# +# Copyright (c) 1999 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. +# # RSA Key Generation PROG= rsakeygen -SRCS= log.c asn.c gmp_util.c pkcs.c rsakeygen.c sysdep.c +SRCS= log.c rsakeygen.c sysdep.c TOPSRC= ${.CURDIR}/../.. TOPOBJ!= cd ${TOPSRC}; printf "all:\n\t@pwd\n" |${MAKE} -f- OS!= awk '/^OS=/ { print $$2 }' ${.CURDIR}/../../Makefile .PATH: ${TOPSRC} ${TOPSRC}/sysdep/${OS} ${TOPOBJ} CFLAGS+= -I${TOPSRC} -I${TOPSRC}/sysdep/${OS} -I${TOPOBJ} -Wall -LDADD+= -lgmp +LDADD+= -lgmp -lcrypto DPADD+= ${LIBDES} NOMAN= DEBUG= -g diff --git a/sbin/isakmpd/regress/rsakeygen/rsakeygen.c b/sbin/isakmpd/regress/rsakeygen/rsakeygen.c index fbc4be408c1..684896c3297 100644 --- a/sbin/isakmpd/regress/rsakeygen/rsakeygen.c +++ b/sbin/isakmpd/regress/rsakeygen/rsakeygen.c @@ -1,8 +1,8 @@ -/* $OpenBSD: rsakeygen.c,v 1.7 1999/04/27 21:02:56 niklas Exp $ */ -/* $EOM: rsakeygen.c,v 1.7 1999/04/05 18:27:38 niklas Exp $ */ +/* $OpenBSD: rsakeygen.c,v 1.8 1999/07/17 21:54:38 niklas Exp $ */ +/* $EOM: rsakeygen.c,v 1.8 1999/07/17 20:44:13 niklas Exp $ */ /* - * Copyright (c) 1998 Niels Provos. All rights reserved. + * Copyright (c) 1998, 1999 Niels Provos. All rights reserved. * Copyright (c) 1999 Niklas Hallqvist. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,15 +42,16 @@ #include <string.h> #include <gmp.h> +#include <ssl/rsa.h> + #include "log.h" -#include "gmp_util.h" -#include "asn.h" -#include "pkcs.h" #define nibble2bin(y) (tolower((y)) < 'a' ? (y) - '0': tolower((y)) - 'a' + 10) #define hexchar2bin(x) ((nibble2bin((x)[0]) << 4) + nibble2bin((x)[1])) #define nibble2c(x) ((x) >= 10 ? ('a'-10+(x)) : ('0' + (x))) +#define TEST_STRING "!Dies ist ein Test" + void asc2bin (u_int8_t *bin, u_int8_t *asc, u_int16_t len) { int i; @@ -64,60 +65,67 @@ void asc2bin (u_int8_t *bin, u_int8_t *asc, u_int16_t len) int main (void) { - char *data = "Niels ist ein Luser!"; - u_int8_t *enc, *dec, *asn; - u_int32_t enclen; - u_int16_t len; + u_int8_t enc[256], dec[256], *asn, *foo; + int len; FILE *fd; int erg = 0; - struct rsa_public_key key; - struct rsa_private_key priv; + RSA *key; log_debug_cmd ((enum log_classes)LOG_CRYPTO, 99); - pkcs_generate_rsa_keypair (&key, &priv, 1024); + strcpy(dec, TEST_STRING); + + key = RSA_generate_key(1024, RSA_F4, NULL, NULL); + if (key == NULL) + { + printf("Failed to generate key\n"); + return 0; + } - printf ("n: 0x"); mpz_out_str (stdout, 16, key.n); - printf ("\ne: 0x"); mpz_out_str (stdout, 16, key.e); + printf ("n: 0x"); BN_print_fp(stdout, key->n); + printf ("\ne: 0x"); BN_print_fp (stdout, key->e); printf ("\n"); - printf ("n: 0x"); mpz_out_str (stdout, 16, priv.n); - printf ("\ne: 0x"); mpz_out_str (stdout, 16, priv.e); - printf ("\nd: 0x"); mpz_out_str (stdout, 16, priv.d); - printf ("\np: 0x"); mpz_out_str (stdout, 16, priv.p); - printf ("\nq: 0x"); mpz_out_str (stdout, 16, priv.q); + printf ("n: 0x"); BN_print_fp (stdout, key->n); + printf ("\ne: 0x"); BN_print_fp (stdout, key->e); + printf ("\nd: 0x"); BN_print_fp (stdout, key->d); + printf ("\np: 0x"); BN_print_fp (stdout, key->p); + printf ("\nq: 0x"); BN_print_fp (stdout, key->q); printf ("\n"); printf ("Testing Signing/Verifying: "); /* Sign with Private Key */ - if (!pkcs_rsa_encrypt (PKCS_PRIVATE, NULL, &priv, data, strlen(data)+1, - &enc, &enclen)) + if ((len = RSA_private_encrypt (strlen(dec)+1, dec, enc, key, + RSA_PKCS1_PADDING)) == -1) printf ("FAILED "); else /* Decrypt/Verify with Public Key */ - erg = pkcs_rsa_decrypt (PKCS_PRIVATE, &key, NULL, enc, &dec, &len); + erg = RSA_public_decrypt (len, enc, dec, key, RSA_PKCS1_PADDING); - if (!erg || strcmp(data,dec)) + if (erg == -1 || strcmp(dec, TEST_STRING)) printf ("FAILED "); else printf ("OKAY "); printf ("\n"); - asn = pkcs_public_key_to_asn (&key); + len = i2d_RSAPublicKey(key, NULL); + foo = asn = malloc(len); + len = i2d_RSAPublicKey(key, &foo); fd = fopen ("isakmpd_key.pub", "w"); - fwrite (asn, asn_get_len (asn), 1, fd); + fwrite (asn, len, 1, fd); fclose (fd); free (asn); - asn = pkcs_private_key_to_asn (&priv); + len = i2d_RSAPrivateKey(key, NULL); + foo = asn = malloc(len); + len = i2d_RSAPrivateKey(key, &foo); fd = fopen ("isakmpd_key", "w"); - fwrite (asn, asn_get_len (asn), 1, fd); + fwrite (asn, len, 1, fd); fclose (fd); free (asn); - pkcs_free_public_key (&key); - pkcs_free_private_key (&priv); + RSA_free(key); return 1; } diff --git a/sbin/isakmpd/regress/x509/Makefile b/sbin/isakmpd/regress/x509/Makefile index 0097e050cee..27a8f11784f 100644 --- a/sbin/isakmpd/regress/x509/Makefile +++ b/sbin/isakmpd/regress/x509/Makefile @@ -1,17 +1,51 @@ -# $OpenBSD: Makefile,v 1.5 1999/03/02 15:27:36 niklas Exp $ -# $EOM: Makefile,v 1.5 1999/02/25 15:10:11 niklas Exp $ +# $OpenBSD: Makefile,v 1.6 1999/07/17 21:54:38 niklas Exp $ +# $EOM: Makefile,v 1.6 1999/07/17 20:44:13 niklas Exp $ + +# +# Copyright (c) 1999 Niels Provos. All rights reserved. +# Copyright (c) 1999 Niklas Hallqvist. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# 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. +# # Test X509 PROG= x509test -SRCS= x509test.c conf.c asn.c asn_useful.c gmp_util.c log.c pkcs.c \ - sysdep.c hash.c x509.c +SRCS= x509test.c conf.c log.c sysdep.c x509.c field.c util.c \ + isakmp_fld.c ipsec_fld.c ipsec_num.c isakmp_num.c constants.c TOPSRC= ${.CURDIR}/../.. TOPOBJ!= cd ${TOPSRC}; printf "all:\n\t@pwd\n" |${MAKE} -f- OS!= awk '/^OS=/ { print $$2 }' ${.CURDIR}/../../Makefile .PATH: ${TOPSRC} ${TOPSRC}/sysdep/${OS} ${TOPOBJ} CFLAGS+= -I${TOPSRC} -I${TOPSRC}/sysdep/${OS} -I${TOPOBJ} -Wall -LDADD+= -lgmp +LDADD+= -lgmp -lcrypto DPADD+= ${LIBDES} NOMAN= DEBUG= -g diff --git a/sbin/isakmpd/regress/x509/x509test.c b/sbin/isakmpd/regress/x509/x509test.c index 8ee150e979a..2c070dc6788 100644 --- a/sbin/isakmpd/regress/x509/x509test.c +++ b/sbin/isakmpd/regress/x509/x509test.c @@ -1,8 +1,9 @@ -/* $OpenBSD: x509test.c,v 1.7 1999/07/07 22:14:31 niklas Exp $ */ -/* $EOM: x509test.c,v 1.5 1999/06/10 13:39:20 niklas Exp $ */ +/* $OpenBSD: x509test.c,v 1.8 1999/07/17 21:54:38 niklas Exp $ */ +/* $EOM: x509test.c,v 1.6 1999/07/17 20:44:14 niklas Exp $ */ /* - * Copyright (c) 1998 Niels Provos. All rights reserved. + * Copyright (c) 1998, 1999 Niels Provos. All rights reserved. + * Copyright (c) 1999 Niklas Hallqvist. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,6 +35,14 @@ * This code was written under funding by Ericsson Radio Systems. */ +/* + * This program takes a certificate generated by ssleay and a key pair + * from rsakeygen. It reads the IP address from certificate.txt and + * includes this as subject alt name extension into the certifcate. + * The result gets written as new certificate that can be used by + * isakmpd. + */ + #include <sys/param.h> #include <sys/types.h> #include <sys/mman.h> @@ -43,23 +52,25 @@ #include <stdio.h> #include <gmp.h> #include <stdlib.h> +#include <stdio.h> #include <string.h> #include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ssl/rsa.h> +#include <ssl/x509.h> +#include <ssl/pem.h> + #include "conf.h" -#include "asn.h" -#include "asn_useful.h" -#include "pkcs.h" -#include "x509.h" #include "log.h" +#include "ipsec_num.h" +#include "x509.h" u_int32_t file_sz; -#define LINECOL(x,y) (x) = strsep (&(y), "\n\r"); \ - (x) = strchr ((x), ':') + 1; \ - while (isspace((x)[0])) (x)++; \ - - u_int8_t * open_file (char *name) { @@ -84,83 +95,123 @@ open_file (char *name) } int -main (void) +main (int argc, char *argv[]) { - struct rsa_private_key priv; - struct x509_certificate cert; - FILE *fd; - char *p, *p2; - u_int8_t *addr, *asn; - u_int32_t asnlen, len; - - addr = open_file ("isakmpd_key"); - if (!pkcs_private_key_from_asn (&priv, addr, asn_get_len (addr))) + RSA *pub_key, *priv_key; + X509 *cert; + BIO *certfile, *keyfile; + EVP_PKEY *pkey_pub; + u_char ipaddr[6]; + struct in_addr saddr; + char enc[256], dec[256]; + u_int8_t idpayload[8]; + int err, len; + + if (argc < 3 || argc > 4) { - munmap (addr, file_sz); + fprintf (stderr, "usage: x509test private-key certificate ip-address\n"); exit (1); } - munmap (addr, file_sz); - addr = open_file ("isakmpd_key.pub"); - if (!pkcs_public_key_from_asn (&cert.key, addr, asn_get_len (addr))) + /* + * X509_verify will fail, as will all other functions that call + * EVP_get_digest_byname. + */ + + SSLeay_add_all_algorithms (); + + printf ("Reading private key %s\n", argv[1]); + keyfile = BIO_new (BIO_s_file ()); + if (BIO_read_filename (keyfile, argv[1]) == -1) { - munmap (addr, file_sz); + perror ("read"); exit (1); } - munmap (addr, file_sz); - - cert.signaturetype = strdup (ASN_ID_MD5WITHRSAENC); - cert.issuer1.type = strdup (ASN_ID_COUNTRY_NAME); - cert.issuer2.type = strdup (ASN_ID_ORGANIZATION_NAME); - cert.subject1.type = strdup (ASN_ID_COUNTRY_NAME); - cert.subject2.type = strdup (ASN_ID_ORGANIZATION_NAME); - - addr = open_file ("certificate.txt"); - p = addr; - - LINECOL (p2, p); cert.version = atoi (p2); - LINECOL (p2, p); cert.serialnumber = atoi (p2); - LINECOL (p2, p); cert.issuer1.val = strdup (p2); - LINECOL (p2, p); cert.issuer2.val = strdup (p2); - LINECOL (p2, p); cert.subject1.val = strdup (p2); - LINECOL (p2, p); cert.subject2.val = strdup (p2); - LINECOL (p2, p); cert.start = strdup (p2); - LINECOL (p2, p); cert.end = strdup (p2); - munmap (addr, file_sz); - - /* XXX Just put any IP number in there. */ - cert.extension.type = strdup (ASN_ID_SUBJECT_ALT_NAME); - cert.extension.val = p = malloc (8); - /* XXX This could also be encoded as norm_type, but time is lacking. */ - p[0] = 0x30; p[1] = 0x06; p[2] = 0x87; p[3] = 0x04; - memset (p + 4, 0, 4); - - printf ("Encoding Certificate: "); - if (!x509_encode_certificate(&cert, &asn, &asnlen)) - printf ("FAILED "); + priv_key = PEM_read_bio_RSAPrivateKey (keyfile, NULL, NULL); + BIO_free (keyfile); + if (priv_key == NULL) + { + printf("PEM_read_bio_RSAPrivateKey () failed\n"); + exit (1); + } + + /* Use a certificate created by ssleay. */ + printf ("Reading ssleay created certificate %s\n", argv[2]); + certfile = BIO_new (BIO_s_file ()); + if (BIO_read_filename (certfile, argv[2]) == -1) + { + perror ("read"); + exit (1); + } + cert = PEM_read_bio_X509 (certfile, NULL, NULL); + BIO_free (certfile); + if (cert == NULL) + { + printf("PEM_read_bio_X509 () failed\n"); + exit (1); + } + + pkey_pub = X509_get_pubkey (cert); + /* XXX Violation of the interface? */ + pub_key = pkey_pub->pkey.rsa; + if (pub_key == NULL) + { + exit (1); + } + + printf ("Testing RSA keys: "); + + err = 0; + strcpy (dec, "Eine kleine Testmeldung"); + if ((len = RSA_private_encrypt (strlen (dec), dec, enc, priv_key, + RSA_PKCS1_PADDING)) == -1) + + printf ("SIGN FAILED "); else - printf ("OKAY "); - printf ("\n"); + err = RSA_public_decrypt (len, enc, dec, pub_key, RSA_PKCS1_PADDING); - printf ("Creating Signature: "); - if (!x509_create_signed (asn, asnlen, &priv, &addr, &len)) - printf ("FAILED "); + if (err == -1 || strcmp (dec, "Eine kleine Testmeldung")) + printf ("SIGN/VERIFY FAILED"); else - printf ("OKAY "); + printf ("OKAY"); printf ("\n"); + printf ("Validate SIGNED: "); - if (!x509_validate_signed (addr, len, &cert.key, &asn, &asnlen)) + err = X509_verify (cert, pkey_pub); + printf ("X509 verify: %d ", err); + if (err == -1) printf ("FAILED "); else printf ("OKAY "); printf ("\n"); - fd = fopen ("cert.asn", "w"); - fwrite (addr, len, 1, fd); - fclose (fd); - - free (addr); + if (argc == 4) + { + printf ("Verifying extension: "); + if (inet_aton (argv[3], &saddr) == -1) + { + printf ("inet_aton () failed\n"); + exit (1); + } + + saddr.s_addr = htonl (saddr.s_addr); + ipaddr[0] = 0x87; + ipaddr[1] = 0x04; + ipaddr[2] = saddr.s_addr >> 24; + ipaddr[3] = (saddr.s_addr >> 16) & 0xff; + ipaddr[4] = (saddr.s_addr >> 8) & 0xff; + ipaddr[5] = saddr.s_addr & 0xff; + bzero (idpayload, sizeof idpayload); + idpayload[0] = IPSEC_ID_IPV4_ADDR; + bcopy (ipaddr + 2, idpayload + 4, 4); + + if (!x509_check_subjectaltname (idpayload, sizeof idpayload, cert)) + printf("FAILED "); + else + printf("OKAY "); + printf ("\n"); + } return 1; } diff --git a/sbin/isakmpd/samples/VPN-east.conf b/sbin/isakmpd/samples/VPN-east.conf index 1a04a5b0e5b..f5a457a7982 100644 --- a/sbin/isakmpd/samples/VPN-east.conf +++ b/sbin/isakmpd/samples/VPN-east.conf @@ -1,5 +1,5 @@ -# $OpenBSD: VPN-east.conf,v 1.4 1999/04/05 21:07:43 niklas Exp $ -# $EOM: VPN-east.conf,v 1.5 1999/04/05 21:01:45 niklas Exp $ +# $OpenBSD: VPN-east.conf,v 1.5 1999/07/17 21:54:38 niklas Exp $ +# $EOM: VPN-east.conf,v 1.6 1999/07/17 20:44:14 niklas Exp $ # A configuration sample for the isakmpd ISAKMP/Oakley (aka IKE) daemon. @@ -309,7 +309,11 @@ LIFE_DURATION= 32768,16384:65536 LIFE_TYPE= KILOBYTES LIFE_DURATION= 4608000,4096000:8192000 +# Certificates stored in PEM format +[X509-certificates] +CA-directory= /etc/isakmpd/ca/ +Cert-directory= /etc/isakmpd/certs/ +#Accept-self-signed= + [RSA_SIG] -CERT= /etc/isakmpd_cert -PRIVKEY= /etc/isakmpd_key -PUBKEY= /etc/isakmpd_key.pub +PRIVKEY= /etc/isakmpd/private/local.key diff --git a/sbin/isakmpd/samples/VPN-west.conf b/sbin/isakmpd/samples/VPN-west.conf index c8b68d17fab..c4ff9e69c49 100644 --- a/sbin/isakmpd/samples/VPN-west.conf +++ b/sbin/isakmpd/samples/VPN-west.conf @@ -1,5 +1,5 @@ -# $OpenBSD: VPN-west.conf,v 1.4 1999/04/05 21:08:11 niklas Exp $ -# $EOM: VPN-west.conf,v 1.5 1999/04/05 21:01:46 niklas Exp $ +# $OpenBSD: VPN-west.conf,v 1.5 1999/07/17 21:54:38 niklas Exp $ +# $EOM: VPN-west.conf,v 1.6 1999/07/17 20:44:14 niklas Exp $ # A configuration sample for the isakmpd ISAKMP/Oakley (aka IKE) daemon. @@ -309,7 +309,11 @@ LIFE_DURATION= 32768,16384:65536 LIFE_TYPE= KILOBYTES LIFE_DURATION= 4608000,4096000:8192000 +# Certificates stored in PEM format +[X509-certificates] +CA-directory= /etc/isakmpd/ca/ +Cert-directory= /etc/isakmpd/certs/ +#Accept-self-signed= + [RSA_SIG] -CERT= /etc/isakmpd_cert -PRIVKEY= /etc/isakmpd_key -PUBKEY= /etc/isakmpd_key.pub +PRIVKEY= /etc/isakmpd/private/local.key diff --git a/sbin/isakmpd/samples/singlehost-east.conf b/sbin/isakmpd/samples/singlehost-east.conf index bac9f34666c..3c7263b4a59 100644 --- a/sbin/isakmpd/samples/singlehost-east.conf +++ b/sbin/isakmpd/samples/singlehost-east.conf @@ -1,5 +1,5 @@ -# $OpenBSD: singlehost-east.conf,v 1.2 1999/04/05 20:57:13 niklas Exp $ -# $EOM: singlehost-east.conf,v 1.2 1999/04/05 18:32:48 niklas Exp $ +# $OpenBSD: singlehost-east.conf,v 1.3 1999/07/17 21:54:38 niklas Exp $ +# $EOM: singlehost-east.conf,v 1.3 1999/07/17 20:44:16 niklas Exp $ # A configuration sample for the isakmpd ISAKMP/Oakley (aka IKE) daemon. @@ -12,6 +12,7 @@ Shared-SADB= Defined # Incoming phase 1 negotiations are multiplexed on the source IP address [Phase 1] 10.1.0.1= ISAKMP-peer-west +Default= ISAKMP-peer-west-aggressive # These connections are walked over after config file parsing and told # to the application layer so that it will inform us when traffic wants to @@ -31,6 +32,18 @@ Configuration= Default-main-mode Authentication= mekmitasdigoat Flags= Stayalive +[ISAKMP-peer-west-aggressive] +Phase= 1 +Transport= udp +Local-address= 10.1.0.2 +Address= 10.1.0.1 +# Default values for "Port" commented out +#Port= isakmp +#Port= 500 +Configuration= Default-aggressive-mode +Authentication= mekmitasdigoat +Flags= Stayalive + [IPsec-east-west] Phase= 2 ISAKMP-peer= ISAKMP-peer-west @@ -49,13 +62,18 @@ ID-type= IPV4_ADDR_SUBNET Network= 192.168.2.0 Netmask= 255.255.255.0 -# Main mode descriptions +# Phase 1 descriptions [Default-main-mode] DOI= IPSEC EXCHANGE_TYPE= ID_PROT Transforms= 3DES-SHA +[Default-aggressive-mode] +DOI= IPSEC +EXCHANGE_TYPE= AGGRESSIVE +Transforms= 3DES-SHA-RSA + # Main mode transforms ###################### @@ -91,6 +109,13 @@ AUTHENTICATION_METHOD= PRE_SHARED GROUP_DESCRIPTION= MODP_1024 Life= LIFE_180_SECS +[3DES-SHA-RSA] +ENCRYPTION_ALGORITHM= 3DES_CBC +HASH_ALGORITHM= SHA +AUTHENTICATION_METHOD= RSA_SIG +GROUP_DESCRIPTION= MODP_1024 +Life= LIFE_180_SECS + # Blowfish [BLF-SHA-M1024] @@ -318,7 +343,11 @@ LIFE_DURATION= 32768,16384:65536 LIFE_TYPE= KILOBYTES LIFE_DURATION= 4608000,4096000:8192000 +# Certificates stored in PEM format +[X509-certificates] +CA-directory= /etc/isakmpd/ca/ +Cert-directory= /etc/isakmpd/certs/ +#Accept-self-signed= + [RSA_SIG] -CERT= /etc/isakmpd_cert -PRIVKEY= /etc/isakmpd_key -PUBKEY= /etc/isakmpd_key.pub +PRIVKEY= /etc/isakmpd/private/local.key diff --git a/sbin/isakmpd/samples/singlehost-west.conf b/sbin/isakmpd/samples/singlehost-west.conf index 0cce7fd092d..ce501155355 100644 --- a/sbin/isakmpd/samples/singlehost-west.conf +++ b/sbin/isakmpd/samples/singlehost-west.conf @@ -1,5 +1,5 @@ -# $OpenBSD: singlehost-west.conf,v 1.2 1999/04/05 20:57:25 niklas Exp $ -# $EOM: singlehost-west.conf,v 1.2 1999/04/05 18:32:49 niklas Exp $ +# $OpenBSD: singlehost-west.conf,v 1.3 1999/07/17 21:54:38 niklas Exp $ +# $EOM: singlehost-west.conf,v 1.3 1999/07/17 20:44:16 niklas Exp $ # A configuration sample for the isakmpd ISAKMP/Oakley (aka IKE) daemon. @@ -12,6 +12,7 @@ Shared-SADB= Defined # Incoming phase 1 negotiations are multiplexed on the source IP address [Phase 1] 10.1.0.2= ISAKMP-peer-east +Default= ISAKMP-peer-east-aggressive # These connections are walked over after config file parsing and told # to the application layer so that it will inform us when traffic wants to @@ -28,6 +29,20 @@ Address= 10.1.0.2 #Port= isakmp #Port= 500 Configuration= Default-main-mode +Identification= IPV4_ADDR/10.1.0.1 +Authentication= mekmitasdigoat +Flags= Stayalive + +[ISAKMP-peer-east-aggressive] +Phase= 1 +Transport= udp +Local-address= 10.1.0.1 +Address= 10.1.0.2 +# Default values for "Port" commented out +#Port= isakmp +#Port= 500 +Configuration= Default-aggressive-mode +Identification= FQDN/diego.niklas.hallqvist.se Authentication= mekmitasdigoat Flags= Stayalive @@ -49,13 +64,18 @@ ID-type= IPV4_ADDR_SUBNET Network= 192.168.2.0 Netmask= 255.255.255.0 -# Main mode descriptions +# Phase 1 descriptions [Default-main-mode] DOI= IPSEC EXCHANGE_TYPE= ID_PROT Transforms= 3DES-SHA +[Default-aggressive-mode] +DOI= IPSEC +EXCHANGE_TYPE= AGGRESSIVE +Transforms= 3DES-SHA-RSA + # Main mode transforms ###################### @@ -91,6 +111,13 @@ AUTHENTICATION_METHOD= PRE_SHARED GROUP_DESCRIPTION= MODP_1024 Life= LIFE_180_SECS +[3DES-SHA-RSA] +ENCRYPTION_ALGORITHM= 3DES_CBC +HASH_ALGORITHM= SHA +AUTHENTICATION_METHOD= RSA_SIG +GROUP_DESCRIPTION= MODP_1024 +Life= LIFE_180_SECS + # Blowfish [BLF-SHA-M1024] @@ -318,7 +345,11 @@ LIFE_DURATION= 32768,16384:65536 LIFE_TYPE= KILOBYTES LIFE_DURATION= 4608000,4096000:8192000 +# Certificates stored in PEM format +[X509-certificates] +CA-directory= /etc/isakmpd/ca/ +Cert-directory= /etc/isakmpd/certs/ +#Accept-self-signed= + [RSA_SIG] -CERT= /etc/isakmpd_cert -PRIVKEY= /etc/isakmpd_key -PUBKEY= /etc/isakmpd_key.pub +PRIVKEY= /etc/isakmpd/private/local.key diff --git a/sbin/isakmpd/sysdep/openbsd/Makefile.sysdep b/sbin/isakmpd/sysdep/openbsd/Makefile.sysdep index 403974c7d73..95de74742e0 100644 --- a/sbin/isakmpd/sysdep/openbsd/Makefile.sysdep +++ b/sbin/isakmpd/sysdep/openbsd/Makefile.sysdep @@ -1,5 +1,5 @@ -# $OpenBSD: Makefile.sysdep,v 1.4 1999/07/07 22:05:48 niklas Exp $ -# $EOM: Makefile.sysdep,v 1.4 1999/06/07 00:02:49 ho Exp $ +# $OpenBSD: Makefile.sysdep,v 1.5 1999/07/17 21:54:39 niklas Exp $ +# $EOM: Makefile.sysdep,v 1.5 1999/07/17 20:44:16 niklas Exp $ # # Copyright (c) 1999 Niklas Hallqvist. All rights reserved. @@ -25,7 +25,7 @@ # 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 +# THEORY OF LIABILITY, WHETHER INN 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. # @@ -34,8 +34,8 @@ # This code was written under funding by Ericsson Radio Systems. # -LDADD+= -lgmp -ldes -DPADD+= ${LIBGMP} ${LIBDES} +LDADD+= -lgmp -ldes -lcrypto +DPADD+= ${LIBGMP} ${LIBDES} ${LIBCRYPTO} SRCS+= pf_key_v2.c CFLAGS+= -DUSE_PF_KEY_V2 diff --git a/sbin/isakmpd/x509.c b/sbin/isakmpd/x509.c index 548f22cf31f..9ecf5c76cd0 100644 --- a/sbin/isakmpd/x509.c +++ b/sbin/isakmpd/x509.c @@ -1,8 +1,8 @@ -/* $OpenBSD: x509.c,v 1.11 1999/07/07 22:15:42 niklas Exp $ */ -/* $EOM: x509.c,v 1.16 1999/06/15 11:21:19 niklas Exp $ */ +/* $OpenBSD: x509.c,v 1.12 1999/07/17 21:54:39 niklas Exp $ */ +/* $EOM: x509.c,v 1.17 1999/07/17 20:44:12 niklas Exp $ */ /* - * Copyright (c) 1998 Niels Provos. All rights reserved. + * Copyright (c) 1998, 1999 Niels Provos. All rights reserved. * Copyright (c) 1999 Niklas Hallqvist. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,12 +38,18 @@ #include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> +#include <dirent.h> #include <fcntl.h> #include <gmp.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <ssl/x509.h> +#include <ssl/x509_vfy.h> +#include <ssl/pem.h> + #include "sysdep.h" #include "conf.h" @@ -53,842 +59,679 @@ #include "sa.h" #include "ipsec.h" #include "log.h" -#include "asn.h" -#include "asn_useful.h" -#include "pkcs.h" #include "x509.h" -/* X509 Certificate Handling functions */ +/* + * X509_STOREs do not support subjectAltNames, so we have to build + * our own hash table. + */ -/* Validate the BER Encoding of a RDNSequence in the CERT_REQ payload. */ -int -x509_certreq_validate (u_int8_t *asn, u_int32_t len) -{ - struct norm_type name = SEQOF ("issuer", RDNSequence); - int res = 1; +/* + * XXX Actually this store is not really useful, we never use it as we have + * our own hash table. It also gets collisons if we have several certificates + * only differing in subjectAltName. + */ +static X509_STORE *x509_certs; +static X509_STORE *x509_cas; - if (!asn_template_clone (&name, 1) - || (asn = asn_decode_sequence (asn, len, &name)) == 0) - { - log_print ("x509_certreq_validate: can not decode 'acceptable CA' info"); - res = 0; - } - asn_free (&name); +/* Initial number of bits used as hash. */ +#define INITIAL_BUCKET_BITS 6 - return res; -} +struct x509_hash { + LIST_ENTRY (x509_hash) link; -/* Decode the BER Encoding of a RDNSequence in the CERT_REQ payload. */ -void * -x509_certreq_decode (u_int8_t *asn, u_int32_t len) -{ - struct norm_type aca = SEQOF ("aca", RDNSequence); - struct norm_type *tmp; - struct x509_aca naca, *ret; + X509 *cert; +}; - if (!asn_template_clone (&aca, 1) - || (asn = asn_decode_sequence (asn, len, &aca)) == 0) - { - log_print ("x509_certreq_validate: can not decode 'acceptable CA' info"); - goto fail; - } - memset (&naca, 0, sizeof (naca)); +static LIST_HEAD (x509_list, x509_hash) *x509_tab; - tmp = asn_decompose ("aca.RelativeDistinguishedName.AttributeValueAssertion", - &aca); - if (!tmp) - goto fail; - x509_get_attribval (tmp, &naca.name1); +/* Works both as a maximum index and a mask. */ +static int bucket_mask; - tmp = asn_decompose ("aca.RelativeDistinguishedName[1]" - ".AttributeValueAssertion", &aca); - if (tmp) - x509_get_attribval (tmp, &naca.name2); - - asn_free (&aca); +u_int16_t +x509_hash (u_int8_t *id, size_t len) +{ + int i; + u_int16_t bucket = 0; - ret = malloc (sizeof (struct x509_aca)); - if (ret) - memcpy (ret, &naca, sizeof (struct x509_aca)); - else + /* XXX We might resize if we are crossing a certain threshold. */ + len &= ~2; + for (i = 0; i < len; i += 2) { - log_error ("x509_certreq_decode: malloc (%d) failed", - sizeof (struct x509_aca)); - x509_free_aca (&aca); + /* Doing it this way avoids alignment problems. */ + bucket ^= (id[i] + 1) * (id[i + 1] + 257); } - return ret; + bucket &= bucket_mask; - fail: - asn_free (&aca); - return 0; + return bucket; } void -x509_free_aca (void *blob) +x509_hash_init () { - struct x509_aca *aca = blob; + int i; + + bucket_mask = (1 << INITIAL_BUCKET_BITS) - 1; + x509_tab = malloc ((bucket_mask + 1) * sizeof (struct x509_list)); + if (!x509_tab) + log_fatal ("x509_hash_init: malloc (%d) failed", + (bucket_mask + 1) * sizeof (struct x509_list)); + for (i = 0; i <= bucket_mask; i++) + { + LIST_INIT (&x509_tab[i]); + } +} - if (aca->name1.type) - free (aca->name1.type); - if (aca->name1.val) - free (aca->name1.val); +/* Lookup a certificate by an ID blob. */ +X509 * +x509_hash_find (u_int8_t *id, size_t len) +{ + struct x509_hash *cert; + u_int8_t *cid; + size_t clen; - if (aca->name2.type) - free (aca->name2.type); - if (aca->name2.val) - free (aca->name2.val); + for (cert = LIST_FIRST (&x509_tab[x509_hash (id, len)]); cert; + cert = LIST_NEXT (cert, link)) + { + if (!x509_cert_get_subject (cert->cert, &cid, &clen)) + continue; + + if (clen != len || memcmp (id, cid, len) != 0) + { + free (cid); + continue; + } + free (cid); + + log_debug (LOG_CRYPTO, 70, "x509_hash_find: return X509 %p", cert->cert); + return cert->cert; + } + + log_debug (LOG_CRYPTO, 70, "x509_hash_find: no certificate matched query"); + return 0; } -/* - * Obtain a Certificate from an acceptable Certification Authority. - * XXX This is where all the magic should happen, but yet here - * you will find nothing. - */ int -x509_cert_obtain (struct exchange *exchange, void *data, u_int8_t **cert, - u_int32_t *certlen) +x509_hash_enter (X509 *cert) { - struct x509_aca *aca = data; - struct ipsec_exch *ie = exchange->data; - char *certfile; - int fd, res = 0; - struct stat st; - u_int8_t *id_cert, *asn, *id; - size_t id_len; - u_int32_t id_cert_len; - - if (aca) - log_debug (LOG_CRYPTO, 60, "x509_cert_obtain: (%s) %s, (%s) %s", - asn_parse_objectid (asn_ids, aca->name1.type), aca->name1.val, - asn_parse_objectid (asn_ids, aca->name2.type), aca->name2.val); + u_int16_t bucket = 0; + u_int8_t *id; + u_int32_t len; + struct x509_hash *certh; - /* XXX This needs to be changed - but how else would I know? */ - switch (ie->ike_auth->id) + if (!x509_cert_get_subject (cert, &id, &len)) { - case IKE_AUTH_RSA_SIG: - certfile = conf_get_str ("RSA_sig", "cert"); - if (!certfile) - return 0; - break; - default: + log_print ("x509_hash_enter: can not retrieve subjectAltName"); return 0; } - if (stat (certfile, &st) == -1) + certh = malloc (sizeof *certh); + if (!certh) { - log_error ("x509_cert_obtain: failed to state %s", certfile); + free (id); + log_error ("x509_hash_enter: malloc (%d) failed", sizeof *certh); return 0; } + memset (certh, 0, sizeof *certh); + + certh->cert = cert; - *certlen = st.st_size; + bucket = x509_hash (id, len); + free (id); + + LIST_INSERT_HEAD (&x509_tab[bucket], certh, link); + log_debug (LOG_CRYPTO, 70, "x509_hash_enter: cert %p added to bucket %d", + cert, bucket); + return 1; +} - if ((fd = open (certfile, O_RDONLY)) == -1) +/* X509 Certificate Handling functions. */ + +int +x509_read_from_dir (X509_STORE *ctx, char *name, int hash) +{ + DIR *dir; + struct dirent *file; + BIO *certh; + X509 *cert; + char fullname[PATH_MAX]; + int off, size; + + if (strlen (name) >= sizeof fullname - 1) { - log_error ("x509_cert_obtain: failed to open %s", certfile); + log_print ("x509_read_from_dir: directory name too long"); return 0; } - - *cert = malloc (st.st_size); - if (!*cert) - { - log_error ("x509_cert_obtain: malloc (%d) failed", st.st_size); - res = 0; - goto done; - } - if (read (fd, *cert, st.st_size) != st.st_size) + log_debug (LOG_CRYPTO, 40, "x509_read_from_dir: reading certs from %s", + name); + + dir = opendir (name); + if (!dir) { - log_print ("x509_cert_obtain: cert file ended early"); - free (*cert); - res = 0; - goto done; + log_error ("x509_read_from_dir: opendir (\"%s\") failed", name); + return 0; } - /* - * XXX We assume IPv4 here and a certificate with an extension - * type of subjectAltName at the end. This can go once the saved - * certificate is only used with one host with a fixed IP address. - */ - id = exchange->initiator ? exchange->id_i : exchange->id_r; - id_len = exchange->initiator ? exchange->id_i_len : exchange->id_r_len; + strncpy (fullname, name, sizeof fullname - 1); + fullname[sizeof fullname - 1] = 0; + off = strlen (fullname); + size = sizeof fullname - off - 1; - /* XXX We need our ID to set that in the cert. */ - if (id) + while ((file = readdir (dir)) != NULL) { - id += ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ; - id_len -= ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ; + if (file->d_type != DT_REG && file->d_type != DT_LNK) + continue; - /* Get offset into data structure where the IP is saved. */ - asn = *cert; - id_cert_len = asn_get_data_len (0, &asn, &id_cert); - asn = id_cert; - id_cert_len = asn_get_data_len (0, &asn, &id_cert); - id_cert += id_cert_len - 4; - memcpy (id_cert, id, 4); - } + log_debug (LOG_CRYPTO, 60, "x509_read_from_dir: reading certificate %s", + file->d_name); - res = 1; + certh = BIO_new (BIO_s_file ()); + if (!certh) + { + log_error ("x509_read_from_dir: BIO_new (BIO_s_file ()) failed"); + continue; + } - done: - close (fd); + strncpy (fullname + off, file->d_name, size); + fullname[off + size] = 0; - return res; -} + if (BIO_read_filename (certh, fullname) == -1) + { + BIO_free (certh); + log_error ("x509_read_from_dir: BIO_read_filename () failed"); + continue; + } -/* Retrieve the public key from a X509 certificate. */ -int -x509_cert_get_key (u_int8_t *asn, u_int32_t asnlen, void *blob) -{ - struct rsa_public_key *key = blob; - struct x509_certificate cert; + cert = PEM_read_bio_X509 (certh, NULL, NULL); + BIO_free (certh); + if (cert == NULL) + { + log_error ("x509_read_from_dir: PEM_read_bio_X509 (%s) failed", + file->d_name); + continue; + } - if (!x509_decode_certificate (asn, asnlen, &cert)) - return 0; + if (!X509_STORE_add_cert (ctx, cert)) + { + /* + * This is actually expected if we have several certificates only + * differing in subjectAltName, which is not an something that is + * strange. Consider multi-homed machines. + */ + log_debug (LOG_CRYPTO, 50, + "x509_read_from_dir: X509_STORE_add_cert (%s) failed", + file->d_name); + } - /* XXX Perhaps put into pkcs? */ - mpz_init_set (key->n, cert.key.n); - mpz_init_set (key->e, cert.key.e); + if (hash && !x509_hash_enter (cert)) + log_print ("x509_read_from_dir: X509_hash_enter (%s) failed", + file->d_name); + } - x509_free_certificate (&cert); + closedir (dir); return 1; } -/* Retrieve the public key from a X509 certificate. */ +/* Initialize our databases and load our own certificates. */ int -x509_cert_get_subject (u_int8_t *asn, u_int32_t asnlen, - u_int8_t **subject, u_int32_t *subjectlen) +x509_cert_init (void) { - struct x509_certificate cert; + char *dirname; - if (!x509_decode_certificate (asn, asnlen, &cert)) - return 0; + x509_hash_init (); - if (!cert.extension.type || !cert.extension.val) - goto fail; + /* Process client certificates we will accept. */ + dirname = conf_get_str ("X509-certificates", "Cert-directory"); + if (!dirname) + { + log_print ("x509_cert_init: no Cert-directory"); + return 0; + } - log_debug (LOG_CRYPTO, 60, "x509_cert_get_subject: Extension Type %s = %s", - cert.extension.type, - asn_parse_objectid (asn_ids, cert.extension.type)); + x509_certs = X509_STORE_new (); + if (!x509_certs) + { + log_print ("x509_cert_init: creating new X509_STORE failed"); + return 0; + } - if (strcmp (ASN_ID_SUBJECT_ALT_NAME, cert.extension.type)) + if (!x509_read_from_dir (x509_certs, dirname, 1)) { - log_print ("x509_cert_get_subject: extension type != subjectAltName"); - goto fail; + log_print ("x509_cert_init: x509_read_from_dir failed"); + return 0; } - /* - * XXX Evil**3, due to lack of time the IP encoding of subjectAltName - * is supposed to be: 0x30 0x06 0x087 0x04 aa bb cc dd, where the IPV4 - * IP number is aa.bb.cc.dd. - */ - if (asn_get_len (cert.extension.val) != 8 || cert.extension.val[3] != 4) + /* Process CA certificates we will trust. */ + dirname = conf_get_str ("X509-certificates", "CA-directory"); + if (!dirname) { - log_print ("x509_cert_get_subject: " - "subjectAltName uses unhandled encoding"); - goto fail; + log_print ("x509_cert_init: no CA-directory"); + return 0; } - /* XXX IPV4 address. */ - *subject = malloc (sizeof (in_addr_t)); - if (!*subject) + x509_cas = X509_STORE_new (); + if (!x509_cas) { - log_error ("x509_cert_get_subject: malloc (%d) failed", - sizeof (in_addr_t)); - goto fail; + log_print ("x509_cert_init: creating new X509_STORE failed"); + return 0; + } + + if (!x509_read_from_dir (x509_cas, dirname, 0)) + { + log_print ("x509_cert_init: x509_read_from_dir failed"); + return 0; } - *subjectlen = sizeof (in_addr_t); - memcpy (*subject, cert.extension.val + 4, *subjectlen); - x509_free_certificate (&cert); return 1; +} - fail: - x509_free_certificate (&cert); - return 0; +void * +x509_cert_get (u_int8_t *asn, u_int32_t len) +{ + return x509_from_asn (asn, len); } -/* - * Initalizes the struct x509_attribval from a AttributeValueAssertion. - * XXX Error checking. - */ -void -x509_get_attribval (struct norm_type *obj, struct x509_attribval *a) +int +x509_cert_validate (void *scert) { - struct norm_type *tmp; + X509_STORE_CTX csc; + X509_NAME *issuer, *subject; + X509 *cert = (X509 *)scert; + EVP_PKEY *key; + int res; - tmp = asn_decompose ("AttributeValueAssertion.AttributeType", obj); - if (tmp && tmp->data) - a->type = strdup ((char *)tmp->data); + /* + * Validate the peer certificate by checking with the CA certificates we + * trust. + */ + X509_STORE_CTX_init (&csc, x509_cas, cert, NULL); + res = X509_verify_cert (&csc); + X509_STORE_CTX_cleanup (&csc); - tmp = asn_decompose ("AttributeValueAssertion.AttributeValue", obj); - if (tmp && tmp->data) - a->val = strdup ((char *)tmp->data); -} + /* Return if validation succeeded or self-signed certs are not accepted. */ + if (res || !conf_get_str ("X509-certificates", "Accept-self-signed")) + return res; -/* Set OBJ with values from A. XXX Error checking. */ -void -x509_set_attribval (struct norm_type *obj, struct x509_attribval *a) -{ - struct norm_type *tmp; + issuer = X509_get_issuer_name (cert); + subject = X509_get_subject_name (cert); + + if (!issuer || !subject || X509_name_cmp (issuer, subject)) + return 0; + + key = X509_get_pubkey (cert); + if (!key) + return 0; - tmp = asn_decompose ("AttributeValueAssertion.AttributeType", obj); - tmp->data = strdup (a->type); - tmp->len = strlen (tmp->data); - tmp = asn_decompose ("AttributeValueAssertion.AttributeValue", obj); - tmp->type = TAG_PRINTSTRING; - tmp->data = strdup (a->val); - tmp->len = strlen (tmp->data); + if (X509_verify (cert, key) == -1) + return 0; + + return 1; } -void -x509_free_attribval (struct x509_attribval *a) +int +x509_cert_insert (void *scert) { - if (a->type) - free (a->type); - if (a->val) - free (a->val); + X509 *cert = X509_dup ((X509 *)scert); + int res; + + if (!cert) + { + log_print ("x509_cert_insert: X509_dup failed"); + return 0; + } + + res = x509_hash_enter (cert); + if (!res) + X509_free (cert); + + return res; } void -x509_free_certificate (struct x509_certificate *cert) +x509_cert_free (void *cert) { - pkcs_free_public_key (&cert->key); - if (cert->signaturetype) - free (cert->signaturetype); - if (cert->start) - free (cert->start); - if (cert->end) - free (cert->end); - - x509_free_attribval (&cert->issuer1); - x509_free_attribval (&cert->issuer2); - x509_free_attribval (&cert->subject1); - x509_free_attribval (&cert->subject2); - x509_free_attribval (&cert->extension); + X509_free ((X509 *)cert); } +/* Validate the BER Encoding of a RDNSequence in the CERT_REQ payload. */ int -x509_decode_certificate (u_int8_t *asn, u_int32_t asnlen, - struct x509_certificate *rcert) +x509_certreq_validate (u_int8_t *asn, u_int32_t len) { - struct norm_type cert = SEQ ("cert", Certificate); - struct norm_type *tmp; - u_int8_t *data; - u_int32_t datalen; + int res = 1; + /* struct norm_type name = SEQOF ("issuer", RDNSequence); - /* - * Get access to the inner Certificate. - * XXX We don't know how to get at the CA's public key yet. - */ - if (!x509_validate_signed (asn, asnlen, 0, &data, &datalen)) - return 0; - - memset (rcert, 0, sizeof *rcert); + if (!asn_template_clone (&name, 1) + || (asn = asn_decode_sequence (asn, len, &name)) == 0) + { + log_print ("x509_certreq_validate: can not decode 'acceptable CA' info"); + res = 0; + } + asn_free (&name); */ - if (!asn_template_clone (&cert, 1) - || !asn_decode_sequence (data, datalen, &cert)) - goto fail; + /* XXX - not supported directly in SSL - later */ - tmp = asn_decompose ("cert.subjectPublicKeyInfo.subjectPublicKey", &cert); - if (!tmp || !tmp->data) - goto fail; - if (!pkcs_public_key_from_asn (&rcert->key, tmp->data + 1, tmp->len - 1)) - goto fail; - - tmp = asn_decompose ("cert.version", &cert); - if (!tmp || !tmp->data) - goto fail; - rcert->version = mpz_get_ui (tmp->data); + return res; +} - tmp = asn_decompose ("cert.serialNumber", &cert); - if (!tmp || !tmp->data) - goto fail; - rcert->serialnumber = mpz_get_ui (tmp->data); +/* Decode the BER Encoding of a RDNSequence in the CERT_REQ payload. */ +void * +x509_certreq_decode (u_int8_t *asn, u_int32_t len) +{ + /* XXX This needs to be done later. + struct norm_type aca = SEQOF ("aca", RDNSequence); + struct norm_type *tmp; + struct x509_aca naca, *ret; - tmp = asn_decompose ("cert.signature.algorithm", &cert); - if (!tmp || !tmp->data) - goto fail; - rcert->signaturetype = strdup ((char *)tmp->data); + if (!asn_template_clone (&aca, 1) + || (asn = asn_decode_sequence (asn, len, &aca)) == 0) + { + log_print ("x509_certreq_validate: can not decode 'acceptable CA' info"); + goto fail; + } + memset (&naca, 0, sizeof (naca)); - tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName." - "AttributeValueAssertion", &cert); + tmp = asn_decompose ("aca.RelativeDistinguishedName.AttributeValueAssertion", + &aca); if (!tmp) goto fail; - x509_get_attribval (tmp, &rcert->issuer1); + x509_get_attribval (tmp, &naca.name1); - tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName[1]." - "AttributeValueAssertion", &cert); + tmp = asn_decompose ("aca.RelativeDistinguishedName[1]" + ".AttributeValueAssertion", &aca); if (tmp) - x509_get_attribval (tmp, &rcert->issuer2); - else - rcert->issuer2.type = 0; - - tmp = asn_decompose ("cert.subject.RelativeDistinguishedName." - "AttributeValueAssertion", &cert); - if (!tmp) - goto fail; - x509_get_attribval (tmp, &rcert->subject1); + x509_get_attribval (tmp, &naca.name2); + + asn_free (&aca); - tmp = asn_decompose ("cert.subject.RelativeDistinguishedName[1]." - "AttributeValueAssertion", &cert); - if (tmp) - x509_get_attribval (tmp, &rcert->subject2); + ret = malloc (sizeof (struct x509_aca)); + if (ret) + memcpy (ret, &naca, sizeof (struct x509_aca)); else - rcert->subject2.type = 0; - - tmp = asn_decompose ("cert.validity.notBefore", &cert); - if (!tmp || !tmp->data) - goto fail; - rcert->start = strdup ((char *)tmp->data); - if (!rcert->start) { - log_error ("x509_decode_certificate: strdup(\"%s\") failed", tmp->data); - goto fail; - } - - tmp = asn_decompose ("cert.validity.notAfter", &cert); - if (!tmp || !tmp->data) - goto fail; - rcert->end = strdup ((char *)tmp->data); - if (!rcert->end) - { - log_error ("x509_decode_certificate: strdup(\"%s\") failed", tmp->data); - goto fail; + log_error ("x509_certreq_decode: malloc (%d) failed", + sizeof (struct x509_aca)); + x509_free_aca (&aca); } - /* For x509v3 there might be an extension, try to decode it. */ - tmp = asn_decompose ("cert.extension", &cert); - if (tmp && tmp->data && rcert->version == 2) - x509_decode_cert_extension (tmp->data, tmp->len, rcert); - - asn_free (&cert); - return 1; + return ret; fail: - x509_free_certificate (rcert); - asn_free (&cert); + asn_free (&aca); */ return 0; } -int -x509_encode_certificate (struct x509_certificate *rcert, - u_int8_t **asn, u_int32_t *asnlen) +void +x509_free_aca (void *blob) { - struct norm_type cert = SEQ ("cert", Certificate); - struct norm_type *tmp; - u_int8_t *data, *new_buf; - mpz_t num; - u_int8_t *tmpasn; - u_int32_t tmpasnlen; - - - if (!asn_template_clone (&cert, 1)) - goto fail; + struct x509_aca *aca = blob; - if (rcert->extension.type && rcert->extension.val) - { - tmp = asn_decompose ("cert.extension", &cert); - if (x509_encode_cert_extension (rcert, &tmpasn, &tmpasnlen)) - { - tmp->data = tmpasn; - tmp->len = tmpasnlen; - } - } + if (aca->name1.type) + free (aca->name1.type); + if (aca->name1.val) + free (aca->name1.val); - tmp = asn_decompose ("cert.subjectPublicKeyInfo.algorithm.parameters", - &cert); - tmp->type = TAG_NULL; - tmp = asn_decompose ("cert.subjectPublicKeyInfo.algorithm.algorithm", - &cert); - tmp->data = strdup (ASN_ID_RSAENCRYPTION); - tmp->len = strlen (tmp->data); + if (aca->name2.type) + free (aca->name2.type); + if (aca->name2.val) + free (aca->name2.val); +} - tmp = asn_decompose ("cert.subjectPublicKeyInfo.subjectPublicKey", &cert); - data = pkcs_public_key_to_asn (&rcert->key); - if (!data) - goto fail; +X509 * +x509_from_asn (u_char *asn, u_int len) +{ + BIO *certh; + X509 *scert = NULL; - /* This is a BITSTRING, add 0 octet for padding. */ - tmp->len = asn_get_len (data); - new_buf = realloc (data, tmp->len + 1); - if (!new_buf) + certh = BIO_new (BIO_s_mem ()); + if (!certh) { - log_error ("x509_encode_certificate: realloc (%p, %d) failed", data, - tmp->len + 1); - free (data); - goto fail; + log_error ("X509_from_asn: BIO_new (BIO_s_mem ()) failed"); + return NULL; } - data = new_buf; - memmove (data + 1, data, tmp->len); - data[0] = 0; - tmp->data = data; - tmp->len++; - - mpz_init (num); - tmp = asn_decompose ("cert.version", &cert); - mpz_set_ui (num, rcert->version); - if (!pkcs_mpz_to_norm_type (tmp, num)) + + if (BIO_write (certh, asn, len) == -1) { - mpz_clear (num); - goto fail; + log_error ("X509_from_asn: BIO_write failed\n"); + goto end; } - tmp = asn_decompose ("cert.serialNumber", &cert); - mpz_set_ui (num, rcert->serialnumber); - if (!pkcs_mpz_to_norm_type (tmp, num)) + scert = d2i_X509_bio (certh, NULL); + if (!scert) { - mpz_clear (num); - goto fail; + log_print ("X509_from_asn: d2i_X509_bio failed\n"); + goto end; } - mpz_clear (num); - - tmp = asn_decompose ("cert.signature.parameters", &cert); - tmp->type = TAG_NULL; - tmp = asn_decompose ("cert.signature.algorithm", &cert); - tmp->data = strdup (rcert->signaturetype); - tmp->len = strlen ((char *)tmp->data); - - tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName." - "AttributeValueAssertion", &cert); - x509_set_attribval (tmp, &rcert->issuer1); - tmp = asn_decompose ("cert.issuer.RelativeDistinguishedName[1]." - "AttributeValueAssertion", &cert); - x509_set_attribval (tmp, &rcert->issuer2); - - tmp = asn_decompose ("cert.subject.RelativeDistinguishedName." - "AttributeValueAssertion", &cert); - x509_set_attribval (tmp, &rcert->subject1); - tmp = asn_decompose ("cert.subject.RelativeDistinguishedName[1]." - "AttributeValueAssertion", &cert); - x509_set_attribval (tmp, &rcert->subject2); - - tmp = asn_decompose ("cert.validity.notBefore", &cert); - tmp->data = strdup (rcert->start); - tmp->len = strlen ((char *)tmp->data); - - tmp = asn_decompose ("cert.validity.notAfter", &cert); - tmp->data = strdup (rcert->end); - tmp->len = strlen ((char *)tmp->data); - - *asn = asn_encode_sequence (&cert, 0); - if (!*asn) - goto fail; - - *asnlen = asn_get_len (*asn); - asn_free (&cert); - return 1; - - fail: - asn_free (&cert); - return 0; + end: + BIO_free (certh); + return scert; } /* - * Decode an Extension to a X509 certificate. - * XXX We ignore the critical boolean. + * Check that a certificate has a subjectAltName and that it matches our ID. */ - int -x509_decode_cert_extension (u_int8_t *asn, u_int32_t asnlen, - struct x509_certificate *cert) +x509_check_subjectaltname (u_char *id, u_int id_len, X509 *scert) { - struct norm_type *tmp; - struct norm_type ex = SEQOF ("ex", Extensions); + u_int8_t *altname; + u_int32_t altlen; + int type, idtype, ret; - /* Implicit tagging for extension. */ - ex.class = ADD_EXP (3, UNIVERSAL); - - if (!asn_template_clone (&ex, 1) || !asn_decode_sequence (asn, asnlen, &ex)) + type = x509_cert_subjectaltname (scert, &altname, &altlen); + if (!type) { - asn_free (&ex); + log_print ("x509_check_subjectaltname: can't access subjectAltName"); return 0; } - tmp = asn_decompose ("ex.extension.extnValue", &ex); - if (!tmp || !tmp->data || asn_get_len (tmp->data) != tmp->len) - goto fail; - cert->extension.val = malloc (tmp->len); - if (cert->extension.val == 0) - goto fail; - memcpy (cert->extension.val, tmp->data, tmp->len); + /* + * Now that we have the X509 certicate in native form, get the + * subjectAltName extension and verify that it matches our ID. + */ - tmp = asn_decompose ("ex.extension.extnId", &ex); - if (!tmp || !tmp->data) - goto fail; - cert->extension.type = strdup (tmp->data); - if (!cert->extension.type) + /* XXX Get type of ID. */ + idtype = id[0]; + id += ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ; + id_len -= ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ; + + ret = 0; + switch (idtype) { - free (cert->extension.val); - cert->extension.val = 0; - goto fail; + case IPSEC_ID_IPV4_ADDR: + if (type == X509v3_IPV4_ADDR) + ret = 1; + break; + case IPSEC_ID_FQDN: + if (type == X509v3_DNS_NAME) + ret = 1; + break; + case IPSEC_ID_USER_FQDN: + if (type == X509v3_RFC_NAME) + ret = 1; + break; + default: + ret = 0; + break; } - asn_free (&ex); - return 1; + if (!ret) + { + log_debug (LOG_CRYPTO, 50, + "X509_check_subjectaltname: " + "our ID type does not match X509 cert ID type"); + return 0; + } - fail: - asn_free (&ex); - return 0; + if (altlen != id_len || memcmp (altname, id, id_len) != 0) + { + log_debug (LOG_CRYPTO, 50, + "X509_check_subjectaltname: " + "our ID does not match X509 cert ID"); + return 0; + } + + return 1; } -/* - * Encode a Cert Extension. - * XXX Only one extension per certificate. - * XXX We tag everything as critical. +/* + * Obtain a certificate from an acceptable CA. + * XXX We don't check if the certificate we find is from an accepted CA. */ int -x509_encode_cert_extension (struct x509_certificate *cert, - u_int8_t **asn, u_int32_t *asnlen) +x509_cert_obtain (u_int8_t *id, size_t id_len, void *data, u_int8_t **cert, + u_int32_t *certlen) { - struct norm_type ex = SEQ ("ex", Extensions); - struct norm_type *tmp; - ex.class = ADD_EXP (3, UNIVERSAL); - - if (!asn_template_clone (&ex ,1)) - goto fail; + struct x509_aca *aca = data; + X509 *scert; + u_char *p; - tmp = asn_decompose ("ex.extension.extnId", &ex); - tmp->data = strdup (cert->extension.type); - tmp->len = strlen (tmp->data); + if (aca) + log_debug (LOG_CRYPTO, 60, + "x509_cert_obtain: acceptable certificate authorities here"); - /* XXX We mark every extension as critical. */ - tmp = asn_decompose ("ex.extension.critical", &ex); - tmp->data = malloc (1); - if (!tmp->data) + /* We need our ID to find a certificate. */ + if (!id) { - log_error ("x509_encode_cert_extension: malloc (1) failed"); - goto fail; + log_print ("X509_cert_obtain: ID is missing"); + return 0; } - *(u_int8_t *)tmp->data = 0xff; - tmp->len = 1; - tmp = asn_decompose ("ex.extension.extnValue", &ex); - tmp->data = malloc (asn_get_len (cert->extension.val)); - if (!tmp->data) + scert = x509_hash_find (id, id_len); + if (!scert) + return 0; + + if (!x509_check_subjectaltname (id, id_len, scert)) { - log_error ("x509_encode_cert_extension: malloc (%d) failed", - asn_get_len (cert->extension.val)); - goto fail; + log_print ("X509_cert_obtain: subjectAltName does not match id"); + free (*cert); + return 0; } - tmp->len = asn_get_len (cert->extension.val); - memcpy (tmp->data, cert->extension.val, tmp->len); - *asn = asn_encode_sequence (&ex, 0); - if (!*asn) - goto fail; + *certlen = i2d_X509 (scert, NULL); + p = *cert = malloc (*certlen); + if (!p) + { + log_error ("X509_cert_obtain: malloc (%d) failed", *certlen); + return 0; + } + *certlen = i2d_X509 (scert, &p); - *asnlen = asn_get_len (*asn); - - asn_free (&ex); return 1; - fail: - asn_free (&ex); - return 0; } -/* - * Checks the signature on an ASN.1 Signed Type. If the passed KEY is - * NULL we just unwrap the inner object and return it. - */ +/* Returns a pointer to the subjectAltName information of X509 certificate. */ int -x509_validate_signed (u_int8_t *asn, u_int32_t asnlen, - struct rsa_public_key *key, u_int8_t **data, - u_int32_t *datalen) +x509_cert_subjectaltname (X509 *scert, u_int8_t **altname, u_int32_t *len) { - struct norm_type sig = SEQ ("signed", Signed); - struct norm_type digest = SEQ ("digest", DigestInfo); - struct norm_type *tmp; - struct hash *hash = 0; - int res; - u_int8_t *dec; - u_int16_t declen; - char *id; + X509_EXTENSION *subjectaltname; + u_int8_t *sandata; + int extpos; + int santype, sanlen; - if (!asn_template_clone (&sig, 1)) - /* Failed, probably memory allocation, free what we got anyway. */ - goto fail; - - if (!asn_decode_sequence (asn, asnlen, &sig)) + extpos = X509_get_ext_by_NID (scert, NID_subject_alt_name, -1); + if (extpos == -1) { - log_print ("x509_validate_signed: input data could not be decoded"); - goto fail; + log_print ("X509_cert_subjectaltname: " + "certificate does not contain subjectAltName"); + return 0; } - tmp = asn_decompose ("signed.algorithm.algorithm", &sig); + subjectaltname = X509_get_ext (scert, extpos); - if (strcmp ((char *)tmp->data, ASN_ID_MD5WITHRSAENC) == 0) - hash = hash_get (HASH_MD5); - else + if (!subjectaltname || !subjectaltname->value + || !subjectaltname->value->data || subjectaltname->value->length < 4) { - id = asn_parse_objectid (asn_ids, tmp->data); - log_print ("x509_validate_signed: can not handle SigType %s", - id ? id : tmp->data); - goto fail; + log_print ("X509_check_subjectaltname: " + "invalid subjectaltname extension"); + return 0; } - if (!hash) - goto fail; - - tmp = asn_decompose ("signed.data", &sig); - - /* Hash the data. */ - hash->Init (hash->ctx); - hash->Update (hash->ctx, tmp->data, tmp->len); - hash->Final (hash->digest, hash->ctx); - - *data = tmp->data; - *datalen = tmp->len; + /* SSL does not handle unknown ASN stuff well, do it by hand. */ + sandata = subjectaltname->value->data; + santype = sandata[2] & 0x3f; + sanlen = sandata[3]; + sandata += 4; - /* Used to unwrap the SIGNED object around the Certificate. */ - if (!key) + if (sanlen + 4 != subjectaltname->value->length) { - asn_free (&sig); - return 1; + log_print ("X509_check_subjectaltname: subjectaltname invalid length"); + return 0; } + + *len = sanlen; + *altname = sandata; - tmp = asn_decompose ("signed.encrypted", &sig); + return santype; +} - /* - * tmp->data is a BITSTRING, the first octet in the BITSTRING gives - * the padding bits at the end. Per definition there are no padding - * bits at the end in this case, so just skip it. - */ - if (!pkcs_rsa_decrypt (PKCS_PRIVATE, key, 0, tmp->data + 1, &dec, &declen)) - goto fail; +int +x509_cert_get_subject (void *scert, u_int8_t **id, u_int32_t *id_len) +{ + X509 *cert = scert; + int type; + u_int8_t *altname; + u_int32_t altlen; - if (!asn_template_clone (&digest, 1) - || !asn_decode_sequence (dec, declen, &digest)) - { - asn_free (&digest); - goto fail; - } - tmp = asn_decompose ("digest.digestAlgorithm.algorithm", &digest); - if (strcmp (ASN_ID_MD5, (char *)tmp->data)) - { - log_print ("x509_validate_signed: DigestAlgorithm is not MD5"); - res = 0; - } - else + type = x509_cert_subjectaltname (cert, &altname, &altlen); + + switch (type) { - tmp = asn_decompose ("digest.digest", &digest); - if (tmp->len != hash->hashsize - || memcmp (tmp->data, hash->digest, tmp->len)) - { - log_print ("x509_validate_signed: Digest does not match Data"); - res = 0; - } - else - res = 1; + case X509v3_IPV4_ADDR: + { + char buf[ISAKMP_ID_DATA_OFF + 4]; + + /* XXX sizeof IPV4_ADDR, how any better? */ + if (altlen != 4) + { + log_print ("x509_cert_get_subject: length != IP4addr: %d", + altlen); + return 0; + } + + SET_ISAKMP_ID_TYPE (buf, IPSEC_ID_IPV4_ADDR); + SET_IPSEC_ID_PROTO (buf + ISAKMP_ID_DOI_DATA_OFF, 0); + SET_IPSEC_ID_PORT (buf + ISAKMP_ID_DOI_DATA_OFF, 0); + memcpy (buf + ISAKMP_ID_DATA_OFF, altname, altlen); + + *id_len = ISAKMP_ID_DATA_OFF + 4 - ISAKMP_GEN_SZ; + *id = malloc (*id_len); + if (!*id) + { + log_print ("x509_cert_get_subject: malloc (%d) failed", *id_len); + return 0; + } + memcpy (*id, buf + ISAKMP_GEN_SZ, *id_len); + } + break; + default: + log_print ("x509_cert_get_subject: unsupported subjectAltName type: %d", + type); + return 0; } - asn_free (&digest); - asn_free (&sig); - return res; - - fail: - asn_free (&sig); - return 0; + return 1; } -/* - * Create an ASN Signed Structure from the data passed in data - * and return the result in asn. - * At the moment the used hash is MD5, this is the only common - * hash between us and X509. - */ int -x509_create_signed (u_int8_t *data, u_int32_t datalen, - struct rsa_private_key *key, u_int8_t **asn, - u_int32_t *asnlen) +x509_cert_get_key (void *scert, void *keyp) { - struct norm_type digest = SEQ ("digest", DigestInfo); - struct norm_type sig = SEQ ("signed", Signed); - struct norm_type *tmp; - struct hash *hash; - u_int8_t *diginfo, *enc; - u_int32_t enclen; - int res = 0; - - /* Hash the Data. */ - hash = hash_get (HASH_MD5); - hash->Init (hash->ctx); - hash->Update (hash->ctx, data, datalen); - hash->Final (hash->digest, hash->ctx); + X509 *cert = scert; + EVP_PKEY *key; - if (!asn_template_clone (&digest, 1)) - goto fail; + key = X509_get_pubkey (cert); - tmp = asn_decompose ("digest.digest", &digest); - tmp->len = hash->hashsize; - tmp->data = malloc (hash->hashsize); - if (!tmp->data) + /* Check if we got the right key type */ + if (key->type != EVP_PKEY_RSA) { - log_error ("x509_create_signed: malloc (%d) failed", hash->hashsize); - goto fail; + log_print ("x509_cert_get_key: public key is not a RSA key"); + X509_free (cert); + return 0; } - memcpy (tmp->data, hash->digest, hash->hashsize); - tmp = asn_decompose ("digest.digestAlgorithm.parameters", &digest); - tmp->type = TAG_NULL; - tmp = asn_decompose ("digest.digestAlgorithm.algorithm", &digest); - tmp->data = strdup (ASN_ID_MD5); - tmp->len = strlen (tmp->data); + *(RSA **)keyp = RSAPublicKey_dup (key->pkey.rsa); - /* ASN encode Digest Information. */ - diginfo = asn_encode_sequence (&digest, 0); - if (!diginfo) - goto fail; - - /* Encrypt the Digest Info with Private Key. */ - res = pkcs_rsa_encrypt (PKCS_PRIVATE, 0, key, diginfo, asn_get_len (diginfo), - &enc, &enclen); - free (diginfo); - if (!res) - goto fail; - res = 0; - - if (!asn_template_clone (&sig, 1)) - goto fail2; - - tmp = asn_decompose ("signed.algorithm.parameters", &sig); - tmp->type = TAG_NULL; - tmp = asn_decompose ("signed.algorithm.algorithm", &sig); - tmp->data = strdup (ASN_ID_MD5WITHRSAENC); - tmp->len = strlen (tmp->data); - - /* The type is BITSTRING, i.e. first octet need to be zero. */ - tmp = asn_decompose ("signed.encrypted", &sig); - tmp->data = malloc (enclen + 1); - if (!tmp->data) - { - log_error ("x509_create_signed: malloc (%d) failed", enclen + 1); - free (enc); - goto fail2; - } - tmp->len = enclen + 1; - memcpy (tmp->data + 1, enc, enclen); - *(char *)tmp->data = 0; - free (enc); - - tmp = asn_decompose ("signed.data", &sig); - tmp->data = data; - tmp->len = datalen; - - *asn = asn_encode_sequence (&sig, 0); - if (!*asn) - goto fail2; - *asnlen = asn_get_len (*asn); - - /* This is the data we have been given, we can not free it in asn_free. */ - tmp->data = 0; - res = 1; /* Successful. */ - - fail2: - asn_free (&sig); - fail: - asn_free (&digest); - return res; + return *(RSA **)keyp == NULL ? 0 : 1; } diff --git a/sbin/isakmpd/x509.h b/sbin/isakmpd/x509.h index a0c92cf95d2..3325b27fcda 100644 --- a/sbin/isakmpd/x509.h +++ b/sbin/isakmpd/x509.h @@ -1,8 +1,8 @@ -/* $OpenBSD: x509.h,v 1.3 1998/11/17 11:10:22 niklas Exp $ */ -/* $EOM: x509.h,v 1.5 1998/08/21 13:47:54 provos Exp $ */ +/* $OpenBSD: x509.h,v 1.4 1999/07/17 21:54:39 niklas Exp $ */ +/* $EOM: x509.h,v 1.6 1999/07/17 20:44:12 niklas Exp $ */ /* - * Copyright (c) 1998 Niels Provos. All rights reserved. + * Copyright (c) 1998, 1999 Niels Provos. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,7 +37,10 @@ #ifndef _X509_H_ #define _X509_H_ -#include "pkcs.h" /* for struct rsa_public_key */ +#define X509v3_RFC_NAME 1 +#define X509v3_DNS_NAME 2 +#define X509v3_IPV4_ADDR 7 + struct x509_attribval { char *type; @@ -45,9 +48,9 @@ struct x509_attribval { }; /* - * The acceptable certification authority - * XXX we only support two names at the moment, as of ASN this can - * be dynamic but we dont care for now. + * The acceptable certification authority. + * XXX We only support two names at the moment, as of ASN this can + * be dynamic but we don't care for now. */ struct x509_aca { @@ -55,45 +58,28 @@ struct x509_aca { struct x509_attribval name2; }; -struct exchange; +struct X509; -struct x509_certificate { - u_int32_t version; - u_int32_t serialnumber; - char *signaturetype; - struct x509_attribval issuer1; /* At the moment Country */ - struct x509_attribval issuer2; /* At the moment Organization */ - struct x509_attribval subject1; /* At the moment Country */ - struct x509_attribval subject2; /* At the moment Organization */ - struct x509_attribval extension; /* Raw Extension */ - char *start; /* Certificate Validity Start and End */ - char *end; - struct rsa_public_key key; -}; +/* Functions provided by cert handler. */ +int x509_cert_init (void); +void *x509_cert_get (u_int8_t *, u_int32_t); +int x509_cert_validate (void *); +void x509_cert_free (void *); int x509_certreq_validate (u_int8_t *, u_int32_t); void *x509_certreq_decode (u_int8_t *, u_int32_t); void x509_free_aca (void *); -int x509_cert_obtain (struct exchange *, void *, u_int8_t **, u_int32_t *); -int x509_cert_get_key (u_int8_t *, u_int32_t, void *); -int x509_cert_get_subject (u_int8_t *, u_int32_t, u_int8_t **, u_int32_t *); +int x509_cert_obtain (u_int8_t *, size_t, void *, u_int8_t **, u_int32_t *); +int x509_cert_get_key (void *, void *); +int x509_cert_get_subject (void *, u_int8_t **, u_int32_t *); -void x509_get_attribval (struct norm_type *, struct x509_attribval *); -void x509_set_attribval (struct norm_type *, struct x509_attribval *); -void x509_free_attrbival (struct x509_attribval *); +/* Misc. X509 certificate functions. */ -int x509_validate_signed (u_int8_t *, u_int32_t, struct rsa_public_key *, - u_int8_t **, u_int32_t *); -int x509_create_signed (u_int8_t *, u_int32_t, struct rsa_private_key *, - u_int8_t **, u_int32_t *); -int x509_decode_certificate (u_int8_t *, u_int32_t, struct x509_certificate *); -int x509_encode_certificate (struct x509_certificate *, u_int8_t **, - u_int32_t *); -void x509_free_certificate (struct x509_certificate *); +int x509_cert_insert (void *); +int x509_read_from_dir (X509_STORE *, char *, int); -int x509_decode_cert_extension (u_int8_t *, u_int32_t, - struct x509_certificate *); -int x509_encode_cert_extension (struct x509_certificate *, u_int8_t **, - u_int32_t *); +int x509_cert_subjectaltname (X509 *cert, u_char **, u_int *); +int x509_check_subjectaltname (u_char *, u_int, X509 *); +X509 *x509_from_asn (u_char *, u_int); #endif /* _X509_H_ */ |