diff options
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/iked/crypto.c | 105 | ||||
-rw-r--r-- | sbin/iked/iked.conf.5 | 10 | ||||
-rw-r--r-- | sbin/iked/iked.h | 12 | ||||
-rw-r--r-- | sbin/iked/ikev2.c | 64 | ||||
-rw-r--r-- | sbin/iked/ikev2.h | 7 | ||||
-rw-r--r-- | sbin/iked/ikev2_msg.c | 195 | ||||
-rw-r--r-- | sbin/iked/parse.y | 43 | ||||
-rw-r--r-- | sbin/iked/policy.c | 17 |
8 files changed, 367 insertions, 86 deletions
diff --git a/sbin/iked/crypto.c b/sbin/iked/crypto.c index 2218e6a3dd0..66df3cafbee 100644 --- a/sbin/iked/crypto.c +++ b/sbin/iked/crypto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: crypto.c,v 1.27 2020/05/14 15:08:30 tobhe Exp $ */ +/* $OpenBSD: crypto.c,v 1.28 2020/05/26 20:24:31 tobhe Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -92,7 +92,7 @@ hash_new(uint8_t type, uint16_t id) struct iked_hash *hash; const EVP_MD *md = NULL; HMAC_CTX *ctx = NULL; - int length = 0, fixedkey = 0, trunc = 0; + int length = 0, fixedkey = 0, trunc = 0, isaead = 0; switch (type) { case IKEV2_XFORMTYPE_PRF: @@ -156,6 +156,14 @@ hash_new(uint8_t type, uint16_t id) length = SHA512_DIGEST_LENGTH; trunc = 32; break; + case IKEV2_XFORMAUTH_AES_GCM_12: + length = 12; + isaead = 1; + break; + case IKEV2_XFORMAUTH_AES_GCM_16: + length = 16; + isaead = 1; + break; case IKEV2_XFORMAUTH_NONE: case IKEV2_XFORMAUTH_DES_MAC: case IKEV2_XFORMAUTH_KPDK_MD5: @@ -177,7 +185,7 @@ hash_new(uint8_t type, uint16_t id) print_map(id, ikev2_xformtype_map)); break; } - if (md == NULL) + if (!isaead && md == NULL) return (NULL); if ((hash = calloc(1, sizeof(*hash))) == NULL) { @@ -192,6 +200,10 @@ hash_new(uint8_t type, uint16_t id) hash->hash_trunc = trunc; hash->hash_length = length; hash->hash_fixedkey = fixedkey; + hash->hash_isaead = isaead; + + if (isaead) + return (hash); if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { log_debug("%s: alloc hash ctx", __func__); @@ -276,6 +288,7 @@ cipher_new(uint8_t type, uint16_t id, uint16_t id_length) const EVP_CIPHER *cipher = NULL; EVP_CIPHER_CTX *ctx = NULL; int length = 0, fixedkey = 0, ivlength = 0; + int saltlength = 0, authid = 0; switch (type) { case IKEV2_XFORMTYPE_ENCR: @@ -309,6 +322,36 @@ cipher_new(uint8_t type, uint16_t id, uint16_t id_length) ivlength = EVP_CIPHER_iv_length(cipher); fixedkey = EVP_CIPHER_key_length(cipher); break; + case IKEV2_XFORMENCR_AES_GCM_16: + case IKEV2_XFORMENCR_AES_GCM_12: + switch (id_length) { + case 128: + cipher = EVP_aes_128_gcm(); + break; + case 256: + cipher = EVP_aes_256_gcm(); + break; + default: + log_debug("%s: invalid key length %d" + " for cipher %s", __func__, id_length, + print_map(id, ikev2_xformencr_map)); + break; + } + if (cipher == NULL) + break; + switch(id) { + case IKEV2_XFORMENCR_AES_GCM_16: + authid = IKEV2_XFORMAUTH_AES_GCM_16; + break; + case IKEV2_XFORMENCR_AES_GCM_12: + authid = IKEV2_XFORMAUTH_AES_GCM_12; + break; + } + length = EVP_CIPHER_block_size(cipher); + ivlength = 8; + saltlength = 4; + fixedkey = EVP_CIPHER_key_length(cipher) + saltlength; + break; case IKEV2_XFORMENCR_DES_IV64: case IKEV2_XFORMENCR_DES: case IKEV2_XFORMENCR_RC5: @@ -346,6 +389,8 @@ cipher_new(uint8_t type, uint16_t id, uint16_t id_length) encr->encr_length = length; encr->encr_fixedkey = fixedkey; encr->encr_ivlength = ivlength ? ivlength : length; + encr->encr_saltlength = saltlength; + encr->encr_authid = authid; if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { log_debug("%s: alloc cipher ctx", __func__); @@ -392,6 +437,20 @@ cipher_setiv(struct iked_cipher *encr, void *iv, size_t len) return (encr->encr_iv); } +int +cipher_settag(struct iked_cipher *encr, uint8_t *data, size_t len) +{ + return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx, + EVP_CTRL_GCM_SET_TAG, len, data) != 1); +} + +int +cipher_gettag(struct iked_cipher *encr, uint8_t *data, size_t len) +{ + return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx, + EVP_CTRL_GCM_GET_TAG, len, data) != 1); +} + void cipher_free(struct iked_cipher *encr) { @@ -409,11 +468,33 @@ cipher_free(struct iked_cipher *encr) int cipher_init(struct iked_cipher *encr, int enc) { + struct ibuf *nonce = NULL; + int ret = -1; + if (EVP_CipherInit_ex(encr->encr_ctx, encr->encr_priv, NULL, - ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1) + NULL, NULL, enc) != 1) return (-1); + if (encr->encr_saltlength > 0) { + /* For AEADs the nonce is salt + IV (see RFC5282) */ + nonce = ibuf_new(ibuf_data(encr->encr_key) + + ibuf_size(encr->encr_key) - encr->encr_saltlength, + encr->encr_saltlength); + if (nonce == NULL) + return (-1); + if (ibuf_add(nonce, ibuf_data(encr->encr_iv) , ibuf_size(encr->encr_iv)) != 0) + goto done; + if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL, + ibuf_data(encr->encr_key), ibuf_data(nonce), enc) != 1) + goto done; + } else + if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL, + ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1) + return (-1); EVP_CIPHER_CTX_set_padding(encr->encr_ctx, 0); - return (0); + ret = 0; + done: + ibuf_free(nonce); + return (ret); } int @@ -428,6 +509,20 @@ cipher_init_decrypt(struct iked_cipher *encr) return (cipher_init(encr, 0)); } +void +cipher_aad(struct iked_cipher *encr, void *in, size_t inlen, + size_t *outlen) +{ + int olen = 0; + + if (EVP_CipherUpdate(encr->encr_ctx, NULL, &olen, in, inlen) != 1) { + ca_sslerror(__func__); + *outlen = 0; + return; + } + *outlen = (size_t)olen; +} + int cipher_update(struct iked_cipher *encr, void *in, size_t inlen, void *out, size_t *outlen) diff --git a/sbin/iked/iked.conf.5 b/sbin/iked/iked.conf.5 index 26df204822e..654f408d4e7 100644 --- a/sbin/iked/iked.conf.5 +++ b/sbin/iked/iked.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: iked.conf.5,v 1.68 2020/05/01 17:44:02 tobhe Exp $ +.\" $OpenBSD: iked.conf.5,v 1.69 2020/05/26 20:24:31 tobhe 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: May 1 2020 $ +.Dd $Mdocdate: May 26 2020 $ .Dt IKED.CONF 5 .Os .Sh NAME @@ -864,9 +864,11 @@ keyword: .It Li aes-128-ctr Ta "160 bits" Ta "[ESP only]" .It Li aes-192-ctr Ta "224 bits" Ta "[ESP only]" .It Li aes-256-ctr Ta "288 bits" Ta "[ESP only]" -.It Li aes-128-gcm Ta "160 bits" Ta "[ESP only]" +.It Li aes-128-gcm Ta "160 bits" Ta "" .It Li aes-192-gcm Ta "224 bits" Ta "[ESP only]" -.It Li aes-256-gcm Ta "288 bits" Ta "[ESP only]" +.It Li aes-256-gcm Ta "288 bits" Ta "" +.It Li aes-128-gcm-12 Ta "160 bits" Ta "[IKE only]" +.It Li aes-256-gcm-12 Ta "288 bits" Ta "[IKE only]" .It Li blowfish Ta "160 bits" Ta "[ESP only]" .It Li cast Ta "128 bits" Ta "[ESP only]" .It Li chacha20-poly1305 Ta "288 bits" Ta "[ESP only]" diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h index f93fe35cb05..866f342f4a0 100644 --- a/sbin/iked/iked.h +++ b/sbin/iked/iked.h @@ -1,4 +1,4 @@ -/* $OpenBSD: iked.h,v 1.150 2020/05/14 15:08:30 tobhe Exp $ */ +/* $OpenBSD: iked.h,v 1.151 2020/05/26 20:24:31 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de> @@ -309,6 +309,7 @@ struct iked_hash { size_t hash_length; /* Output length */ size_t hash_trunc; /* Truncate the output length */ struct iked_hash *hash_prf; /* PRF pointer */ + int hash_isaead; }; struct iked_cipher { @@ -321,6 +322,8 @@ struct iked_cipher { struct ibuf *encr_iv; /* Initialization Vector */ size_t encr_ivlength; /* IV length */ size_t encr_length; /* Block length */ + size_t encr_saltlength; /* IV salt length */ + uint16_t encr_authid; /* ID of associated authentication */ }; struct iked_dsa { @@ -841,10 +844,13 @@ struct ibuf * cipher_setkey(struct iked_cipher *, void *, size_t); struct ibuf * cipher_setiv(struct iked_cipher *, void *, size_t); +int cipher_settag(struct iked_cipher *, uint8_t *, size_t); +int cipher_gettag(struct iked_cipher *, uint8_t *, size_t); void cipher_free(struct iked_cipher *); int cipher_init(struct iked_cipher *, int); int cipher_init_encrypt(struct iked_cipher *); int cipher_init_decrypt(struct iked_cipher *); +void cipher_aad(struct iked_cipher *, void *, size_t, size_t *); int cipher_update(struct iked_cipher *, void *, size_t, void *, size_t *); int cipher_final(struct iked_cipher *); size_t cipher_length(struct iked_cipher *); @@ -933,7 +939,8 @@ int ikev2_msg_send(struct iked *, struct iked_message *); int ikev2_msg_send_encrypt(struct iked *, struct iked_sa *, struct ibuf **, uint8_t, uint8_t, int); struct ibuf - *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *); + *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *, + struct ibuf *); struct ibuf * ikev2_msg_decrypt(struct iked *, struct iked_sa *, struct ibuf *, struct ibuf *); @@ -1129,5 +1136,6 @@ void print_policy(struct iked_policy *); size_t keylength_xf(unsigned int, unsigned int, unsigned int); size_t noncelength_xf(unsigned int, unsigned int); int cmdline_symset(char *); +int encxf_noauth(unsigned int); #endif /* IKED_H */ diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c index ebc89de0339..5db382cbe92 100644 --- a/sbin/iked/ikev2.c +++ b/sbin/iked/ikev2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2.c,v 1.225 2020/05/11 20:11:35 tobhe Exp $ */ +/* $OpenBSD: ikev2.c,v 1.226 2020/05/26 20:24:31 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de> @@ -4473,7 +4473,7 @@ ikev2_send_informational(struct iked *env, struct iked_message *msg) goto done; /* Encrypt message and add as an E payload */ - if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) { + if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) { log_debug("%s: encryption failed", __func__); goto done; } @@ -4666,6 +4666,16 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa, } } + /* For AEAD ciphers integrity is implicit */ + if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) { + if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR, + sa->sa_encr->encr_authid)) == NULL) { + log_info("%s: failed to get AEAD integr", + SPI_SA(sa, __func__)); + return (-1); + } + } + if (sa->sa_prf == NULL) { if ((xform = config_findtransform(&sa->sa_proposals, IKEV2_XFORMTYPE_PRF, 0)) == NULL) { @@ -4840,6 +4850,16 @@ ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa, } } + /* For AEAD ciphers integrity is implicit */ + if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) { + if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR, + sa->sa_encr->encr_authid)) == NULL) { + log_info("%s: failed to get AEAD integr", + SPI_SA(sa, __func__)); + return (-1); + } + } + if (sa->sa_prf == NULL) { if ((xform = config_findtransform(&sa->sa_proposals, IKEV2_XFORMTYPE_PRF, 0)) == NULL) { @@ -4883,6 +4903,7 @@ ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key) size_t nonceminlen, ilen, rlen, tmplen; uint64_t ispi, rspi; int ret = -1; + int isaead = 0; ninr = dhsecret = skeyseed = s = t = NULL; @@ -4895,6 +4916,9 @@ ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key) return (-1); } + /* For AEADs no auth keys are required (see RFC 5282) */ + isaead = !!integr->hash_isaead; + if (prf->hash_fixedkey) nonceminlen = prf->hash_fixedkey; else @@ -5026,13 +5050,13 @@ ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key) * Get the size of the key material we need and the number * of rounds we need to run the prf+ function. */ - ilen = hash_length(prf) + /* SK_d */ - hash_keylength(integr) + /* SK_ai */ - hash_keylength(integr) + /* SK_ar */ - cipher_keylength(encr) + /* SK_ei */ - cipher_keylength(encr) + /* SK_er */ - hash_keylength(prf) + /* SK_pi */ - hash_keylength(prf); /* SK_pr */ + ilen = hash_length(prf) + /* SK_d */ + (isaead ? 0 : hash_keylength(integr)) + /* SK_ai */ + (isaead ? 0 : hash_keylength(integr)) + /* SK_ar */ + cipher_keylength(encr) + /* SK_ei */ + cipher_keylength(encr) + /* SK_er */ + hash_keylength(prf) + /* SK_pi */ + hash_keylength(prf); /* SK_pr */ if ((t = ikev2_prfplus(prf, skeyseed, s, ilen)) == NULL) { log_info("%s: failed to get IKE SA key material", @@ -5042,8 +5066,10 @@ ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key) /* ibuf_get() returns a new buffer from the next read offset */ if ((sa->sa_key_d = ibuf_get(t, hash_length(prf))) == NULL || - (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL || - (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL || + (!isaead && + (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL) || + (!isaead && + (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL) || (sa->sa_key_iencr = ibuf_get(t, cipher_keylength(encr))) == NULL || (sa->sa_key_rencr = ibuf_get(t, cipher_keylength(encr))) == NULL || (sa->sa_key_iprf = ibuf_get(t, hash_length(prf))) == NULL || @@ -5055,12 +5081,16 @@ ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key) log_debug("%s: SK_d with %zu bytes", __func__, ibuf_length(sa->sa_key_d)); print_hex(sa->sa_key_d->buf, 0, ibuf_length(sa->sa_key_d)); - log_debug("%s: SK_ai with %zu bytes", __func__, - ibuf_length(sa->sa_key_iauth)); - print_hex(sa->sa_key_iauth->buf, 0, ibuf_length(sa->sa_key_iauth)); - log_debug("%s: SK_ar with %zu bytes", __func__, - ibuf_length(sa->sa_key_rauth)); - print_hex(sa->sa_key_rauth->buf, 0, ibuf_length(sa->sa_key_rauth)); + if (!isaead) { + log_debug("%s: SK_ai with %zu bytes", __func__, + ibuf_length(sa->sa_key_iauth)); + print_hex(sa->sa_key_iauth->buf, 0, + ibuf_length(sa->sa_key_iauth)); + log_debug("%s: SK_ar with %zu bytes", __func__, + ibuf_length(sa->sa_key_rauth)); + print_hex(sa->sa_key_rauth->buf, 0, + ibuf_length(sa->sa_key_rauth)); + } log_debug("%s: SK_ei with %zu bytes", __func__, ibuf_length(sa->sa_key_iencr)); print_hex(sa->sa_key_iencr->buf, 0, ibuf_length(sa->sa_key_iencr)); diff --git a/sbin/iked/ikev2.h b/sbin/iked/ikev2.h index 09a28a4397b..074928f8699 100644 --- a/sbin/iked/ikev2.h +++ b/sbin/iked/ikev2.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2.h,v 1.32 2020/04/28 15:18:52 tobhe Exp $ */ +/* $OpenBSD: ikev2.h,v 1.33 2020/05/26 20:24:31 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de> @@ -225,6 +225,11 @@ extern struct iked_constmap ikev2_xformprf_map[]; #define IKEV2_XFORMAUTH_HMAC_SHA2_384_192 13 /* RFC4868 */ #define IKEV2_XFORMAUTH_HMAC_SHA2_512_256 14 /* RFC4868 */ +/* Placeholders for AEAD ciphers (only used internally) */ +#define IKEV2_XFORMAUTH_AES_GCM_8 2018 /* internal */ +#define IKEV2_XFORMAUTH_AES_GCM_12 2019 /* internal */ +#define IKEV2_XFORMAUTH_AES_GCM_16 2020 /* internal */ + extern struct iked_constmap ikev2_xformauth_map[]; #define IKEV2_XFORMDH_NONE 0 /* No DH */ diff --git a/sbin/iked/ikev2_msg.c b/sbin/iked/ikev2_msg.c index bdd88abb678..cc76dfde3f5 100644 --- a/sbin/iked/ikev2_msg.c +++ b/sbin/iked/ikev2_msg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2_msg.c,v 1.67 2020/05/14 15:08:30 tobhe Exp $ */ +/* $OpenBSD: ikev2_msg.c,v 1.68 2020/05/26 20:24:31 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de> @@ -51,6 +51,8 @@ void ikev2_msg_retransmit_timeout(struct iked *, void *); int ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf); int ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa, struct ibuf *in,uint8_t exchange, uint8_t firstpayload, int response); +int ikev2_msg_encrypt_prepare(struct iked_sa *, struct ikev2_payload *, + struct ibuf*, struct ibuf *, struct ike_header *, uint8_t, int); void ikev2_msg_cb(int fd, short event, void *arg) @@ -329,8 +331,46 @@ ikev2_msg_id(struct iked *env, struct iked_sa *sa) return (id); } +/* + * Calculate the final sizes of the IKEv2 header and the encrypted payload + * header. This must be done before encryption to make sure the correct + * headers are authenticated. + */ +int +ikev2_msg_encrypt_prepare(struct iked_sa *sa, struct ikev2_payload *pld, + struct ibuf *buf, struct ibuf *e, struct ike_header *hdr, + uint8_t firstpayload, int fragmentation) +{ + size_t len, ivlen, encrlen, integrlen, blocklen, pldlen, outlen; + + if (sa == NULL || + sa->sa_encr == NULL || + sa->sa_integr == NULL) { + log_debug("%s: invalid SA", __func__); + return (-1); + } + + len = ibuf_size(e); + blocklen = cipher_length(sa->sa_encr); + integrlen = hash_length(sa->sa_integr); + ivlen = cipher_ivlength(sa->sa_encr); + encrlen = roundup(len + 1, blocklen); + outlen = cipher_outlength(sa->sa_encr, encrlen); + pldlen = ivlen + outlen + integrlen; + + if (ikev2_next_payload(pld, + pldlen + (fragmentation ? sizeof(struct ikev2_frag_payload) : 0), + firstpayload) == -1) + return (-1); + if (ikev2_set_header(hdr, ibuf_size(buf) + pldlen - sizeof(*hdr)) == -1) + return (-1); + + return (0); +} + struct ibuf * -ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src) +ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src, + struct ibuf *aad) { size_t len, ivlen, encrlen, integrlen, blocklen, outlen; @@ -390,12 +430,23 @@ ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src) goto done; outlen = ibuf_size(out); + + /* Add AAD for AEAD ciphers */ + if (sa->sa_integr->hash_isaead) + cipher_aad(sa->sa_encr, ibuf_data(aad), + ibuf_length(aad), &outlen); + if (cipher_update(sa->sa_encr, ibuf_data(src), encrlen, ibuf_data(out), &outlen) == -1) { log_info("%s: error updating cipher.", __func__); goto done; } + if (cipher_final(sa->sa_encr) == -1) { + log_info("%s: encryption failed.", __func__); + goto done; + } + if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0) goto done; @@ -429,18 +480,13 @@ ikev2_msg_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src) print_hex(ibuf_data(src), 0, ibuf_size(src)); if (sa == NULL || + sa->sa_encr == NULL || sa->sa_integr == NULL) { log_debug("%s: invalid SA", __func__); return (-1); } - if (sa->sa_hdr.sh_initiator) - integr = sa->sa_key_iauth; - else - integr = sa->sa_key_rauth; - integrlen = hash_length(sa->sa_integr); - log_debug("%s: integrity checksum length %zu", __func__, integrlen); @@ -450,21 +496,33 @@ ikev2_msg_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src) if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL) goto done; - hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr)); - hash_init(sa->sa_integr); - hash_update(sa->sa_integr, ibuf_data(src), - ibuf_size(src) - integrlen); - hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen); - - if (tmplen != integrlen) { - log_debug("%s: hash failure", __func__); - goto done; + if (!sa->sa_integr->hash_isaead) { + if (sa->sa_hdr.sh_initiator) + integr = sa->sa_key_iauth; + else + integr = sa->sa_key_rauth; + + hash_setkey(sa->sa_integr, ibuf_data(integr), + ibuf_size(integr)); + hash_init(sa->sa_integr); + hash_update(sa->sa_integr, ibuf_data(src), + ibuf_size(src) - integrlen); + hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen); + + if (tmplen != integrlen) { + log_debug("%s: hash failure", __func__); + goto done; + } + } else { + /* Append AEAD tag */ + if (cipher_gettag(sa->sa_encr, ibuf_data(tmp), ibuf_size(tmp))) + goto done; } if ((ptr = ibuf_seek(src, ibuf_size(src) - integrlen, integrlen)) == NULL) goto done; - memcpy(ptr, ibuf_data(tmp), tmplen); + memcpy(ptr, ibuf_data(tmp), integrlen); print_hex(ibuf_data(tmp), 0, ibuf_size(tmp)); @@ -481,7 +539,7 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, { ssize_t ivlen, encrlen, integrlen, blocklen, outlen, tmplen; - uint8_t pad = 0, *ptr; + uint8_t pad = 0, *ptr, *integrdata; struct ibuf *integr, *encr, *tmp = NULL, *out = NULL; off_t ivoff, encroff, integroff; @@ -524,25 +582,30 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, /* * Validate packet checksum */ - if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL) - goto done; + if (!sa->sa_integr->hash_isaead) { + if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL) + goto done; - hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr)); - hash_init(sa->sa_integr); - hash_update(sa->sa_integr, ibuf_data(msg), - ibuf_size(msg) - integrlen); - hash_final(sa->sa_integr, tmp->buf, &tmplen); + hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr)); + hash_init(sa->sa_integr); + hash_update(sa->sa_integr, ibuf_data(msg), + ibuf_size(msg) - integrlen); + hash_final(sa->sa_integr, tmp->buf, &tmplen); - if (memcmp(tmp->buf, ibuf_data(src) + integroff, integrlen) != 0) { - log_debug("%s: integrity check failed", __func__); - goto done; - } + integrdata = ibuf_seek(src, integroff, integrlen); + if (integrdata == NULL) + goto done; + if (memcmp(tmp->buf, integrdata, integrlen) != 0) { + log_debug("%s: integrity check failed", __func__); + goto done; + } - log_debug("%s: integrity check succeeded", __func__); - print_hex(tmp->buf, 0, tmplen); + log_debug("%s: integrity check succeeded", __func__); + print_hex(tmp->buf, 0, tmplen); - ibuf_release(tmp); - tmp = NULL; + ibuf_release(tmp); + tmp = NULL; + } /* * Decrypt the payload and strip any padding @@ -559,10 +622,31 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, goto done; } + /* Set AEAD tag */ + if (sa->sa_integr->hash_isaead) { + integrdata = ibuf_seek(src, integroff, integrlen); + if (integrdata == NULL) + goto done; + if (cipher_settag(sa->sa_encr, integrdata, integrlen)) { + log_info("%s: failed to set tag.", __func__); + goto done; + } + } + if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr, encrlen))) == NULL) goto done; + /* + * Add additional authenticated data for AEAD ciphers + */ + if (sa->sa_integr->hash_isaead) { + log_debug("%s: AAD length %zu", __func__, ibuf_length(msg) - ibuf_length(src)); + print_hex(ibuf_data(msg), 0, ibuf_length(msg) - ibuf_length(src)); + cipher_aad(sa->sa_encr, ibuf_data(msg), + ibuf_length(msg) - ibuf_length(src), &outlen); + } + if ((outlen = ibuf_length(out)) != 0) { if (cipher_update(sa->sa_encr, ibuf_data(src) + encroff, encrlen, ibuf_data(out), &outlen) == -1) { @@ -574,6 +658,11 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, pad = *ptr; } + if (cipher_final(sa->sa_encr) == -1) { + log_info("%s: decryption failed.", __func__); + goto done; + } + log_debug("%s: decrypted payload length %zd/%zd padding %d", __func__, outlen, encrlen, pad); print_hex(ibuf_data(out), 0, ibuf_size(out)); @@ -598,6 +687,13 @@ ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf) { size_t max; size_t ivlen, integrlen, blocklen; + if (sa == NULL || + sa->sa_encr == NULL || + sa->sa_integr == NULL) { + log_debug("%s: invalid SA", __func__); + return (-1); + } + sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family; max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG @@ -642,18 +738,16 @@ ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf **ep, if ((pld = ikev2_add_payload(buf)) == NULL) goto done; + if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 0) == -1) + goto done; + /* Encrypt message and add as an E payload */ - if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) { + if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) { log_debug("%s: encryption failed", __func__); goto done; } if (ibuf_cat(buf, e) != 0) goto done; - if (ikev2_next_payload(pld, ibuf_size(e), firstpayload) == -1) - goto done; - - if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) - goto done; /* Add integrity checksum (HMAC) */ if (ikev2_msg_integr(env, sa, buf) != 0) { @@ -694,6 +788,13 @@ ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa, uint32_t msgid; int ret = -1; + if (sa == NULL || + sa->sa_encr == NULL || + sa->sa_integr == NULL) { + log_debug("%s: invalid SA", __func__); + goto done; + } + sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family; left = ibuf_length(in); @@ -743,20 +844,18 @@ ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa, if ((e = ibuf_new(data, MIN(left, max_len))) == NULL) { goto done; } - if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) { + + if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, + firstpayload, 1) == -1) + goto done; + + if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) { log_debug("%s: encryption failed", __func__); goto done; } if (ibuf_cat(buf, e) != 0) goto done; - if (ikev2_next_payload(pld, ibuf_size(e) + sizeof(*frag), - firstpayload) == -1) - goto done; - - if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) - goto done; - /* Add integrity checksum (HMAC) */ if (ikev2_msg_integr(env, sa, buf) != 0) { log_debug("%s: integrity checksum failed", __func__); diff --git a/sbin/iked/parse.y b/sbin/iked/parse.y index 71bdbc0ab24..2cb00c29833 100644 --- a/sbin/iked/parse.y +++ b/sbin/iked/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.99 2020/04/30 21:11:13 tobhe Exp $ */ +/* $OpenBSD: parse.y,v 1.100 2020/05/26 20:24:31 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de> @@ -198,6 +198,10 @@ const struct ipsec_xf ikeencxfs[] = { { "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 }, { "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 }, { "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 }, + { "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 }, + { "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 }, + { "aes-128-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 16, 16, 4, 1 }, + { "aes-256-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 32, 32, 4, 1 }, { NULL } }; @@ -2417,6 +2421,17 @@ print_xf(unsigned int id, unsigned int length, const struct ipsec_xf xfs[]) return ("unknown"); } +int +encxf_noauth(unsigned int id) +{ + int i; + + for (i = 0; ikeencxfs[i].name != NULL; i++) + if (ikeencxfs[i].id == id) + return ikeencxfs[i].noauth; + return (0); +} + size_t keylength_xf(unsigned int saproto, unsigned int type, unsigned int id) { @@ -2852,21 +2867,37 @@ create_ike(char *name, int af, uint8_t ipproto, pol.pol_nproposals++; } else { for (i = 0; i < ike_sa->nxfs; i++) { + noauth = 0; + for (j = 0; j < ike_sa->xfs[i]->nencxf; j++) { + if (ike_sa->xfs[i]->encxf[j]->noauth) + noauth++; + } if (ike_sa->xfs[i]->nesnxf) { yyerror("cannot use ESN with ikesa."); goto done; } + if (noauth && noauth != ike_sa->xfs[i]->nencxf) { + yyerror("cannot mix encryption transforms with " + "implicit and non-implicit authentication"); + goto done; + } + if (noauth && ike_sa->xfs[i]->nauthxf) { + yyerror("authentication is implicit for given " + "encryption transforms"); + goto done; + } if ((p = calloc(1, sizeof(*p))) == NULL) err(1, "%s", __func__); xf = NULL; xfi = 0; - copy_transforms(IKEV2_XFORMTYPE_INTEGR, - ike_sa->xfs[i]->authxf, - ike_sa->xfs[i]->nauthxf, &xf, &xfi, - ikev2_default_ike_transforms, - ikev2_default_nike_transforms); + if (!ike_sa->xfs[i]->nencxf || !noauth) + copy_transforms(IKEV2_XFORMTYPE_INTEGR, + ike_sa->xfs[i]->authxf, + ike_sa->xfs[i]->nauthxf, &xf, &xfi, + ikev2_default_ike_transforms, + ikev2_default_nike_transforms); copy_transforms(IKEV2_XFORMTYPE_ENCR, ike_sa->xfs[i]->encxf, ike_sa->xfs[i]->nencxf, &xf, &xfi, diff --git a/sbin/iked/policy.c b/sbin/iked/policy.c index 735284af401..8992f37bd99 100644 --- a/sbin/iked/policy.c +++ b/sbin/iked/policy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: policy.c,v 1.62 2020/05/13 23:03:20 tobhe Exp $ */ +/* $OpenBSD: policy.c,v 1.63 2020/05/26 20:24:31 tobhe Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -774,7 +774,7 @@ proposals_match(struct iked_proposal *local, struct iked_proposal *peer, struct iked_transform **xforms, int rekey) { struct iked_transform *tpeer, *tlocal; - unsigned int i, j, type, score, requiredh = 0; + unsigned int i, j, type, score, requiredh = 0, noauth = 0; uint8_t protoid = peer->prop_protoid; uint8_t peerxfs[IKEV2_XFORMTYPE_MAX]; @@ -782,8 +782,18 @@ proposals_match(struct iked_proposal *local, struct iked_proposal *peer, for (i = 0; i < peer->prop_nxforms; i++) { tpeer = peer->prop_xforms + i; + /* If any of the ENC transforms is an AEAD, ignore auth */ + if (tpeer->xform_type == IKEV2_XFORMTYPE_ENCR && + encxf_noauth(tpeer->xform_id)) + noauth = 1; + } + + for (i = 0; i < peer->prop_nxforms; i++) { + tpeer = peer->prop_xforms + i; if (tpeer->xform_type > IKEV2_XFORMTYPE_MAX) continue; + if (noauth && tpeer->xform_type == IKEV2_XFORMTYPE_INTEGR) + return (0); /* * Record all transform types from the peer's proposal, @@ -832,7 +842,8 @@ proposals_match(struct iked_proposal *local, struct iked_proposal *peer, for (i = score = 0; i < IKEV2_XFORMTYPE_MAX; i++) { if (protoid == IKEV2_SAPROTO_IKE && xforms[i] == NULL && (i == IKEV2_XFORMTYPE_ENCR || i == IKEV2_XFORMTYPE_PRF || - i == IKEV2_XFORMTYPE_INTEGR || i == IKEV2_XFORMTYPE_DH)) { + (!noauth && i == IKEV2_XFORMTYPE_INTEGR) || + i == IKEV2_XFORMTYPE_DH)) { score = 0; break; } else if (protoid == IKEV2_SAPROTO_AH && xforms[i] == NULL && |