diff options
author | Markus Friedl <markus@cvs.openbsd.org> | 2015-03-26 19:52:36 +0000 |
---|---|---|
committer | Markus Friedl <markus@cvs.openbsd.org> | 2015-03-26 19:52:36 +0000 |
commit | 5bbc02b037f252e64791fb7893aecc7070880917 (patch) | |
tree | 54355122a2f27f391913a34daf154197b29b40e2 /sbin | |
parent | 8ec8a98046a1296045e16eb3c678256f752ad8e3 (diff) |
initial support for RFC 7427 signatures, so we are no longer
restricted to SHA1 for RSA signatures. ok mikeb@
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/iked/ca.c | 8 | ||||
-rw-r--r-- | sbin/iked/crypto.c | 141 | ||||
-rw-r--r-- | sbin/iked/iked.h | 5 | ||||
-rw-r--r-- | sbin/iked/ikev2.c | 70 | ||||
-rw-r--r-- | sbin/iked/ikev2.h | 14 | ||||
-rw-r--r-- | sbin/iked/ikev2_msg.c | 6 | ||||
-rw-r--r-- | sbin/iked/ikev2_pld.c | 33 |
7 files changed, 259 insertions, 18 deletions
diff --git a/sbin/iked/ca.c b/sbin/iked/ca.c index 357bb57ba4c..d1eb3dd5e1e 100644 --- a/sbin/iked/ca.c +++ b/sbin/iked/ca.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ca.c,v 1.34 2015/02/06 10:39:01 deraadt Exp $ */ +/* $OpenBSD: ca.c,v 1.35 2015/03/26 19:52:35 markus Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -302,6 +302,12 @@ ca_setauth(struct iked *env, struct iked_sa *sa, struct iked_policy *policy = sa->sa_policy; u_int8_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 (type == IKEV2_AUTH_SHARED_KEY_MIC) { sa->sa_stateflags |= IKED_REQ_AUTH; return (ikev2_msg_authsign(env, sa, diff --git a/sbin/iked/crypto.c b/sbin/iked/crypto.c index 3d6f6a29583..2fd6f356d29 100644 --- a/sbin/iked/crypto.c +++ b/sbin/iked/crypto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: crypto.c,v 1.15 2015/01/16 06:39:58 deraadt Exp $ */ +/* $OpenBSD: crypto.c,v 1.16 2015/03/26 19:52:35 markus Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -39,6 +39,34 @@ #include "iked.h" #include "ikev2.h" +/* RFC 7427, A.1 */ +static const u_int8_t sha256WithRSAEncryption[] = { + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00 +}; +static const u_int8_t sha384WithRSAEncryption[] = { + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0c, 0x05, 0x00 +}; +static const u_int8_t sha512WithRSAEncryption[] = { + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0d, 0x05, 0x00 +}; + +struct { + u_int8_t sc_len; + const u_int8_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 }, +}; + +int _dsa_verify_init(struct iked_dsa *, const u_int8_t *, size_t); +size_t _dsa_verify_offset(struct iked_dsa *, u_int8_t *); +int _dsa_sign_encode(struct iked_dsa *, u_int8_t *, size_t *); + struct iked_hash * hash_new(u_int8_t type, u_int16_t id) { @@ -442,6 +470,12 @@ dsa_new(u_int16_t id, struct iked_hash *prf, int sign) bzero(&dsa, sizeof(dsa)); switch (id) { + case IKEV2_AUTH_SIG: + if (sign) + dsa.dsa_priv = EVP_sha256(); /* XXX should be passed */ + else + dsa.dsa_priv = NULL; /* set later by dsa_init() */ + break; case IKEV2_AUTH_RSA_SIG: /* RFC5996 says we SHOULD use SHA1 here */ dsa.dsa_priv = EVP_sha1(); @@ -603,9 +637,54 @@ dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, u_int8_t type) } int -dsa_init(struct iked_dsa *dsa) +_dsa_verify_init(struct iked_dsa *dsa, const u_int8_t *sig, size_t len) +{ + u_int8_t oidlen; + size_t i; + + if (dsa->dsa_priv != NULL) + return (0); + /* + * For IKEV2_AUTH_SIG the oid of the authentication signature + * is encoded in the first bytes of the auth message. + */ + if (dsa->dsa_method != IKEV2_AUTH_SIG) { + log_debug("%s: dsa_priv not set for %s", __func__, + print_map(dsa->dsa_method, ikev2_auth_map)); + return (-1); + } + if (sig == NULL) { + log_debug("%s: signature missing", __func__); + return (-1); + } + if (len < 1) { + 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) { + 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 && + memcmp(sig + 1, schemes[i].sc_oid, schemes[i].sc_len) == 0) { + dsa->dsa_priv = (*schemes[i].sc_md)(); + log_debug("%s: signature scheme %zd selected", + __func__, i); + return (0); + } + } + log_debug("%s: unsupported signature (%d)", __func__, oidlen); + return (-1); +} + +int +dsa_init(struct iked_dsa *dsa, const void *buf, size_t len) { - int ret; + int ret; if (dsa->dsa_hmac) { if (!HMAC_Init_ex(dsa->dsa_ctx, ibuf_data(dsa->dsa_keydata), @@ -616,8 +695,11 @@ dsa_init(struct iked_dsa *dsa) if (dsa->dsa_sign) ret = EVP_SignInit_ex(dsa->dsa_ctx, dsa->dsa_priv, NULL); - else + else { + if ((ret = _dsa_verify_init(dsa, buf, len)) != 0) + return (ret); ret = EVP_VerifyInit_ex(dsa->dsa_ctx, dsa->dsa_priv, NULL); + } return (ret ? 0 : -1); } @@ -637,18 +719,44 @@ dsa_update(struct iked_dsa *dsa, const void *buf, size_t len) return (ret ? 0 : -1); } +/* Prefix signature hash with encoded type */ +int +_dsa_sign_encode(struct iked_dsa *dsa, u_int8_t *ptr, size_t *offp) +{ + if (offp) + *offp = 0; + if (dsa->dsa_method != IKEV2_AUTH_SIG) + return (0); + if (dsa->dsa_priv != EVP_sha256()) + return (-1); + if (ptr) { + ptr[0] = sizeof(sha256WithRSAEncryption); + memcpy(ptr + 1, sha256WithRSAEncryption, + sizeof(sha256WithRSAEncryption)); + } + if (offp) + *offp = 1 + sizeof(sha256WithRSAEncryption); + return (0); +} + size_t dsa_length(struct iked_dsa *dsa) { + size_t off = 0; + if (dsa->dsa_hmac) return (EVP_MD_size(dsa->dsa_priv)); - return (EVP_PKEY_size(dsa->dsa_key)); + if (_dsa_sign_encode(dsa, NULL, &off) < 0) + fatal("dsa_length: internal error"); + return (EVP_PKEY_size(dsa->dsa_key) + off); } ssize_t dsa_sign_final(struct iked_dsa *dsa, void *buf, size_t len) { u_int siglen; + size_t off = 0; + u_int8_t *ptr = buf; if (len < dsa_length(dsa)) return (-1); @@ -657,19 +765,36 @@ dsa_sign_final(struct iked_dsa *dsa, void *buf, size_t len) if (!HMAC_Final(dsa->dsa_ctx, buf, &siglen)) return (-1); } else { - if (!EVP_SignFinal(dsa->dsa_ctx, buf, &siglen, + 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; } return (siglen); } +size_t +_dsa_verify_offset(struct iked_dsa *dsa, u_int8_t *ptr) +{ + /* + * 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); +} + ssize_t dsa_verify_final(struct iked_dsa *dsa, void *buf, size_t len) { u_int8_t sig[EVP_MAX_MD_SIZE]; u_int siglen = sizeof(sig); + u_int8_t *ptr = buf; + size_t off = 0; if (dsa->dsa_hmac) { if (!HMAC_Final(dsa->dsa_ctx, sig, &siglen)) @@ -677,7 +802,9 @@ dsa_verify_final(struct iked_dsa *dsa, void *buf, size_t len) if (siglen != len || memcmp(buf, sig, siglen) != 0) return (-1); } else { - if (EVP_VerifyFinal(dsa->dsa_ctx, buf, len, + if ((off = _dsa_verify_offset(dsa, ptr)) >= len) + return (-1); + if (EVP_VerifyFinal(dsa->dsa_ctx, ptr + off, len - off, dsa->dsa_key) != 1) { ca_sslerror(__func__); return (-1); diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h index ab0525c6776..e9da417d39a 100644 --- a/sbin/iked/iked.h +++ b/sbin/iked/iked.h @@ -1,4 +1,4 @@ -/* $OpenBSD: iked.h,v 1.83 2015/01/16 06:39:58 deraadt Exp $ */ +/* $OpenBSD: iked.h,v 1.84 2015/03/26 19:52:35 markus Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -407,6 +407,7 @@ struct iked_sa { struct ibuf *sa_1stmsg; /* for initiator AUTH */ struct ibuf *sa_2ndmsg; /* for responder AUTH */ struct iked_id sa_localauth; /* local AUTH message */ + int sa_sigsha2; /* use SHA2 for signatures */ struct iked_id sa_iid; /* initiator id */ struct iked_id sa_rid; /* responder id */ @@ -732,7 +733,7 @@ struct iked_dsa * struct ibuf * dsa_setkey(struct iked_dsa *, void *, size_t, u_int8_t); void dsa_free(struct iked_dsa *); -int dsa_init(struct iked_dsa *); +int dsa_init(struct iked_dsa *, const void *, size_t); 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 d6a7235cee1..a2c94622a8b 100644 --- a/sbin/iked/ikev2.c +++ b/sbin/iked/ikev2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2.c,v 1.119 2015/02/06 10:39:01 deraadt Exp $ */ +/* $OpenBSD: ikev2.c,v 1.120 2015/03/26 19:52:35 markus Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -54,6 +54,7 @@ struct iked_sa * u_int8_t *, u_int8_t **, size_t *); void ikev2_recv(struct iked *, struct iked_message *); +int ikev2_ike_auth_compatible(struct iked_sa *, u_int8_t, u_int8_t); int ikev2_ike_auth_recv(struct iked *, struct iked_sa *, struct iked_message *); int ikev2_ike_auth(struct iked *, struct iked_sa *); @@ -123,6 +124,9 @@ int ikev2_cp_setaddr(struct iked *, struct iked_sa *); int ikev2_cp_fixaddr(struct iked_sa *, struct iked_addr *, struct iked_addr *); +ssize_t ikev2_add_sighashnotify(struct ibuf *, struct ikev2_payload **, + ssize_t); + static struct privsep_proc procs[] = { { "parent", PROC_PARENT, ikev2_dispatch_parent }, { "ikev1", PROC_IKEV1, ikev2_dispatch_ikev1 }, @@ -494,6 +498,17 @@ done: } int +ikev2_ike_auth_compatible(struct iked_sa *sa, u_int8_t want, u_int8_t have) +{ + if (want == have) + return (0); + if (sa->sa_sigsha2 && + have == IKEV2_AUTH_SIG && want == IKEV2_AUTH_RSA_SIG) + return (0); + return (-1); +} + +int ikev2_ike_auth_recv(struct iked *env, struct iked_sa *sa, struct iked_message *msg) { @@ -579,11 +594,13 @@ ikev2_ike_auth_recv(struct iked *env, struct iked_sa *sa, ikeauth.auth_length = ibuf_size(sa->sa_eapmsk); } - if (msg->msg_auth.id_type != ikeauth.auth_method) { + 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(ikeauth.auth_method, ikev2_auth_map)); + print_map(msg->msg_auth.id_type, ikev2_auth_map)); return (-1); } + ikeauth.auth_method = msg->msg_auth.id_type; if ((authmsg = ikev2_msg_auth(env, sa, sa->sa_hdr.sh_initiator)) == NULL) { @@ -908,6 +925,9 @@ ikev2_init_ike_sa_peer(struct iked *env, struct iked_policy *pol, len += sizeof(*n); } + if ((len = ikev2_add_sighashnotify(buf, &pld, len)) == -1) + goto done; + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; @@ -1477,6 +1497,46 @@ ikev2_add_ipcompnotify(struct iked *env, struct ibuf *e, return (len); } +ssize_t +ikev2_add_sighashnotify(struct ibuf *e, struct ikev2_payload **pld, + ssize_t len) +{ + struct ikev2_notify *n; + u_int8_t *ptr; + size_t i; + u_int16_t hash, signature_hashes[] = { + IKEV2_SIGHASH_SHA2_256, + IKEV2_SIGHASH_SHA2_384, + IKEV2_SIGHASH_SHA2_512 + }; + + if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) + return (-1); + + /* XXX signature_hashes are hardcoded for now */ + len = sizeof(*n) + nitems(signature_hashes) * sizeof(hash); + + /* NOTIFY payload */ + if ((*pld = ikev2_add_payload(e)) == NULL) + return (-1); + if ((ptr = ibuf_advance(e, len)) == NULL) + return (-1); + + n = (struct ikev2_notify *)ptr; + n->n_protoid = 0; + n->n_spisize = 0; + n->n_type = htobe16(IKEV2_N_SIGNATURE_HASH_ALGORITHMS); + ptr += sizeof(*n); + + for (i = 0; i < nitems(signature_hashes); i++) { + hash = htobe16(signature_hashes[i]); + memcpy(ptr, &hash, sizeof(hash)); + ptr += sizeof(hash); + } + + return (len); +} + int ikev2_next_payload(struct ikev2_payload *pld, size_t length, u_int8_t nextpayload) @@ -2085,6 +2145,10 @@ ikev2_resp_ike_sa_init(struct iked *env, struct iked_message *msg) goto done; } + if (sa->sa_sigsha2 && + (len = ikev2_add_sighashnotify(buf, &pld, len)) == -1) + goto done; + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; diff --git a/sbin/iked/ikev2.h b/sbin/iked/ikev2.h index 8d307a41eeb..62831f71be7 100644 --- a/sbin/iked/ikev2.h +++ b/sbin/iked/ikev2.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2.h,v 1.17 2014/11/07 14:02:32 mikeb Exp $ */ +/* $OpenBSD: ikev2.h,v 1.18 2015/03/26 19:52:35 markus Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -350,6 +350,7 @@ struct ikev2_notify { #define IKEV2_N_PSK_CONFIRM 16426 /* RFC6631 */ #define IKEV2_N_ERX_SUPPORTED 16427 /* RFC6867 */ #define IKEV2_N_IFOM_CAPABILITY 16428 /* OA3GPP */ +#define IKEV2_N_SIGNATURE_HASH_ALGORITHMS 16431 /* RFC7427 */ extern struct iked_constmap ikev2_n_map[]; @@ -454,9 +455,20 @@ struct ikev2_auth { #define IKEV2_AUTH_ECDSA_384 10 /* RFC4754 */ #define IKEV2_AUTH_ECDSA_512 11 /* RFC4754 */ #define IKEV2_AUTH_GSPM 12 /* RFC6467 */ +#define IKEV2_AUTH_SIG 14 /* RFC7427 */ extern struct iked_constmap ikev2_auth_map[]; +/* Notifications used together with IKEV2_AUTH_SIG */ + +#define IKEV2_SIGHASH_RESERVED 0 /* RFC7427 */ +#define IKEV2_SIGHASH_SHA1 1 /* RFC7427 */ +#define IKEV2_SIGHASH_SHA2_256 2 /* RFC7427 */ +#define IKEV2_SIGHASH_SHA2_384 3 /* RFC7427 */ +#define IKEV2_SIGHASH_SHA2_512 4 /* RFC7427 */ + +extern struct iked_constmap ikev2_sighash_map[]; + /* * CP payload */ diff --git a/sbin/iked/ikev2_msg.c b/sbin/iked/ikev2_msg.c index d08fd93660a..88ffefc55c6 100644 --- a/sbin/iked/ikev2_msg.c +++ b/sbin/iked/ikev2_msg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2_msg.c,v 1.41 2015/02/15 01:56:42 tedu Exp $ */ +/* $OpenBSD: ikev2_msg.c,v 1.42 2015/03/26 19:52:35 markus Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -761,7 +761,7 @@ ikev2_msg_authverify(struct iked *env, struct iked_sa *sa, print_map(id->id_type, ikev2_cert_map)); if (dsa_setkey(dsa, key, keylen, keytype) == NULL || - dsa_init(dsa) != 0 || + dsa_init(dsa, buf, len) != 0 || dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) { log_debug("%s: failed to compute digital signature", __func__); goto done; @@ -833,7 +833,7 @@ ikev2_msg_authsign(struct iked *env, struct iked_sa *sa, } if (dsa_setkey(dsa, key, keylen, keytype) == NULL || - dsa_init(dsa) != 0 || + dsa_init(dsa, NULL, 0) != 0 || dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) { log_debug("%s: failed to compute digital signature", __func__); goto done; diff --git a/sbin/iked/ikev2_pld.c b/sbin/iked/ikev2_pld.c index 301c2e101b5..766aefc92a3 100644 --- a/sbin/iked/ikev2_pld.c +++ b/sbin/iked/ikev2_pld.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2_pld.c,v 1.49 2015/02/06 10:39:01 deraadt Exp $ */ +/* $OpenBSD: ikev2_pld.c,v 1.50 2015/03/26 19:52:35 markus Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -1104,6 +1104,7 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, u_int16_t type; u_int16_t group; u_int16_t cpi; + u_int16_t signature_hash; u_int8_t transform; if (ikev2_validate_notify(msg, offset, left, pld, &n)) @@ -1253,6 +1254,36 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, msg->msg_sa->sa_cpi_out = betoh16(cpi); } break; + case IKEV2_N_SIGNATURE_HASH_ALGORITHMS: + if (msg->msg_e) { + log_debug("%s: SIGNATURE_HASH_ALGORITHMS: encrypted", + __func__); + return (-1); + } + if (msg->msg_sa == NULL || + msg->msg_sa->sa_sigsha2) { + log_debug("%s: SIGNATURE_HASH_ALGORITHMS: no SA or " + "duplicate notify", __func__); + return (-1); + } + if (len < sizeof(signature_hash) || + len % sizeof(signature_hash)) { + log_debug("%s: malformed signature hash notification" + "(%zu bytes)", __func__, len); + return (0); + } + while (len >= sizeof(signature_hash)) { + memcpy(&signature_hash, buf, sizeof(signature_hash)); + signature_hash = betoh16(signature_hash); + log_debug("%s: signature hash %s (%x)", __func__, + print_map(signature_hash, ikev2_sighash_map), + signature_hash); + len -= sizeof(signature_hash); + buf += sizeof(signature_hash); + if (signature_hash == IKEV2_SIGHASH_SHA2_256) + msg->msg_sa->sa_sigsha2 = 1; + } + break; } return (0); |