summaryrefslogtreecommitdiff
path: root/sbin/iked
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2017-03-27 10:06:42 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2017-03-27 10:06:42 +0000
commite3affe111fdf78b3b570254f4a8bcac92e8e2264 (patch)
tree73ccda4a2c611b5451af51e88ec416c476c3f7fd /sbin/iked
parent650ec76b079154b185c886970968e2cc614b4e65 (diff)
Add support for RFC4754 (ECDSA) and RFC7427 authentication.
These modes provide stronger and more flexible ways for authentication: while RSA public key auth relies on SHA-1 hashes, the news modes use SHA2-256 and up to SHA2-512 hashes. Original diff from markus@ with patches from mikeb@ and me. OK mikeb@ patrick@
Diffstat (limited to 'sbin/iked')
-rw-r--r--sbin/iked/ca.c150
-rw-r--r--sbin/iked/crypto.c263
-rw-r--r--sbin/iked/iked.87
-rw-r--r--sbin/iked/iked.conf.529
-rw-r--r--sbin/iked/iked.h3
-rw-r--r--sbin/iked/ikev2.c49
-rw-r--r--sbin/iked/ikev2.h14
-rw-r--r--sbin/iked/ikev2_msg.c15
-rw-r--r--sbin/iked/parse.y223
9 files changed, 641 insertions, 112 deletions
diff --git a/sbin/iked/ca.c b/sbin/iked/ca.c
index 9fe18d75df0..7ecd75143f0 100644
--- a/sbin/iked/ca.c
+++ b/sbin/iked/ca.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ca.c,v 1.42 2017/01/20 14:08:08 mikeb Exp $ */
+/* $OpenBSD: ca.c,v 1.43 2017/03/27 10:06:41 reyk Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -61,6 +61,7 @@ int ca_validate_pubkey(struct iked *, struct iked_static_id *,
void *, size_t);
int ca_validate_cert(struct iked *, struct iked_static_id *,
void *, size_t);
+int ca_privkey_to_method(struct iked_id *);
struct ibuf *
ca_x509_serialize(X509 *);
int ca_x509_subjectaltname_cmp(X509 *, struct iked_static_id *);
@@ -82,6 +83,8 @@ struct ca_store {
struct iked_id ca_privkey;
struct iked_id ca_pubkey;
+
+ uint8_t ca_privkey_method;
};
pid_t
@@ -125,6 +128,10 @@ ca_getkey(struct privsep *ps, struct iked_id *key, enum imsg_type type)
if (type == IMSG_PRIVKEY) {
name = "private";
id = &store->ca_privkey;
+
+ store->ca_privkey_method = ca_privkey_to_method(key);
+ if (store->ca_privkey_method == IKEV2_AUTH_NONE)
+ fatalx("ca: failed to get auth method for privkey");
} else if (type == IMSG_PUBKEY) {
name = "public";
id = &store->ca_pubkey;
@@ -323,10 +330,18 @@ ca_setauth(struct iked *env, struct iked_sa *sa,
struct iked_policy *policy = sa->sa_policy;
uint8_t type = policy->pol_auth.auth_method;
- /* switch encoding to IKEV2_AUTH_SIG if SHA2 is supported */
- if (sa->sa_sigsha2 && type == IKEV2_AUTH_RSA_SIG) {
- log_debug("%s: switching from RSA_SIG to SIG", __func__);
- type = IKEV2_AUTH_SIG;
+ if (id == PROC_CERT) {
+ /* switch encoding to IKEV2_AUTH_SIG if SHA2 is supported */
+ if (sa->sa_sigsha2 && type == IKEV2_AUTH_RSA_SIG) {
+ log_debug("%s: switching RSA_SIG to SIG", __func__);
+ type = IKEV2_AUTH_SIG;
+ } else if (!sa->sa_sigsha2 && type == IKEV2_AUTH_SIG_ANY) {
+ log_debug("%s: switching SIG to RSA_SIG(*)", __func__);
+ /* XXX ca might auto-switch to ECDSA */
+ type = IKEV2_AUTH_RSA_SIG;
+ } else if (type == IKEV2_AUTH_SIG) {
+ log_debug("%s: using SIG (RFC7427)", __func__);
+ }
}
if (type == IKEV2_AUTH_SHARED_KEY_MIC) {
@@ -389,6 +404,7 @@ ca_getcert(struct iked *env, struct imsg *imsg)
}
break;
case IKEV2_CERT_RSA_KEY:
+ case IKEV2_CERT_ECDSA:
ret = ca_validate_pubkey(env, &id, ptr, len);
break;
default:
@@ -439,6 +455,7 @@ ca_getreq(struct iked *env, struct imsg *imsg)
switch (type) {
case IKEV2_CERT_RSA_KEY:
+ case IKEV2_CERT_ECDSA:
if (store->ca_pubkey.id_type != type ||
(buf = store->ca_pubkey.id_buf) == NULL)
return (-1);
@@ -525,12 +542,13 @@ ca_getauth(struct iked *env, struct imsg *imsg)
bzero(&policy, sizeof(policy));
memcpy(&sa.sa_hdr, &sh, sizeof(sh));
sa.sa_policy = &policy;
- policy.pol_auth.auth_method = method;
if (sh.sh_initiator)
id = &sa.sa_icert;
else
id = &sa.sa_rcert;
memcpy(id, &store->ca_privkey, sizeof(*id));
+ policy.pol_auth.auth_method = method == IKEV2_AUTH_SIG ?
+ method : store->ca_privkey_method;
if (ikev2_msg_authsign(env, &sa, &policy.pol_auth, authmsg) != 0) {
log_debug("%s: AUTH sign failed", __func__);
@@ -833,6 +851,7 @@ int
ca_pubkey_serialize(EVP_PKEY *key, struct iked_id *id)
{
RSA *rsa = NULL;
+ EC_KEY *ec = NULL;
uint8_t *d;
int len = 0;
int ret = -1;
@@ -842,6 +861,7 @@ ca_pubkey_serialize(EVP_PKEY *key, struct iked_id *id)
id->id_type = 0;
id->id_offset = 0;
ibuf_release(id->id_buf);
+ id->id_buf = NULL;
if ((rsa = EVP_PKEY_get1_RSA(key)) == NULL)
goto done;
@@ -853,11 +873,34 @@ ca_pubkey_serialize(EVP_PKEY *key, struct iked_id *id)
d = ibuf_data(id->id_buf);
if (i2d_RSAPublicKey(rsa, &d) != len) {
ibuf_release(id->id_buf);
+ id->id_buf = NULL;
goto done;
}
id->id_type = IKEV2_CERT_RSA_KEY;
break;
+ case EVP_PKEY_EC:
+ id->id_type = 0;
+ id->id_offset = 0;
+ ibuf_release(id->id_buf);
+ id->id_buf = NULL;
+
+ if ((ec = EVP_PKEY_get1_EC_KEY(key)) == NULL)
+ goto done;
+ if ((len = i2d_EC_PUBKEY(ec, NULL)) <= 0)
+ goto done;
+ if ((id->id_buf = ibuf_new(NULL, len)) == NULL)
+ goto done;
+
+ d = ibuf_data(id->id_buf);
+ if (i2d_EC_PUBKEY(ec, &d) != len) {
+ ibuf_release(id->id_buf);
+ id->id_buf = NULL;
+ goto done;
+ }
+
+ id->id_type = IKEV2_CERT_ECDSA;
+ break;
default:
log_debug("%s: unsupported key type %d", __func__, key->type);
return (-1);
@@ -870,6 +913,8 @@ ca_pubkey_serialize(EVP_PKEY *key, struct iked_id *id)
done:
if (rsa != NULL)
RSA_free(rsa);
+ if (ec != NULL)
+ EC_KEY_free(ec);
return (ret);
}
@@ -877,6 +922,7 @@ int
ca_privkey_serialize(EVP_PKEY *key, struct iked_id *id)
{
RSA *rsa = NULL;
+ EC_KEY *ec = NULL;
uint8_t *d;
int len = 0;
int ret = -1;
@@ -886,6 +932,7 @@ ca_privkey_serialize(EVP_PKEY *key, struct iked_id *id)
id->id_type = 0;
id->id_offset = 0;
ibuf_release(id->id_buf);
+ id->id_buf = NULL;
if ((rsa = EVP_PKEY_get1_RSA(key)) == NULL)
goto done;
@@ -897,11 +944,34 @@ ca_privkey_serialize(EVP_PKEY *key, struct iked_id *id)
d = ibuf_data(id->id_buf);
if (i2d_RSAPrivateKey(rsa, &d) != len) {
ibuf_release(id->id_buf);
+ id->id_buf = NULL;
goto done;
}
id->id_type = IKEV2_CERT_RSA_KEY;
break;
+ case EVP_PKEY_EC:
+ id->id_type = 0;
+ id->id_offset = 0;
+ ibuf_release(id->id_buf);
+ id->id_buf = NULL;
+
+ if ((ec = EVP_PKEY_get1_EC_KEY(key)) == NULL)
+ goto done;
+ if ((len = i2d_ECPrivateKey(ec, NULL)) <= 0)
+ goto done;
+ if ((id->id_buf = ibuf_new(NULL, len)) == NULL)
+ goto done;
+
+ d = ibuf_data(id->id_buf);
+ if (i2d_ECPrivateKey(ec, &d) != len) {
+ ibuf_release(id->id_buf);
+ id->id_buf = NULL;
+ goto done;
+ }
+
+ id->id_type = IKEV2_CERT_ECDSA;
+ break;
default:
log_debug("%s: unsupported key type %d", __func__, key->type);
return (-1);
@@ -914,9 +984,57 @@ ca_privkey_serialize(EVP_PKEY *key, struct iked_id *id)
done:
if (rsa != NULL)
RSA_free(rsa);
+ if (ec != NULL)
+ EC_KEY_free(ec);
return (ret);
}
+int
+ca_privkey_to_method(struct iked_id *privkey)
+{
+ BIO *rawcert = NULL;
+ EC_KEY *ec = NULL;
+ const EC_GROUP *group = NULL;
+ uint8_t method = IKEV2_AUTH_NONE;
+
+ switch (privkey->id_type) {
+ case IKEV2_CERT_RSA_KEY:
+ method = IKEV2_AUTH_RSA_SIG;
+ break;
+ case IKEV2_CERT_ECDSA:
+ if ((rawcert = BIO_new_mem_buf(ibuf_data(privkey->id_buf),
+ ibuf_length(privkey->id_buf))) == NULL)
+ goto out;
+ if ((ec = d2i_ECPrivateKey_bio(rawcert, NULL)) == NULL)
+ goto out;
+ if ((group = EC_KEY_get0_group(ec)) == NULL)
+ goto out;
+ switch (EC_GROUP_get_degree(group)) {
+ case 256:
+ method = IKEV2_AUTH_ECDSA_256;
+ break;
+ case 384:
+ method = IKEV2_AUTH_ECDSA_384;
+ break;
+ case 521:
+ method = IKEV2_AUTH_ECDSA_521;
+ break;
+ }
+ }
+
+ log_debug("%s: type %s method %s", __func__,
+ print_map(privkey->id_type, ikev2_cert_map),
+ print_map(method, ikev2_auth_map));
+
+ out:
+ if (ec != NULL)
+ EC_KEY_free(ec);
+ if (rawcert != NULL)
+ BIO_free(rawcert);
+
+ return (method);
+}
+
char *
ca_asn1_name(uint8_t *asn1, size_t len)
{
@@ -1047,6 +1165,7 @@ ca_validate_pubkey(struct iked *env, struct iked_static_id *id,
{
BIO *rawcert = NULL;
RSA *peerrsa = NULL, *localrsa = NULL;
+ EC_KEY *peerec = NULL;
EVP_PKEY *peerkey = NULL, *localkey = NULL;
int ret = -1;
FILE *fp = NULL;
@@ -1084,12 +1203,21 @@ ca_validate_pubkey(struct iked *env, struct iked_static_id *id,
if ((rawcert = BIO_new_mem_buf(data, len)) == NULL)
goto done;
- if ((peerrsa = d2i_RSAPublicKey_bio(rawcert, NULL)) == NULL)
- goto sslerr;
if ((peerkey = EVP_PKEY_new()) == NULL)
goto sslerr;
- if (!EVP_PKEY_set1_RSA(peerkey, peerrsa))
+ if ((peerrsa = d2i_RSAPublicKey_bio(rawcert, NULL))) {
+ if (!EVP_PKEY_set1_RSA(peerkey, peerrsa)) {
+ goto sslerr;
+ }
+ } else if (BIO_reset(rawcert) == 1 &&
+ (peerec = d2i_EC_PUBKEY_bio(rawcert, NULL))) {
+ if (!EVP_PKEY_set1_EC_KEY(peerkey, peerec)) {
+ goto sslerr;
+ }
+ } else {
+ log_debug("%s: unknown key type received", __func__);
goto sslerr;
+ }
}
lc_string(idstr);
@@ -1101,7 +1229,7 @@ ca_validate_pubkey(struct iked *env, struct iked_static_id *id,
goto done;
localkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
if (localkey == NULL) {
- /* reading PKCS #8 failed, try PEM */
+ /* reading PKCS #8 failed, try PEM RSA */
rewind(fp);
localrsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
fclose(fp);
@@ -1134,6 +1262,8 @@ ca_validate_pubkey(struct iked *env, struct iked_static_id *id,
EVP_PKEY_free(localkey);
if (peerrsa != NULL)
RSA_free(peerrsa);
+ if (peerec != NULL)
+ EC_KEY_free(peerec);
if (localrsa != NULL)
RSA_free(localrsa);
if (rawcert != NULL)
diff --git a/sbin/iked/crypto.c b/sbin/iked/crypto.c
index fdd7163e7b5..ebee0cb71d4 100644
--- a/sbin/iked/crypto.c
+++ b/sbin/iked/crypto.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: crypto.c,v 1.19 2015/10/31 19:28:19 naddy Exp $ */
+/* $OpenBSD: crypto.c,v 1.20 2017/03/27 10:06:41 reyk Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -39,36 +39,52 @@
#include "iked.h"
#include "ikev2.h"
-/* RFC 7427, A.1 */
-static const uint8_t sha256WithRSAEncryption[] = {
+/* RFC 7427, A.1 RSA */
+static const uint8_t sha256WithRSA[] = {
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00
};
-static const uint8_t sha384WithRSAEncryption[] = {
+static const uint8_t sha384WithRSA[] = {
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x0c, 0x05, 0x00
};
-static const uint8_t sha512WithRSAEncryption[] = {
+static const uint8_t sha512WithRSA[] = {
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x0d, 0x05, 0x00
};
+/* RFC 7427, A.3 ECDSA */
+static const uint8_t ecdsa_sha256[] = {
+ 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x02
+};
+static const uint8_t ecdsa_sha384[] = {
+ 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x03
+};
+static const uint8_t ecdsa_sha512[] = {
+ 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x04
+};
-struct {
+static const struct {
+ int sc_keytype;
+ const EVP_MD *(*sc_md)(void);
uint8_t sc_len;
const uint8_t *sc_oid;
- const EVP_MD *(*sc_md)(void);
} schemes[] = {
- { sizeof(sha256WithRSAEncryption),
- sha256WithRSAEncryption, EVP_sha256 },
- { sizeof(sha384WithRSAEncryption),
- sha384WithRSAEncryption, EVP_sha384 },
- { sizeof(sha512WithRSAEncryption),
- sha512WithRSAEncryption, EVP_sha512 },
+ { EVP_PKEY_RSA, EVP_sha256, sizeof(sha256WithRSA), sha256WithRSA },
+ { EVP_PKEY_RSA, EVP_sha384, sizeof(sha384WithRSA), sha384WithRSA },
+ { EVP_PKEY_RSA, EVP_sha512, sizeof(sha512WithRSA), sha512WithRSA },
+ { EVP_PKEY_EC, EVP_sha256, sizeof(ecdsa_sha256), ecdsa_sha256 },
+ { EVP_PKEY_EC, EVP_sha384, sizeof(ecdsa_sha384), ecdsa_sha384 },
+ { EVP_PKEY_EC, EVP_sha512, sizeof(ecdsa_sha512), ecdsa_sha512 },
};
int _dsa_verify_init(struct iked_dsa *, const uint8_t *, size_t);
-size_t _dsa_verify_offset(struct iked_dsa *, uint8_t *);
+int _dsa_verify_prepare(struct iked_dsa *, uint8_t **, size_t *,
+ uint8_t **);
int _dsa_sign_encode(struct iked_dsa *, uint8_t *, size_t *);
+int _dsa_sign_ecdsa(struct iked_dsa *, uint8_t *, size_t);
struct iked_hash *
hash_new(uint8_t type, uint16_t id)
@@ -358,6 +374,7 @@ struct ibuf *
cipher_setiv(struct iked_cipher *encr, void *iv, size_t len)
{
ibuf_release(encr->encr_iv);
+ encr->encr_iv = NULL;
if (iv != NULL) {
if (len < encr->encr_ivlength) {
log_debug("%s: invalid IV length %zu", __func__, len);
@@ -573,6 +590,7 @@ dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, uint8_t type)
BIO *rawcert = NULL;
X509 *cert = NULL;
RSA *rsa = NULL;
+ EC_KEY *ec = NULL;
EVP_PKEY *pkey = NULL;
ibuf_release(dsa->dsa_keydata);
@@ -609,7 +627,25 @@ dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, uint8_t type)
if (!EVP_PKEY_set1_RSA(pkey, rsa))
goto sslerr;
- RSA_free(rsa); /* pkey now has the reference */
+ RSA_free(rsa); /* pkey now has the reference */
+ dsa->dsa_cert = NULL;
+ dsa->dsa_key = pkey;
+ break;
+ case IKEV2_CERT_ECDSA:
+ if (dsa->dsa_sign) {
+ if ((ec = d2i_ECPrivateKey_bio(rawcert, NULL)) == NULL)
+ goto sslerr;
+ } else {
+ if ((ec = d2i_EC_PUBKEY_bio(rawcert, NULL)) == NULL)
+ goto sslerr;
+ }
+
+ if ((pkey = EVP_PKEY_new()) == NULL)
+ goto sslerr;
+ if (!EVP_PKEY_set1_EC_KEY(pkey, ec))
+ goto sslerr;
+
+ EC_KEY_free(ec); /* pkey now has the reference */
dsa->dsa_cert = NULL;
dsa->dsa_key = pkey;
break;
@@ -629,6 +665,8 @@ dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, uint8_t type)
if (rsa != NULL)
RSA_free(rsa);
+ if (ec != NULL)
+ EC_KEY_free(ec);
if (pkey != NULL)
EVP_PKEY_free(pkey);
if (cert != NULL)
@@ -636,6 +674,7 @@ dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, uint8_t type)
if (rawcert != NULL)
BIO_free(rawcert);
ibuf_release(dsa->dsa_keydata);
+ dsa->dsa_keydata = NULL;
return (NULL);
}
@@ -644,6 +683,7 @@ _dsa_verify_init(struct iked_dsa *dsa, const uint8_t *sig, size_t len)
{
uint8_t oidlen;
size_t i;
+ int keytype;
if (dsa->dsa_priv != NULL)
return (0);
@@ -656,23 +696,30 @@ _dsa_verify_init(struct iked_dsa *dsa, const uint8_t *sig, size_t len)
print_map(dsa->dsa_method, ikev2_auth_map));
return (-1);
}
+ if (dsa->dsa_key == NULL) {
+ log_debug("%s: dsa_key not set for %s", __func__,
+ print_map(dsa->dsa_method, ikev2_auth_map));
+ return (-1);
+ }
+ keytype = EVP_PKEY_type(((EVP_PKEY *)dsa->dsa_key)->type);
if (sig == NULL) {
log_debug("%s: signature missing", __func__);
return (-1);
}
- if (len < 1) {
+ if (len < sizeof(oidlen)) {
log_debug("%s: signature (%zu) too small for oid length",
__func__, len);
return (-1);
}
memcpy(&oidlen, sig, sizeof(oidlen));
- if (len < (size_t)oidlen + 1) {
+ if (len < (size_t)oidlen + sizeof(oidlen)) {
log_debug("%s: signature (%zu) too small for oid (%u)",
__func__, len, oidlen);
return (-1);
}
for (i = 0; i < nitems(schemes); i++) {
- if (oidlen == schemes[i].sc_len &&
+ if (keytype == schemes[i].sc_keytype &&
+ oidlen == schemes[i].sc_len &&
memcmp(sig + 1, schemes[i].sc_oid,
schemes[i].sc_len) == 0) {
dsa->dsa_priv = (*schemes[i].sc_md)();
@@ -727,32 +774,98 @@ dsa_update(struct iked_dsa *dsa, const void *buf, size_t len)
int
_dsa_sign_encode(struct iked_dsa *dsa, uint8_t *ptr, size_t *offp)
{
+ int keytype;
+ size_t i;
+
if (offp)
*offp = 0;
if (dsa->dsa_method != IKEV2_AUTH_SIG)
return (0);
- if (dsa->dsa_priv != EVP_sha256())
+ if (dsa->dsa_key == NULL)
+ return (-1);
+ keytype = EVP_PKEY_type(((EVP_PKEY *)dsa->dsa_key)->type);
+ for (i = 0; i < nitems(schemes); i++) {
+ /* XXX should avoid calling sc_md() each time... */
+ if (keytype == schemes[i].sc_keytype &&
+ (dsa->dsa_priv == (*schemes[i].sc_md)()))
+ break;
+ }
+ if (i >= nitems(schemes))
return (-1);
if (ptr) {
- ptr[0] = sizeof(sha256WithRSAEncryption);
- memcpy(ptr + 1, sha256WithRSAEncryption,
- sizeof(sha256WithRSAEncryption));
+ ptr[0] = schemes[i].sc_len;
+ memcpy(ptr + sizeof(ptr[0]), schemes[i].sc_oid,
+ schemes[i].sc_len);
}
if (offp)
- *offp = 1 + sizeof(sha256WithRSAEncryption);
+ *offp = sizeof(ptr[0]) + schemes[i].sc_len;
return (0);
}
+/* Export size of encoded signature hash type */
size_t
-dsa_length(struct iked_dsa *dsa)
+dsa_prefix(struct iked_dsa *dsa)
{
size_t off = 0;
+ if (_dsa_sign_encode(dsa, NULL, &off) < 0)
+ fatal("dsa_prefix: internal error");
+ return off;
+}
+
+size_t
+dsa_length(struct iked_dsa *dsa)
+{
if (dsa->dsa_hmac)
return (EVP_MD_size(dsa->dsa_priv));
- if (_dsa_sign_encode(dsa, NULL, &off) < 0)
- fatal("dsa_length: internal error");
- return (EVP_PKEY_size(dsa->dsa_key) + off);
+ switch (dsa->dsa_method) {
+ case IKEV2_AUTH_ECDSA_256:
+ case IKEV2_AUTH_ECDSA_384:
+ case IKEV2_AUTH_ECDSA_521:
+ /* size of concat(r|s) */
+ return (2 * ((EVP_PKEY_bits(dsa->dsa_key) + 7) / 8));
+ }
+ return (dsa_prefix(dsa) + EVP_PKEY_size(dsa->dsa_key));
+}
+
+int
+_dsa_sign_ecdsa(struct iked_dsa *dsa, uint8_t *ptr, size_t len)
+{
+ ECDSA_SIG *obj = NULL;
+ uint8_t *otmp = NULL, *tmp;
+ unsigned int tmplen;
+ int ret = -1;
+ int bnlen, off;
+
+ if (len % 2)
+ goto done; /* must be even */
+ bnlen = len/2;
+ /*
+ * (a) create DER signature into 'tmp' buffer
+ * (b) convert buffer to ECDSA_SIG object
+ * (c) concatenate the padded r|s BIGNUMS into 'ptr'
+ */
+ if ((tmplen = EVP_PKEY_size(dsa->dsa_key)) == 0)
+ goto done;
+ if ((otmp = tmp = calloc(1, tmplen)) == NULL)
+ goto done;
+ if (!EVP_SignFinal(dsa->dsa_ctx, tmp, &tmplen, dsa->dsa_key))
+ goto done;
+ if (d2i_ECDSA_SIG(&obj, (const uint8_t **)&tmp, tmplen) == NULL)
+ goto done;
+ if (BN_num_bytes(obj->r) > bnlen || BN_num_bytes(obj->s) > bnlen)
+ goto done;
+ memset(ptr, 0, len);
+ off = bnlen - BN_num_bytes(obj->r);
+ BN_bn2bin(obj->r, ptr + off);
+ off = 2 * bnlen - BN_num_bytes(obj->s);
+ BN_bn2bin(obj->s, ptr + off);
+ ret = 0;
+ done:
+ free(otmp);
+ if (obj)
+ ECDSA_SIG_free(obj);
+ return (ret);
}
ssize_t
@@ -769,36 +882,92 @@ dsa_sign_final(struct iked_dsa *dsa, void *buf, size_t len)
if (!HMAC_Final(dsa->dsa_ctx, buf, &siglen))
return (-1);
} else {
- if (_dsa_sign_encode(dsa, ptr, &off) < 0)
- return (-1);
- if (!EVP_SignFinal(dsa->dsa_ctx, ptr + off, &siglen,
- dsa->dsa_key))
- return (-1);
- siglen += off;
+ switch (dsa->dsa_method) {
+ case IKEV2_AUTH_ECDSA_256:
+ case IKEV2_AUTH_ECDSA_384:
+ case IKEV2_AUTH_ECDSA_521:
+ if (_dsa_sign_ecdsa(dsa, buf, len) < 0)
+ return (-1);
+ siglen = len;
+ break;
+ default:
+ if (_dsa_sign_encode(dsa, ptr, &off) < 0)
+ return (-1);
+ if (!EVP_SignFinal(dsa->dsa_ctx, ptr + off, &siglen,
+ dsa->dsa_key))
+ return (-1);
+ siglen += off;
+ break;
+ }
}
return (siglen);
}
-size_t
-_dsa_verify_offset(struct iked_dsa *dsa, uint8_t *ptr)
+int
+_dsa_verify_prepare(struct iked_dsa *dsa, uint8_t **sigp, size_t *lenp,
+ uint8_t **freemep)
{
- /*
- * XXX assumes that _dsa_verify_init() has already checked
- * the encoded method.
- */
- if (dsa->dsa_method == IKEV2_AUTH_SIG)
- return (ptr[0] + 1);
- return (0);
+ ECDSA_SIG *obj = NULL;
+ uint8_t *ptr = NULL;
+ size_t bnlen, len, off;
+ int ret = -1;
+
+ *freemep = NULL; /* don't return garbage in case of an error */
+
+ switch (dsa->dsa_method) {
+ case IKEV2_AUTH_SIG:
+ /*
+ * The first byte of the signature encodes the OID
+ * prefix length which we need to skip.
+ */
+ off = (*sigp)[0] + 1;
+ *sigp = *sigp + off;
+ *lenp = *lenp - off;
+ *freemep = NULL;
+ ret = 0;
+ break;
+ case IKEV2_AUTH_ECDSA_256:
+ case IKEV2_AUTH_ECDSA_384:
+ case IKEV2_AUTH_ECDSA_521:
+ /*
+ * sigp points to concatenation r|s, while EVP_VerifyFinal()
+ * expects the signature as a DER-encoded blob (of the two
+ * values), so we need to convert the signature in a new
+ * buffer (we cannot override the given buffer) and the caller
+ * has to free this buffer ('freeme').
+ */
+ if (*lenp < 64 || *lenp > 132 || *lenp % 2)
+ goto done;
+ bnlen = (*lenp)/2;
+ /* sigp points to concatenation: r|s */
+ if ((obj = ECDSA_SIG_new()) == NULL ||
+ BN_bin2bn(*sigp, bnlen, obj->r) == NULL ||
+ BN_bin2bn(*sigp+bnlen, bnlen, obj->s) == NULL ||
+ (len = i2d_ECDSA_SIG(obj, &ptr)) == 0)
+ goto done;
+ *lenp = len;
+ *sigp = ptr;
+ *freemep = ptr;
+ ptr = NULL;
+ ret = 0;
+ break;
+ default:
+ return (0);
+ }
+ done:
+ free(ptr);
+ if (obj)
+ ECDSA_SIG_free(obj);
+ return (ret);
}
ssize_t
dsa_verify_final(struct iked_dsa *dsa, void *buf, size_t len)
{
uint8_t sig[EVP_MAX_MD_SIZE];
+ uint8_t *ptr = buf, *freeme = NULL;
unsigned int siglen = sizeof(sig);
- uint8_t *ptr = buf;
- size_t off = 0;
if (dsa->dsa_hmac) {
if (!HMAC_Final(dsa->dsa_ctx, sig, &siglen))
@@ -806,13 +975,15 @@ dsa_verify_final(struct iked_dsa *dsa, void *buf, size_t len)
if (siglen != len || memcmp(buf, sig, siglen) != 0)
return (-1);
} else {
- if ((off = _dsa_verify_offset(dsa, ptr)) >= len)
+ if (_dsa_verify_prepare(dsa, &ptr, &len, &freeme) < 0)
return (-1);
- if (EVP_VerifyFinal(dsa->dsa_ctx, ptr + off, len - off,
+ if (EVP_VerifyFinal(dsa->dsa_ctx, ptr, len,
dsa->dsa_key) != 1) {
+ free(freeme);
ca_sslerror(__func__);
return (-1);
}
+ free(freeme);
}
return (0);
diff --git a/sbin/iked/iked.8 b/sbin/iked/iked.8
index c03ec117a5f..c1a0f0c15c2 100644
--- a/sbin/iked/iked.8
+++ b/sbin/iked/iked.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: iked.8,v 1.19 2014/11/10 13:57:32 jmc Exp $
+.\" $OpenBSD: iked.8,v 1.20 2017/03/27 10:06:41 reyk Exp $
.\"
.\" Copyright (c) 2010 - 2014 Reyk Floeter <reyk@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: November 10 2014 $
+.Dd $Mdocdate: March 27 2017 $
.Dt IKED 8
.Os
.Sh NAME
@@ -44,7 +44,8 @@ is provided by
.Xr isakmpd 8 .
.Pp
.Nm
-supports mutual authentication using RSA public keys and X.509 certificates.
+supports mutual authentication using RSA or ECDSA public keys and X.509
+certificates.
See the
.Sx PUBLIC KEY AUTHENTICATION
section below and PKI AND CERTIFICATE AUTHORITY COMMANDS in
diff --git a/sbin/iked/iked.conf.5 b/sbin/iked/iked.conf.5
index a4b60844e78..68784f45161 100644
--- a/sbin/iked/iked.conf.5
+++ b/sbin/iked/iked.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: iked.conf.5,v 1.47 2017/03/13 15:07:51 patrick Exp $
+.\" $OpenBSD: iked.conf.5,v 1.48 2017/03/27 10:06:41 reyk Exp $
.\"
.\" Copyright (c) 2010 - 2014 Reyk Floeter <reyk@openbsd.org>
.\" Copyright (c) 2004 Mathieu Sauve-Frankel All rights reserved.
@@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: March 13 2017 $
+.Dd $Mdocdate: March 27 2017 $
.Dt IKED.CONF 5
.Os
.Sh NAME
@@ -477,9 +477,14 @@ for kilo-, mega- and gigabytes accordingly.
Please note that rekeying must happen at least several times a day as
IPsec security heavily depends on frequent key renewals.
.It Op Ar ikeauth
-Specify the mode to mutually authenticate the peers.
-Non-psk modes will require to set up certificates and RSA public keys;
-see
+Specify a method to be used to authenticate the remote peer.
+.Xr iked 8
+will automatically determine a method based on public keys or certificates
+configured for the peer.
+.Ar ikeauth
+can be used to override this behaviour.
+Non-psk modes will require to set up certificates and RSA or ECDSA public
+keys; see
.Xr iked 8
for more information.
.Pp
@@ -491,14 +496,24 @@ The only supported EAP
is currently
.Ar MSCHAP-V2 .
The responder will use RSA public key authentication.
+.It Ic ecdsa256
+Use ECDSA with a 256-bit elliptic curve key and SHA2-256 for authentication.
+.It Ic ecdsa384
+Use ECDSA with a 384-bit elliptic curve key and SHA2-384 for authentication.
+.It Ic ecdsa521
+Use ECDSA with a 521-bit elliptic curve key and SHA2-512 for authentication.
.It Ic psk Ar string
Use a pre-shared key
.Ar string
or hex value (starting with 0x) for authentication.
+.It Ic rfc7427
+Only use RFC 7427 signatures for authentication.
+RFC 7427 signatures currently only support SHA2-256 as the hash.
.It Ic rsa
-Use RSA public key authentication.
-This is the default mode if no option is specified.
+Use RSA public key authentication with SHA1 as the hash.
.El
+.Pp
+The default is to allow any signature authentication.
.It Ic config Ar option address
Send one or more optional configuration payloads (CP) to the peer.
The configuration
diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h
index d16404c75bb..b2bda61d145 100644
--- a/sbin/iked/iked.h
+++ b/sbin/iked/iked.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: iked.h,v 1.109 2017/03/13 18:49:20 mikeb Exp $ */
+/* $OpenBSD: iked.h,v 1.110 2017/03/27 10:06:41 reyk Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -753,6 +753,7 @@ struct ibuf *
dsa_setkey(struct iked_dsa *, void *, size_t, uint8_t);
void dsa_free(struct iked_dsa *);
int dsa_init(struct iked_dsa *, const void *, size_t);
+size_t dsa_prefix(struct iked_dsa *);
size_t dsa_length(struct iked_dsa *);
int dsa_update(struct iked_dsa *, const void *, size_t);
ssize_t dsa_sign_final(struct iked_dsa *, void *, size_t);
diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c
index 15d57ba3806..6f01901925d 100644
--- a/sbin/iked/ikev2.c
+++ b/sbin/iked/ikev2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2.c,v 1.143 2017/03/13 18:49:20 mikeb Exp $ */
+/* $OpenBSD: ikev2.c,v 1.144 2017/03/27 10:06:41 reyk Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -323,6 +323,7 @@ ikev2_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg)
id->id_type = type;
id->id_offset = 0;
ibuf_release(id->id_buf);
+ id->id_buf = NULL;
if (type != IKEV2_AUTH_NONE) {
if (len <= 0 ||
@@ -498,13 +499,39 @@ done:
}
int
-ikev2_ike_auth_compatible(struct iked_sa *sa, uint8_t want, uint8_t have)
+ikev2_ike_auth_compatible(struct iked_sa *sa, uint8_t policy, uint8_t wire)
{
- if (want == have)
- return (0);
- if (sa->sa_sigsha2 &&
- have == IKEV2_AUTH_SIG && want == IKEV2_AUTH_RSA_SIG)
+ if (wire == IKEV2_AUTH_SIG_ANY) /* internal, not on wire */
+ return (-1);
+ if (policy == wire || policy == IKEV2_AUTH_NONE)
return (0);
+ switch (policy) {
+ case IKEV2_AUTH_SIG_ANY:
+ switch (wire) {
+ case IKEV2_AUTH_SIG:
+ case IKEV2_AUTH_RSA_SIG:
+ case IKEV2_AUTH_ECDSA_256:
+ case IKEV2_AUTH_ECDSA_384:
+ case IKEV2_AUTH_ECDSA_521:
+ return (0);
+ }
+ break;
+ case IKEV2_AUTH_SIG:
+ case IKEV2_AUTH_RSA_SIG:
+ case IKEV2_AUTH_ECDSA_256:
+ case IKEV2_AUTH_ECDSA_384:
+ case IKEV2_AUTH_ECDSA_521:
+ switch (wire) {
+ /*
+ * XXX Maybe we need an indication saying:
+ * XXX Accept AUTH_SIG as long as its DSA?
+ */
+ case IKEV2_AUTH_SIG:
+ if (sa->sa_sigsha2)
+ return (0);
+ }
+ break;
+ }
return (-1);
}
@@ -592,8 +619,10 @@ ikev2_ike_auth_recv(struct iked *env, struct iked_sa *sa,
if (ikev2_ike_auth_compatible(sa,
ikeauth.auth_method, msg->msg_auth.id_type) < 0) {
- log_warnx("%s: unexpected auth method %s", __func__,
- print_map(msg->msg_auth.id_type, ikev2_auth_map));
+ log_warnx("%s: unexpected auth method %s, was "
+ "expecting %s", __func__,
+ print_map(msg->msg_auth.id_type, ikev2_auth_map),
+ print_map(ikeauth.auth_method, ikev2_auth_map));
return (-1);
}
ikeauth.auth_method = msg->msg_auth.id_type;
@@ -1177,6 +1206,7 @@ ikev2_policy2id(struct iked_static_id *polid, struct iked_id *id, int srcid)
if (inet_pton(AF_INET, (char *)polid->id_data, &in4) != 1 ||
ibuf_add(id->id_buf, &in4, sizeof(in4)) != 0) {
ibuf_release(id->id_buf);
+ id->id_buf = NULL;
return (-1);
}
break;
@@ -1184,6 +1214,7 @@ ikev2_policy2id(struct iked_static_id *polid, struct iked_id *id, int srcid)
if (inet_pton(AF_INET6, (char *)polid->id_data, &in6) != 1 ||
ibuf_add(id->id_buf, &in6, sizeof(in6)) != 0) {
ibuf_release(id->id_buf);
+ id->id_buf = NULL;
return (-1);
}
break;
@@ -1196,6 +1227,7 @@ ikev2_policy2id(struct iked_static_id *polid, struct iked_id *id, int srcid)
if (name)
X509_NAME_free(name);
ibuf_release(id->id_buf);
+ id->id_buf = NULL;
return (-1);
}
X509_NAME_free(name);
@@ -1204,6 +1236,7 @@ ikev2_policy2id(struct iked_static_id *polid, struct iked_id *id, int srcid)
if (ibuf_add(id->id_buf,
polid->id_data, polid->id_length) != 0) {
ibuf_release(id->id_buf);
+ id->id_buf = NULL;
return (-1);
}
break;
diff --git a/sbin/iked/ikev2.h b/sbin/iked/ikev2.h
index 2dc0e6dd98a..683fd47845d 100644
--- a/sbin/iked/ikev2.h
+++ b/sbin/iked/ikev2.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2.h,v 1.25 2017/01/20 13:46:10 mikeb Exp $ */
+/* $OpenBSD: ikev2.h,v 1.26 2017/03/27 10:06:41 reyk Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -409,6 +409,12 @@ struct ikev2_cert {
#define IKEV2_CERT_HASHURL_X509 12 /* RFC4306 */
#define IKEV2_CERT_HASHURL_X509_BUNDLE 13 /* RFC4306 */
#define IKEV2_CERT_OCSP 14 /* RFC4806 */
+/*
+ * As of November 2014, work was still in progress to add a more generic
+ * format for raw public keys (RFC7296), so we use a number in IANA's private
+ * use range (201-255, same RFC) for ECDSA.
+ */
+#define IKEV2_CERT_ECDSA 201 /* Private */
extern struct iked_constmap ikev2_cert_map[];
@@ -456,6 +462,12 @@ struct ikev2_auth {
#define IKEV2_AUTH_GSPM 12 /* RFC6467 */
#define IKEV2_AUTH_NULL 13 /* RFC7619 */
#define IKEV2_AUTH_SIG 14 /* RFC7427 */
+#define IKEV2_AUTH_SIG_ANY 255 /* Internal (any signature) */
+/*
+ * AUTH_SIG also serves as an indication that a given policy has
+ * been configured to accept RSA or ECDSA payloads, as long as it
+ * successfully authenticates against a configured CA.
+ */
extern struct iked_constmap ikev2_auth_map[];
diff --git a/sbin/iked/ikev2_msg.c b/sbin/iked/ikev2_msg.c
index ba603e06934..37bb8995425 100644
--- a/sbin/iked/ikev2_msg.c
+++ b/sbin/iked/ikev2_msg.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2_msg.c,v 1.49 2017/03/13 17:41:14 reyk Exp $ */
+/* $OpenBSD: ikev2_msg.c,v 1.50 2017/03/27 10:06:41 reyk Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -814,7 +814,7 @@ ikev2_msg_authsign(struct iked *env, struct iked_sa *sa,
struct iked_auth *auth, struct ibuf *authmsg)
{
uint8_t *key, *psk = NULL;
- ssize_t keylen;
+ ssize_t keylen, siglen;
struct iked_hash *prf = sa->sa_prf;
struct iked_id *id;
struct iked_dsa *dsa = NULL;
@@ -872,13 +872,20 @@ ikev2_msg_authsign(struct iked *env, struct iked_sa *sa,
goto done;
}
- if ((ret = dsa_sign_final(dsa,
- ibuf_data(buf), ibuf_size(buf))) == -1) {
+ if ((siglen = dsa_sign_final(dsa,
+ ibuf_data(buf), ibuf_size(buf))) < 0) {
log_debug("%s: failed to create auth signature", __func__);
ibuf_release(buf);
goto done;
}
+ if (ibuf_setsize(buf, siglen) < 0) {
+ log_debug("%s: failed to set auth signature size to %zd",
+ __func__, siglen);
+ ibuf_release(buf);
+ goto done;
+ }
+
sa->sa_localauth.id_type = auth->auth_method;
sa->sa_localauth.id_buf = buf;
diff --git a/sbin/iked/parse.y b/sbin/iked/parse.y
index 548686779fb..58b0b825abc 100644
--- a/sbin/iked/parse.y
+++ b/sbin/iked/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.61 2017/01/20 13:56:51 mikeb Exp $ */
+/* $OpenBSD: parse.y,v 1.62 2017/03/27 10:06:41 reyk Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -31,6 +31,9 @@
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+
#include <ctype.h>
#include <err.h>
#include <errno.h>
@@ -59,10 +62,14 @@ static struct file {
int lineno;
int errors;
} *file;
+EVP_PKEY *wrap_pubkey(FILE *);
+EVP_PKEY *find_pubkey(const char *);
+int set_policy(char *, int, struct iked_policy *);
+int set_policy_auth_method(const char *, EVP_PKEY *,
+ struct iked_policy *);
struct file *pushfile(const char *, int);
int popfile(void);
int check_file_secrecy(int, const char *);
-int check_pubkey(char *, int );
int yyparse(void);
int yylex(void);
int yyerror(const char *, ...)
@@ -242,11 +249,13 @@ const struct ipsec_xf groupxfs[] = {
};
const struct ipsec_xf methodxfs[] = {
+ { "none", IKEV2_AUTH_NONE },
{ "rsa", IKEV2_AUTH_RSA_SIG },
- { "dss", IKEV2_AUTH_DSS_SIG },
- { "ecdsa-256", IKEV2_AUTH_ECDSA_256 },
- { "ecdsa-384", IKEV2_AUTH_ECDSA_384 },
- { "ecdsa-521", IKEV2_AUTH_ECDSA_521 },
+ { "ecdsa256", IKEV2_AUTH_ECDSA_256 },
+ { "ecdsa384", IKEV2_AUTH_ECDSA_384 },
+ { "ecdsa521", IKEV2_AUTH_ECDSA_521 },
+ { "rfc7427", IKEV2_AUTH_SIG },
+ { "signature", IKEV2_AUTH_SIG_ANY },
{ NULL }
};
@@ -805,12 +814,7 @@ ipcomp : /* empty */ { $$ = 0; }
;
ikeauth : /* empty */ {
- $$.auth_method = IKEV2_AUTH_RSA_SIG;
- $$.auth_eap = 0;
- $$.auth_length = 0;
- }
- | RSA {
- $$.auth_method = IKEV2_AUTH_RSA_SIG;
+ $$.auth_method = IKEV2_AUTH_SIG_ANY; /* default */
$$.auth_eap = 0;
$$.auth_length = 0;
}
@@ -837,6 +841,21 @@ ikeauth : /* empty */ {
$$.auth_eap = EAP_TYPE_MSCHAP_V2;
$$.auth_length = 0;
}
+ | STRING {
+ const struct ipsec_xf *xf;
+
+ if ((xf = parse_xf($1, 0, methodxfs)) == NULL ||
+ xf->id == IKEV2_AUTH_NONE) {
+ yyerror("not a valid authentication mode");
+ free($1);
+ YYERROR;
+ }
+ free($1);
+
+ $$.auth_method = xf->id;
+ $$.auth_eap = 0;
+ $$.auth_length = 0;
+ }
;
byte_spec : NUMBER {
@@ -1662,44 +1681,178 @@ get_id_type(char *string)
return (IKEV2_ID_FQDN);
}
+EVP_PKEY *
+wrap_pubkey(FILE *fp)
+{
+ EVP_PKEY *key = NULL;
+ struct rsa_st *rsa = NULL;
+
+ key = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
+ if (key == NULL) {
+ /* reading PKCS #8 failed, try PEM */
+ rewind(fp);
+ rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
+ fclose(fp);
+ if (rsa == NULL)
+ return (NULL);
+ if ((key = EVP_PKEY_new()) == NULL) {
+ RSA_free(rsa);
+ return (NULL);
+ }
+ if (!EVP_PKEY_set1_RSA(key, rsa)) {
+ RSA_free(rsa);
+ EVP_PKEY_free(key);
+ return (NULL);
+ }
+ /* Always free RSA *rsa */
+ RSA_free(rsa);
+ } else {
+ fclose(fp);
+ }
+
+ return (key);
+}
+
+EVP_PKEY *
+find_pubkey(const char *keyfile)
+{
+ FILE *fp = NULL;
+ if ((fp = fopen(keyfile, "r")) == NULL)
+ return (NULL);
+
+ return (wrap_pubkey(fp));
+}
+
int
-check_pubkey(char *idstr, int type)
+set_policy_auth_method(const char *peerid, EVP_PKEY *key,
+ struct iked_policy *pol)
+{
+ struct rsa_st *rsa;
+ EC_KEY *ec_key;
+ u_int8_t method;
+ u_int8_t cert_type;
+ struct iked_auth *ikeauth;
+
+ method = IKEV2_AUTH_NONE;
+ cert_type = IKEV2_CERT_NONE;
+
+ if (key != NULL) {
+ /* infer policy from key type */
+ if ((rsa = EVP_PKEY_get1_RSA(key)) != NULL) {
+ method = IKEV2_AUTH_RSA_SIG;
+ cert_type = IKEV2_CERT_RSA_KEY;
+ RSA_free(rsa);
+ } else if ((ec_key = EVP_PKEY_get1_EC_KEY(key)) != NULL) {
+ const EC_GROUP *group = EC_KEY_get0_group(ec_key);
+ if (group == NULL) {
+ EC_KEY_free(ec_key);
+ return (-1);
+ }
+ switch (EC_GROUP_get_degree(group)) {
+ case 256:
+ method = IKEV2_AUTH_ECDSA_256;
+ break;
+ case 384:
+ method = IKEV2_AUTH_ECDSA_384;
+ break;
+ case 521:
+ method = IKEV2_AUTH_ECDSA_521;
+ break;
+ default:
+ EC_KEY_free(ec_key);
+ return (-1);
+ }
+ cert_type = IKEV2_CERT_ECDSA;
+ EC_KEY_free(ec_key);
+ }
+
+ if (method == IKEV2_AUTH_NONE || cert_type == IKEV2_CERT_NONE)
+ return (-1);
+ } else {
+ /* default to IKEV2_CERT_X509_CERT otherwise */
+ method = IKEV2_AUTH_SIG;
+ cert_type = IKEV2_CERT_X509_CERT;
+ }
+
+ ikeauth = &pol->pol_auth;
+
+ if (ikeauth->auth_method == IKEV2_AUTH_SHARED_KEY_MIC) {
+ if (key != NULL &&
+ method != IKEV2_AUTH_RSA_SIG)
+ goto mismatch;
+ return (0);
+ }
+
+ if (ikeauth->auth_method != IKEV2_AUTH_NONE &&
+ ikeauth->auth_method != IKEV2_AUTH_SIG_ANY &&
+ ikeauth->auth_method != method)
+ goto mismatch;
+
+ ikeauth->auth_method = method;
+ pol->pol_certreqtype = cert_type;
+
+ log_debug("%s: using %s for peer %s", __func__,
+ print_xf(method, 0, methodxfs), peerid);
+
+ return (0);
+
+ mismatch:
+ log_warnx("%s: ikeauth policy mismatch, %s specified, but only %s "
+ "possible", __func__, print_xf(ikeauth->auth_method, 0, methodxfs),
+ print_xf(method, 0, methodxfs));
+ return (-1);
+}
+
+int
+set_policy(char *idstr, int type, struct iked_policy *pol)
{
char keyfile[PATH_MAX];
- FILE *fp = NULL;
- const char *suffix = NULL;
+ const char *prefix = NULL;
+ EVP_PKEY *key;
switch (type) {
case IKEV2_ID_IPV4:
- suffix = "ipv4";
+ prefix = "ipv4";
break;
case IKEV2_ID_IPV6:
- suffix = "ipv6";
+ prefix = "ipv6";
break;
case IKEV2_ID_FQDN:
- suffix = "fqdn";
+ prefix = "fqdn";
break;
case IKEV2_ID_UFQDN:
- suffix = "ufqdn";
+ prefix = "ufqdn";
break;
default:
/* Unspecified ID or public key not supported for this type */
+ log_debug("%s: unknown type = %d", __func__, type);
return (-1);
}
lc_string(idstr);
if ((size_t)snprintf(keyfile, sizeof(keyfile),
- IKED_CA IKED_PUBKEY_DIR "%s/%s", suffix,
+ IKED_CA IKED_PUBKEY_DIR "%s/%s", prefix,
idstr) >= sizeof(keyfile)) {
log_warnx("%s: public key path is too long", __func__);
return (-1);
}
- if ((fp = fopen(keyfile, "r")) == NULL)
+ if ((key = find_pubkey(keyfile)) == NULL) {
+ log_warnx("%s: could not find pubkey for %s", __func__,
+ keyfile);
+ }
+
+ if (set_policy_auth_method(keyfile, key, pol) < 0) {
+ EVP_PKEY_free(key);
+ log_warnx("%s: failed to set policy auth method for %s",
+ __func__, keyfile);
return (-1);
- fclose(fp);
+ }
- log_debug("%s: found public key file %s", __func__, keyfile);
+ if (key != NULL) {
+ EVP_PKEY_free(key);
+ log_debug("%s: found pubkey for %s", __func__, keyfile);
+ }
return (0);
}
@@ -2341,12 +2494,16 @@ print_policy(struct iked_policy *pol)
print_verbose(" lifetime %llu bytes %llu",
pol->pol_lifetime.lt_seconds, pol->pol_lifetime.lt_bytes);
- if (pol->pol_auth.auth_method == IKEV2_AUTH_SHARED_KEY_MIC) {
- print_verbose(" psk 0x");
- for (i = 0; i < pol->pol_auth.auth_length; i++)
- print_verbose("%02x",
- pol->pol_auth.auth_data[i]);
- } else {
+ switch (pol->pol_auth.auth_method) {
+ case IKEV2_AUTH_NONE:
+ print_verbose (" none");
+ break;
+ case IKEV2_AUTH_SHARED_KEY_MIC:
+ print_verbose(" psk 0x");
+ for (i = 0; i < pol->pol_auth.auth_length; i++)
+ print_verbose("%02x", pol->pol_auth.auth_data[i]);
+ break;
+ default:
if (pol->pol_auth.auth_eap)
print_verbose(" eap \"%s\"",
print_map(pol->pol_auth.auth_eap, eap_type_map));
@@ -2695,9 +2852,11 @@ create_ike(char *name, int af, uint8_t ipproto, struct ipsec_hosts *hosts,
}
}
- /* Check if we have a raw public key for this peer */
- if (check_pubkey(idstr, idtype) != -1)
- pol.pol_certreqtype = IKEV2_CERT_RSA_KEY;
+ /* Make sure that we know how to authenticate this peer */
+ if (idtype && set_policy(idstr, idtype, &pol) < 0) {
+ log_debug("%s: set_policy failed", __func__);
+ return (-1);
+ }
config_setpolicy(env, &pol, PROC_IKEV2);