summaryrefslogtreecommitdiff
path: root/sbin/iked/ikev2_msg.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/iked/ikev2_msg.c')
-rw-r--r--sbin/iked/ikev2_msg.c195
1 files changed, 147 insertions, 48 deletions
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__);