diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2014-04-10 16:08:03 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2014-04-10 16:08:03 +0000 |
commit | 4ac3aca85959e396fae89a6973f0aeb990ad0f7a (patch) | |
tree | fc743205032a24f6612f837ad31417675f67755b /sbin/iked | |
parent | 60a1df5327a922f1ad07339a8d3200103b181810 (diff) |
Add validation routines to ikev2_pld.c: For each payload type overall
header structure is checked for sanity before copying the header.
Always pass down the number of remaining bytes in the payload or
substructure so we can always ensure to not go beyond actual data.
Also remove the quick parsing step as it does not provide a real
benefit anymore.
From Hans-Joerg Hoexer
ok mikeb@ markus@
Diffstat (limited to 'sbin/iked')
-rw-r--r-- | sbin/iked/iked.h | 6 | ||||
-rw-r--r-- | sbin/iked/ikev2.c | 4 | ||||
-rw-r--r-- | sbin/iked/ikev2_msg.c | 11 | ||||
-rw-r--r-- | sbin/iked/ikev2_pld.c | 853 |
4 files changed, 717 insertions, 157 deletions
diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h index 08b6f589316..e7093c718fc 100644 --- a/sbin/iked/iked.h +++ b/sbin/iked/iked.h @@ -1,4 +1,4 @@ -/* $OpenBSD: iked.h,v 1.70 2014/02/21 20:52:38 markus Exp $ */ +/* $OpenBSD: iked.h,v 1.71 2014/04/10 16:08:02 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -434,7 +434,7 @@ RB_HEAD(iked_addrpool, iked_sa); struct iked_message { struct ibuf *msg_data; - off_t msg_offset; + size_t msg_offset; struct sockaddr_storage msg_local; socklen_t msg_locallen; @@ -784,7 +784,7 @@ struct iked_message * /* ikev2_pld.c */ int ikev2_pld_parse(struct iked *, struct ike_header *, - struct iked_message *, off_t); + struct iked_message *, size_t); /* eap.c */ ssize_t eap_identity_request(struct ibuf *); diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c index 2d6df4d1e14..ffcbce7474e 100644 --- a/sbin/iked/ikev2.c +++ b/sbin/iked/ikev2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2.c,v 1.99 2014/03/12 14:28:09 markus Exp $ */ +/* $OpenBSD: ikev2.c,v 1.100 2014/04/10 16:08:02 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -370,7 +370,7 @@ ikev2_recv(struct iked *env, struct iked_message *msg) hdr = ibuf_seek(msg->msg_data, msg->msg_offset, sizeof(*hdr)); - if (hdr == NULL || (ssize_t)ibuf_size(msg->msg_data) < + if (hdr == NULL || ibuf_size(msg->msg_data) < (betoh32(hdr->ike_length) - msg->msg_offset)) return; diff --git a/sbin/iked/ikev2_msg.c b/sbin/iked/ikev2_msg.c index ade5b51be72..1ba719c2c15 100644 --- a/sbin/iked/ikev2_msg.c +++ b/sbin/iked/ikev2_msg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2_msg.c,v 1.29 2014/02/17 11:00:14 reyk Exp $ */ +/* $OpenBSD: ikev2_msg.c,v 1.30 2014/04/10 16:08:02 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -135,11 +135,14 @@ ikev2_msg_copy(struct iked *env, struct iked_message *msg) { struct iked_message *m = NULL; struct ibuf *buf; - ssize_t len; + size_t len; void *ptr; - if ((len = ibuf_size(msg->msg_data) - msg->msg_offset) <= 0 || - (ptr = ibuf_seek(msg->msg_data, msg->msg_offset, len)) == NULL || + if (ibuf_size(msg->msg_data) < msg->msg_offset) + return (NULL); + len = ibuf_size(msg->msg_data) - msg->msg_offset; + + if ((ptr = ibuf_seek(msg->msg_data, msg->msg_offset, len)) == NULL || (m = malloc(sizeof(*m))) == NULL || (buf = ikev2_msg_init(env, m, &msg->msg_peer, msg->msg_peerlen, &msg->msg_local, msg->msg_locallen, msg->msg_response)) == NULL || diff --git a/sbin/iked/ikev2_pld.c b/sbin/iked/ikev2_pld.c index 4ec1c5ae6be..a271163f085 100644 --- a/sbin/iked/ikev2_pld.c +++ b/sbin/iked/ikev2_pld.c @@ -1,7 +1,8 @@ -/* $OpenBSD: ikev2_pld.c,v 1.38 2014/02/17 11:00:14 reyk Exp $ */ +/* $OpenBSD: ikev2_pld.c,v 1.39 2014/04/10 16:08:02 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2014 Hans-Joerg Hoexer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -45,42 +46,72 @@ #include "eap.h" #include "dh.h" +int ikev2_validate_pld(struct iked_message *, size_t, size_t, + struct ikev2_payload *); int ikev2_pld_payloads(struct iked *, struct iked_message *, - off_t, size_t, u_int, int); + size_t, size_t, u_int); +int ikev2_validate_sa(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_sa_proposal *); int ikev2_pld_sa(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_xform(struct iked_message *, size_t, size_t, + struct ikev2_transform *); int ikev2_pld_xform(struct iked *, struct ikev2_sa_proposal *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_attr(struct iked_message *, size_t, size_t, + struct ikev2_attribute *); int ikev2_pld_attr(struct iked *, struct ikev2_transform *, - struct iked_message *, off_t, int); + struct iked_message *, size_t, size_t); +int ikev2_validate_ke(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_keyexchange *); int ikev2_pld_ke(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_id(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_id *); int ikev2_pld_id(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t, u_int); + struct iked_message *, size_t, size_t, u_int); +int ikev2_validate_cert(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_cert *); int ikev2_pld_cert(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_certreq(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_cert *); int ikev2_pld_certreq(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_nonce(struct iked_message *, size_t, size_t, + struct ikev2_payload *); int ikev2_pld_nonce(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_notify(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_notify *); int ikev2_pld_notify(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_delete(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_delete *); int ikev2_pld_delete(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_ts(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_tsp *); int ikev2_pld_ts(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t, u_int); + struct iked_message *, size_t, size_t, u_int); +int ikev2_validate_auth(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_auth *); int ikev2_pld_auth(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); int ikev2_pld_e(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t); +int ikev2_validate_cp(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_cp *); int ikev2_pld_cp(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_eap(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct eap_header *); int ikev2_pld_eap(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); int ikev2_pld_parse(struct iked *env, struct ike_header *hdr, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset) { log_debug("%s: header ispi %s rspi %s" " nextpayload %s version 0x%02x exchange %s flags 0x%02x" @@ -103,94 +134,130 @@ ikev2_pld_parse(struct iked *env, struct ike_header *hdr, offset += sizeof(*hdr); return (ikev2_pld_payloads(env, msg, offset, - betoh32(hdr->ike_length), hdr->ike_nextpayload, 0)); + betoh32(hdr->ike_length), hdr->ike_nextpayload)); +} + +int +ikev2_validate_pld(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + /* We need at least the generic header. */ + if (left < sizeof(*pld)) { + log_debug("%s: malformed payload: too short for generic " + "header (%zu < %zu)", __func__, left, sizeof(*pld)); + return (-1); + } + memcpy(pld, msgbuf + offset, sizeof(*pld)); + + /* + * We need at least the specified number of bytes. + * pld_length is the full size of the payload including + * the generic payload header. + */ + pld_length = betoh16(pld->pld_length); + if (left < pld_length) { + log_debug("%s: malformed payload: shorter than specified " + "(%zu < %zu)", __func__, left, pld_length); + return (-1); + } + /* + * Sanity check the specified payload size, it must + * be at last the size of the generic payload header. + */ + if (pld_length < sizeof(*pld)) { + log_debug("%s: malformed payload: shorter than minimum " + "header size (%zu < %zu)", __func__, pld_length, + sizeof(*pld)); + return (-1); + } + + return (0); } int ikev2_pld_payloads(struct iked *env, struct iked_message *msg, - off_t offset, size_t length, u_int payload, int quick) + size_t offset, size_t length, u_int payload) { struct ikev2_payload pld; u_int e; int ret; u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t left; /* Check if message was decrypted in an E payload */ e = msg->msg_e ? IKED_E : 0; - if (quick) - print_debug("%s: %spayloads", __func__, - e ? "decrypted " : ""); - else - ikev2_pld_payloads(env, msg, offset, length, payload, 1); + while (payload != 0 && offset < length) { + /* Bytes left in datagram. */ + left = length - offset; - while (payload != 0 && offset < (off_t)length) { - memcpy(&pld, msgbuf + offset, sizeof(pld)); + if (ikev2_validate_pld(msg, offset, left, &pld)) + return (-1); - if (quick) - print_debug(" %s", - print_map(payload, ikev2_payload_map)); - else - log_debug("%s: %spayload %s" - " nextpayload %s critical 0x%02x length %d", - __func__, e ? "decrypted " : "", - print_map(payload, ikev2_payload_map), - print_map(pld.pld_nextpayload, ikev2_payload_map), - pld.pld_reserved & IKEV2_CRITICAL_PAYLOAD, - betoh16(pld.pld_length)); + log_debug("%s: %spayload %s" + " nextpayload %s critical 0x%02x length %d", + __func__, e ? "decrypted " : "", + print_map(payload, ikev2_payload_map), + print_map(pld.pld_nextpayload, ikev2_payload_map), + pld.pld_reserved & IKEV2_CRITICAL_PAYLOAD, + betoh16(pld.pld_length)); + /* Skip over generic payload header. */ offset += sizeof(pld); + left -= sizeof(pld); ret = 0; - if (quick) - goto next; - switch (payload | e) { case IKEV2_PAYLOAD_SA: case IKEV2_PAYLOAD_SA | IKED_E: - ret = ikev2_pld_sa(env, &pld, msg, offset); + ret = ikev2_pld_sa(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_KE: case IKEV2_PAYLOAD_KE | IKED_E: - ret = ikev2_pld_ke(env, &pld, msg, offset); + ret = ikev2_pld_ke(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_IDi | IKED_E: case IKEV2_PAYLOAD_IDr | IKED_E: - ret = ikev2_pld_id(env, &pld, msg, offset, payload); + ret = ikev2_pld_id(env, &pld, msg, offset, left, + payload); break; case IKEV2_PAYLOAD_CERT | IKED_E: - ret = ikev2_pld_cert(env, &pld, msg, offset); + ret = ikev2_pld_cert(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_CERTREQ: case IKEV2_PAYLOAD_CERTREQ | IKED_E: - ret = ikev2_pld_certreq(env, &pld, msg, offset); + ret = ikev2_pld_certreq(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_AUTH | IKED_E: - ret = ikev2_pld_auth(env, &pld, msg, offset); + ret = ikev2_pld_auth(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_NONCE: case IKEV2_PAYLOAD_NONCE | IKED_E: - ret = ikev2_pld_nonce(env, &pld, msg, offset); + ret = ikev2_pld_nonce(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_NOTIFY: case IKEV2_PAYLOAD_NOTIFY | IKED_E: - ret = ikev2_pld_notify(env, &pld, msg, offset); + ret = ikev2_pld_notify(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_DELETE | IKED_E: - ret = ikev2_pld_delete(env, &pld, msg, offset); + ret = ikev2_pld_delete(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_TSi | IKED_E: case IKEV2_PAYLOAD_TSr | IKED_E: - ret = ikev2_pld_ts(env, &pld, msg, offset, payload); + ret = ikev2_pld_ts(env, &pld, msg, offset, left, + payload); break; case IKEV2_PAYLOAD_SK: ret = ikev2_pld_e(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_CP | IKED_E: - ret = ikev2_pld_cp(env, &pld, msg, offset); + ret = ikev2_pld_cp(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_EAP | IKED_E: - ret = ikev2_pld_eap(env, &pld, msg, offset); + ret = ikev2_pld_eap(env, &pld, msg, offset, left); break; default: print_hex(msgbuf, offset, @@ -207,20 +274,87 @@ ikev2_pld_payloads(struct iked *env, struct iked_message *msg, if (payload == IKEV2_PAYLOAD_SK) return (0); - next: payload = pld.pld_nextpayload; offset += betoh16(pld.pld_length) - sizeof(pld); } - if (quick) - print_debug("\n"); + return (0); +} + +int +ikev2_validate_sa(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_sa_proposal *sap) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length, sap_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*sap)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*sap)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*sap)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*sap)); + return (-1); + } + memcpy(sap, msgbuf + offset, sizeof(*sap)); + sap_length = betoh16(sap->sap_length); + if (sap_length < sizeof(*sap)) { + log_debug("%s: malformed payload: shorter than minimum header " + "size (%zu < %zu)", __func__, sap_length, sizeof(*sap)); + return (-1); + } + if (left < sap_length) { + log_debug("%s: malformed payload: too long for actual payload " + "size (%zu < %zu)", __func__, left, sap_length); + return (-1); + } + /* + * NB: There might be more proposals, we parse only the first one. + * This condition must never be true. + */ + if (pld_length - sizeof(*pld) < sap_length) { + log_debug("%s: payload malformed: SA payload length mismatches " + "proposal substructure length (%lu < %zu)", __func__, + pld_length - sizeof(*pld), sap_length); + return (-1); + } + /* + * If there is only one proposal, sap_length must be the + * total payload size. + */ + if (!sap->sap_more && ((pld_length - sizeof(*pld)) != sap_length)) { + log_debug("%s: payload malformed: SA payload length mismatches " + "single proposal substructure length (%lu != %zu)", + __func__, pld_length - sizeof(*pld), sap_length); + return (-1); + } + /* + * If there are more than one proposal, there must be bytes + * left in the payload. + */ + if (sap->sap_more && ((pld_length - sizeof(*pld)) <= sap_length)) { + log_debug("%s: payload malformed: SA payload too small for " + "further proposals (%zu <= %zu)", __func__, + pld_length - sizeof(*pld), sap_length); + return (-1); + } return (0); } +/* + * NB: This function parses both the SA header and the first proposal. + * Additional proposals are ignored. + */ int ikev2_pld_sa(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct ikev2_sa_proposal sap; struct iked_proposal *prop = NULL; @@ -228,13 +362,40 @@ ikev2_pld_sa(struct iked *env, struct ikev2_payload *pld, u_int64_t spi = 0, spi64; u_int8_t *msgbuf = ibuf_data(msg->msg_data); struct iked_proposals *props; + size_t total; + + if (ikev2_validate_sa(msg, offset, left, pld, &sap)) + return (-1); + + if (sap.sap_more) + log_debug("%s: more than one proposal specified", __func__); + + /* Assumed size of the first proposals, including SPI if present. */ + total = (betoh16(sap.sap_length) - sizeof(sap)); props = &msg->msg_parent->msg_proposals; - memcpy(&sap, msgbuf + offset, sizeof(sap)); offset += sizeof(sap); + left -= sizeof(sap); if (sap.sap_spisize) { + if (left < sap.sap_spisize) { + log_debug("%s: malformed payload: SPI larger than " + "actual payload (%zu < %d)", __func__, left, + sap.sap_spisize); + return (-1); + } + if (total < sap.sap_spisize) { + log_debug("%s: malformed payload: SPI larger than " + "proposal (%zu < %d)", __func__, total, + sap.sap_spisize); + return (-1); + } + if (total < sap.sap_spisize) { + log_debug("%s: malformed payload: SPI too large " + "(%zu < %d)", __func__, total, sap.sap_spisize); + return (-1); + } switch (sap.sap_spisize) { case 4: memcpy(&spi32, msgbuf + offset, 4); @@ -251,6 +412,20 @@ ikev2_pld_sa(struct iked *env, struct ikev2_payload *pld, } offset += sap.sap_spisize; + left -= sap.sap_spisize; + + /* Assumed size of the proposal, now without SPI. */ + total -= sap.sap_spisize; + } + + /* + * As we verified sanity of packet headers, this check will + * be always false, but just to be sure we keep it. + */ + if (left < total) { + log_debug("%s: payload malformed: too long for payload " + "(%zu < %zu)", __func__, left, total); + return (-1); } log_debug("%s: more %d reserved %d length %d" @@ -279,7 +454,7 @@ ikev2_pld_sa(struct iked *env, struct ikev2_payload *pld, * Parse the attached transforms */ if (sap.sap_transforms && - ikev2_pld_xform(env, &sap, msg, offset) != 0) { + ikev2_pld_xform(env, &sap, msg, offset, total) != 0) { log_debug("%s: invalid proposal transforms", __func__); return (-1); } @@ -288,14 +463,47 @@ ikev2_pld_sa(struct iked *env, struct ikev2_payload *pld, } int +ikev2_validate_xform(struct iked_message *msg, size_t offset, size_t total, + struct ikev2_transform *xfrm) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t xfrm_length; + + if (total < sizeof(*xfrm)) { + log_debug("%s: payload malformed: too short for header " + "(%zu < %zu)", __func__, total, sizeof(*xfrm)); + return (-1); + } + memcpy(xfrm, msgbuf + offset, sizeof(*xfrm)); + + xfrm_length = betoh16(xfrm->xfrm_length); + if (xfrm_length < sizeof(*xfrm)) { + log_debug("%s: payload malformed: shorter than minimal header " + "(%zu < %zu)", __func__, xfrm_length, sizeof(*xfrm)); + return (-1); + } + if (total < xfrm_length) { + log_debug("%s: malformed payload: too long for payload size " + "(%zu < %zu)", __func__, total, xfrm_length); + return (-1); + } + + return (0); +} + +int ikev2_pld_xform(struct iked *env, struct ikev2_sa_proposal *sap, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t total) { struct ikev2_transform xfrm; char id[BUFSIZ]; - u_int8_t *msgbuf = ibuf_data(msg->msg_data); + int ret = 0; + size_t xfrm_length; + + if (ikev2_validate_xform(msg, offset, total, &xfrm)) + return (-1); - memcpy(&xfrm, msgbuf + offset, sizeof(xfrm)); + xfrm_length = betoh16(xfrm.xfrm_length); switch (xfrm.xfrm_type) { case IKEV2_XFORMTYPE_ENCR: @@ -323,19 +531,21 @@ ikev2_pld_xform(struct iked *env, struct ikev2_sa_proposal *sap, break; } - log_debug("%s: more %d reserved %d length %d" + log_debug("%s: more %d reserved %d length %zu" " type %s id %s", - __func__, xfrm.xfrm_more, xfrm.xfrm_reserved, - betoh16(xfrm.xfrm_length), + __func__, xfrm.xfrm_more, xfrm.xfrm_reserved, xfrm_length, print_map(xfrm.xfrm_type, ikev2_xformtype_map), id); /* * Parse transform attributes, if available */ msg->msg_attrlength = 0; - if ((u_int)betoh16(xfrm.xfrm_length) > sizeof(xfrm)) - ikev2_pld_attr(env, &xfrm, msg, offset + sizeof(xfrm), - betoh16(xfrm.xfrm_length) - sizeof(xfrm)); + if (xfrm_length > sizeof(xfrm)) { + if (ikev2_pld_attr(env, &xfrm, msg, offset + sizeof(xfrm), + xfrm_length - sizeof(xfrm)) != 0) { + return (-1); + } + } if (ikev2_msg_frompeer(msg)) { if (config_add_transform(msg->msg_parent->msg_prop, @@ -347,26 +557,52 @@ ikev2_pld_xform(struct iked *env, struct ikev2_sa_proposal *sap, } /* Next transform */ - offset += betoh16(xfrm.xfrm_length); + offset += xfrm_length; + total -= xfrm_length; if (xfrm.xfrm_more == IKEV2_XFORM_MORE) - ikev2_pld_xform(env, sap, msg, offset); + ret = ikev2_pld_xform(env, sap, msg, offset, total); + else if (total != 0) { + /* No more transforms but still some data left. */ + log_debug("%s: less data than specified, %zu bytes left", + __func__, total); + ret = -1; + } + + return (ret); +} + +int +ikev2_validate_attr(struct iked_message *msg, size_t offset, size_t total, + struct ikev2_attribute *attr) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + + if (total < sizeof(*attr)) { + log_debug("%s: payload malformed: too short for header " + "(%zu < %zu)", __func__, total, sizeof(*attr)); + return (-1); + } + memcpy(attr, msgbuf + offset, sizeof(*attr)); return (0); } int ikev2_pld_attr(struct iked *env, struct ikev2_transform *xfrm, - struct iked_message *msg, off_t offset, int total) + struct iked_message *msg, size_t offset, size_t total) { struct ikev2_attribute attr; u_int type; u_int8_t *msgbuf = ibuf_data(msg->msg_data); + int ret = 0; + size_t attr_length; - memcpy(&attr, msgbuf + offset, sizeof(attr)); + if (ikev2_validate_attr(msg, offset, total, &attr)) + return (-1); type = betoh16(attr.attr_type) & ~IKEV2_ATTRAF_TV; - log_debug("%s: attribute type %s length %d total %d", + log_debug("%s: attribute type %s length %d total %zu", __func__, print_map(type, ikev2_attrtype_map), betoh16(attr.attr_length), total); @@ -379,30 +615,64 @@ ikev2_pld_attr(struct iked *env, struct ikev2_transform *xfrm, msg->msg_attrlength = betoh16(attr.attr_length); } else { /* Type-Length-Value attribute */ + attr_length = betoh16(attr.attr_length); + if (total < attr_length) { + log_debug("%s: payload malformed: attribute larger " + "than actual payload (%zu < %zu)", __func__, + total, attr_length); + return (-1); + } print_hex(msgbuf, offset + sizeof(attr), - betoh16(attr.attr_length) - sizeof(attr)); - offset += betoh16(attr.attr_length); - total -= betoh16(attr.attr_length); + attr_length - sizeof(attr)); + offset += attr_length; + total -= attr_length; } if (total > 0) { /* Next attribute */ - ikev2_pld_attr(env, xfrm, msg, offset, total); + ret = ikev2_pld_attr(env, xfrm, msg, offset, total); } + return (ret); +} + +int +ikev2_validate_ke(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_keyexchange *kex) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*kex)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*kex)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*kex)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*kex)); + return (-1); + } + memcpy(kex, msgbuf + offset, sizeof(*kex)); + return (0); } int ikev2_pld_ke(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct ikev2_keyexchange kex; u_int8_t *buf; size_t len; u_int8_t *msgbuf = ibuf_data(msg->msg_data); - memcpy(&kex, msgbuf + offset, sizeof(kex)); + if (ikev2_validate_ke(msg, offset, left, pld, &kex)) + return (-1); log_debug("%s: dh group %s reserved %d", __func__, print_map(betoh16(kex.kex_dhgroup), ikev2_xformdh_map), @@ -411,6 +681,17 @@ ikev2_pld_ke(struct iked *env, struct ikev2_payload *pld, buf = msgbuf + offset + sizeof(kex); len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(kex); + if (len == 0) { + log_debug("%s: malformed payload: no KE data given", __func__); + return (-1); + } + /* This will actually be caught by earlier checks. */ + if (left < len) { + log_debug("%s: malformed payload: smaller than specified " + "(%zu < %zu)", __func__, left, len); + return (-1); + } + print_hex(buf, 0, len); if (ikev2_msg_frompeer(msg)) { @@ -425,8 +706,34 @@ ikev2_pld_ke(struct iked *env, struct ikev2_payload *pld, } int +ikev2_validate_id(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_id *id) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*id)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*id)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*id)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*id)); + return (-1); + } + memcpy(id, msgbuf + offset, sizeof(*id)); + + return (0); +} + +int ikev2_pld_id(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset, u_int payload) + struct iked_message *msg, size_t offset, size_t left, u_int payload) { u_int8_t *ptr; struct ikev2_id id; @@ -436,7 +743,9 @@ ikev2_pld_id(struct iked *env, struct ikev2_payload *pld, u_int8_t *msgbuf = ibuf_data(msg->msg_data); char idstr[IKED_ID_SIZE]; - memcpy(&id, msgbuf + offset, sizeof(id)); + if (ikev2_validate_id(msg, offset, left, pld, &id)) + return (-1); + bzero(&idb, sizeof(idb)); /* Don't strip the Id payload header */ @@ -480,8 +789,34 @@ ikev2_pld_id(struct iked *env, struct ikev2_payload *pld, } int +ikev2_validate_cert(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_cert *cert) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*cert)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*cert)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*cert)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*cert)); + return (-1); + } + memcpy(cert, msgbuf + offset, sizeof(*cert)); + + return (0); +} + +int ikev2_pld_cert(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct ikev2_cert cert; u_int8_t *buf; @@ -489,7 +824,8 @@ ikev2_pld_cert(struct iked *env, struct ikev2_payload *pld, struct iked_id *certid; u_int8_t *msgbuf = ibuf_data(msg->msg_data); - memcpy(&cert, msgbuf + offset, sizeof(cert)); + if (ikev2_validate_cert(msg, offset, left, pld, &cert)) + return (-1); offset += sizeof(cert); buf = msgbuf + offset; @@ -520,8 +856,34 @@ ikev2_pld_cert(struct iked *env, struct ikev2_payload *pld, } int +ikev2_validate_certreq(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_cert *cert) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*cert)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*cert)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*cert)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*cert)); + return (-1); + } + memcpy(cert, msgbuf + offset, sizeof(*cert)); + + return (0); +} + +int ikev2_pld_certreq(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct iked_sa *sa = msg->msg_sa; struct ikev2_cert cert; @@ -529,7 +891,8 @@ ikev2_pld_certreq(struct iked *env, struct ikev2_payload *pld, ssize_t len; u_int8_t *msgbuf = ibuf_data(msg->msg_data); - memcpy(&cert, msgbuf + offset, sizeof(cert)); + if (ikev2_validate_certreq(msg, offset, left, pld, &cert)) + return (-1); offset += sizeof(cert); buf = msgbuf + offset; @@ -538,6 +901,7 @@ ikev2_pld_certreq(struct iked *env, struct ikev2_payload *pld, log_debug("%s: type %s length %zd", __func__, print_map(cert.cert_type, ikev2_cert_map), len); + /* This will actually be caught by earlier checks. */ if (len < 0) { log_debug("%s: invalid certificate request length", __func__); return (-1); @@ -571,8 +935,34 @@ ikev2_pld_certreq(struct iked *env, struct ikev2_payload *pld, } int +ikev2_validate_auth(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_auth *auth) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*auth)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*auth)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*auth)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*auth)); + return (-1); + } + memcpy(auth, msgbuf + offset, sizeof(*auth)); + + return (0); +} + +int ikev2_pld_auth(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct ikev2_auth auth; struct iked_id *idp; @@ -581,7 +971,8 @@ ikev2_pld_auth(struct iked *env, struct ikev2_payload *pld, struct iked_sa *sa = msg->msg_sa; u_int8_t *msgbuf = ibuf_data(msg->msg_data); - memcpy(&auth, msgbuf + offset, sizeof(auth)); + if (ikev2_validate_auth(msg, offset, left, pld, &auth)) + return (-1); offset += sizeof(auth); buf = msgbuf + offset; @@ -615,15 +1006,48 @@ ikev2_pld_auth(struct iked *env, struct ikev2_payload *pld, } int +ikev2_validate_nonce(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld) +{ + size_t pld_length; + + /* This will actually be caught by earlier checks. */ + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld)); + return (-1); + } + + return (0); +} + +int ikev2_pld_nonce(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { size_t len; u_int8_t *buf; u_int8_t *msgbuf = ibuf_data(msg->msg_data); + if (ikev2_validate_nonce(msg, offset, left, pld)) + return (-1); + buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld); + + if (len == 0) { + log_debug("%s: malformed payload: no NONCE given", __func__); + return (-1); + } + /* This will actually be caught by earlier checks. */ + if (left < len) { + log_debug("%s: malformed payload: smaller than specified " + "(%zu < %zu)", __func__, left, len); + return (-1); + } + print_hex(buf, 0, len); if (ikev2_msg_frompeer(msg)) { @@ -639,10 +1063,36 @@ ikev2_pld_nonce(struct iked *env, struct ikev2_payload *pld, } int +ikev2_validate_notify(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_notify *n) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*n)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*n)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*n)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*n)); + return (-1); + } + memcpy(n, msgbuf + offset, sizeof(*n)); + + return (0); +} + +int ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { - struct ikev2_notify *n; + struct ikev2_notify n; u_int8_t *buf, md[SHA_DIGEST_LENGTH]; size_t len; u_int32_t spi32; @@ -653,17 +1103,17 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, u_int16_t cpi; u_int8_t transform; - if ((n = ibuf_seek(msg->msg_data, offset, sizeof(*n))) == NULL) + if (ikev2_validate_notify(msg, offset, left, pld, &n)) return (-1); - type = betoh16(n->n_type); + type = betoh16(n.n_type); log_debug("%s: protoid %s spisize %d type %s", __func__, - print_map(n->n_protoid, ikev2_saproto_map), n->n_spisize, + print_map(n.n_protoid, ikev2_saproto_map), n.n_spisize, print_map(type, ikev2_n_map)); - len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(*n); - if ((buf = ibuf_seek(msg->msg_data, offset + sizeof(*n), len)) == NULL) + len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(n); + if ((buf = ibuf_seek(msg->msg_data, offset + sizeof(n), len)) == NULL) return (-1); print_hex(buf, 0, len); @@ -674,9 +1124,14 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, switch (type) { case IKEV2_N_NAT_DETECTION_SOURCE_IP: case IKEV2_N_NAT_DETECTION_DESTINATION_IP: + if (len != sizeof(md)) { + log_debug("%s: malformed payload: hash size mismatch" + " (%zu != %zu)", __func__, len, sizeof(md)); + return (-1); + } if (ikev2_nat_detection(env, msg, md, sizeof(md), type) == -1) return (-1); - if (len != sizeof(md) || memcmp(buf, md, len) != 0) { + if (memcmp(buf, md, len) != 0) { log_debug("%s: %s detected NAT, enabling " "UDP encapsulation", __func__, print_map(type, ikev2_n_map)); @@ -692,7 +1147,8 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, break; case IKEV2_N_INVALID_KE_PAYLOAD: if (len != sizeof(group)) { - log_debug("%s: malformed notification", __func__); + log_debug("%s: malformed payload: group size mismatch" + " (%zu != %zu)", __func__, len, sizeof(group)); return (-1); } if (!msg->msg_sa->sa_hdr.sh_initiator) { @@ -724,7 +1180,7 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, } break; case IKEV2_N_REKEY_SA: - if (len != n->n_spisize) { + if (len != n.n_spisize) { log_debug("%s: malformed notification", __func__); return (-1); } @@ -734,7 +1190,7 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, __func__); return (-1); } - switch (n->n_spisize) { + switch (n.n_spisize) { case 4: memcpy(&spi32, buf, len); rekey->spi = betoh32(spi32); @@ -745,19 +1201,19 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, break; default: log_debug("%s: invalid spi size %d", __func__, - n->n_spisize); + n.n_spisize); return (-1); } - rekey->spi_size = n->n_spisize; - rekey->spi_protoid = n->n_protoid; + rekey->spi_size = n.n_spisize; + rekey->spi_protoid = n.n_protoid; log_debug("%s: rekey %s spi %s", __func__, - print_map(n->n_protoid, ikev2_saproto_map), - print_spi(rekey->spi, n->n_spisize)); + print_map(n.n_protoid, ikev2_saproto_map), + print_spi(rekey->spi, n.n_spisize)); break; case IKEV2_N_IPCOMP_SUPPORTED: if (len < sizeof(cpi) + sizeof(transform)) { - log_debug("%s: malformed ipcomp notification", + log_debug("%s: ignoring malformed ipcomp notification", __func__); return (0); } @@ -778,12 +1234,38 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, } int +ikev2_validate_delete(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_delete *del) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*del)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*del)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*del)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*del)); + return (-1); + } + memcpy(del, msgbuf + offset, sizeof(*del)); + + return (0); +} + +int ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct iked_childsa **peersas = NULL; struct iked_sa *sa = msg->msg_sa; - struct ikev2_delete *del, *localdel; + struct ikev2_delete del, *localdel; struct ibuf *resp = NULL; u_int64_t *localspi = NULL; u_int64_t spi64, spi = 0; @@ -792,25 +1274,22 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, size_t found = 0, failed = 0; int cnt, i, len, sz, ret = -1; - /* Skip if it's a reply and we don't have to deal with it */ - if (ikev2_msg_frompeer(msg) && sa && - (sa->sa_stateflags & IKED_REQ_INF)) { - sa->sa_stateflags &= ~IKED_REQ_INF; - if ((sa->sa_stateflags & IKED_REQ_DELETE) == 0) - return (0); - } + /* Skip if it's a response, then we don't have to deal with it */ + if (ikev2_msg_frompeer(msg) && + msg->msg_parent->msg_response) + return (0); - if ((del = ibuf_seek(msg->msg_data, offset, sizeof(*del))) == NULL) + if (ikev2_validate_delete(msg, offset, left, pld, &del)) return (-1); - cnt = betoh16(del->del_nspi); - sz = del->del_spisize; + cnt = betoh16(del.del_nspi); + sz = del.del_spisize; log_debug("%s: proto %s spisize %d nspi %d", - __func__, print_map(del->del_protoid, ikev2_saproto_map), + __func__, print_map(del.del_protoid, ikev2_saproto_map), sz, cnt); - buf = msgbuf + offset + sizeof(*del); - len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(*del); + buf = msgbuf + offset + sizeof(del); + len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(del); print_hex(buf, 0, len); @@ -819,8 +1298,11 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, case 8: break; default: - if (ikev2_msg_frompeer(msg) && - del->del_protoid == IKEV2_SAPROTO_IKE) { + if (del.del_protoid != IKEV2_SAPROTO_IKE) { + log_debug("%s: invalid SPI size", __func__); + return (-1); + } + if (ikev2_msg_frompeer(msg)) { /* Send an empty informational response */ if ((resp = ibuf_static()) == NULL) goto done; @@ -833,7 +1315,7 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, return (ret); } log_debug("%s: invalid SPI size", __func__); - return (-1); + return (ret); } if ((len / sz) != cnt) { @@ -867,13 +1349,13 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, continue; if ((peersas[i] = childsa_lookup(sa, spi, - del->del_protoid)) == NULL) { + del.del_protoid)) == NULL) { log_warnx("%s: CHILD SA doesn't exist for spi %s", - __func__, print_spi(spi, del->del_spisize)); - goto done; + __func__, print_spi(spi, del.del_spisize)); + continue; } - if (ikev2_childsa_delete(env, sa, del->del_protoid, spi, + if (ikev2_childsa_delete(env, sa, del.del_protoid, spi, &localspi[i], 0) == -1) failed++; else @@ -889,9 +1371,7 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, if (!ikev2_msg_frompeer(msg)) goto done; - if (sa && (sa->sa_stateflags & IKED_REQ_DELETE)) { - /* Finish rekeying */ - sa->sa_stateflags &= ~IKED_REQ_DELETE; + if (msg->msg_parent->msg_response) { ret = 0; goto done; } @@ -905,8 +1385,8 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, if ((localdel = ibuf_advance(resp, sizeof(*localdel))) == NULL) goto done; - localdel->del_protoid = del->del_protoid; - localdel->del_spisize = del->del_spisize; + localdel->del_protoid = del.del_protoid; + localdel->del_spisize = del.del_spisize; localdel->del_nspi = htobe16(found); for (i = 0; i < cnt; i++) { @@ -946,8 +1426,34 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, } int +ikev2_validate_ts(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_tsp *tsp) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*tsp)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*tsp)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*tsp)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*tsp)); + return (-1); + } + memcpy(tsp, msgbuf + offset, sizeof(*tsp)); + + return (0); +} + +int ikev2_pld_ts(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset, u_int payload) + struct iked_message *msg, size_t offset, size_t left, u_int payload) { struct ikev2_tsp tsp; struct ikev2_ts ts; @@ -957,7 +1463,8 @@ ikev2_pld_ts(struct iked *env, struct ikev2_payload *pld, u_int8_t buf[2][128]; u_int8_t *msgbuf = ibuf_data(msg->msg_data); - memcpy(&tsp, msgbuf + offset, sizeof(tsp)); + if (ikev2_validate_ts(msg, offset, left, pld, &tsp)) + return (-1); offset += sizeof(tsp); len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(tsp); @@ -1018,7 +1525,7 @@ ikev2_pld_ts(struct iked *env, struct ikev2_payload *pld, int ikev2_pld_e(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset) { struct iked_sa *sa = msg->msg_sa; struct ibuf *e = NULL; @@ -1056,7 +1563,7 @@ ikev2_pld_e(struct iked *env, struct ikev2_payload *pld, TAILQ_INIT(&emsg.msg_proposals); ret = ikev2_pld_payloads(env, &emsg, 0, ibuf_size(e), - pld->pld_nextpayload, 0); + pld->pld_nextpayload); done: ibuf_release(e); @@ -1065,8 +1572,34 @@ ikev2_pld_e(struct iked *env, struct ikev2_payload *pld, } int +ikev2_validate_cp(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_cp *cp) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*cp)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*cp)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*cp)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*cp)); + return (-1); + } + memcpy(cp, msgbuf + offset, sizeof(*cp)); + + return (0); +} + +int ikev2_pld_cp(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct ikev2_cp cp; struct ikev2_cfg *cfg; @@ -1075,7 +1608,8 @@ ikev2_pld_cp(struct iked *env, struct ikev2_payload *pld, u_int8_t *msgbuf = ibuf_data(msg->msg_data); struct iked_sa *sa = msg->msg_sa; - memcpy(&cp, msgbuf + offset, sizeof(cp)); + if (ikev2_validate_cp(msg, offset, left, pld, &cp)) + return (-1); offset += sizeof(cp); buf = msgbuf + offset; @@ -1106,25 +1640,48 @@ ikev2_pld_cp(struct iked *env, struct ikev2_payload *pld, } int +ikev2_validate_eap(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct eap_header *hdr) +{ + u_int8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*hdr)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*hdr)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*hdr)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*hdr)); + return (-1); + } + memcpy(hdr, msgbuf + offset, sizeof(*hdr)); + + return (0); +} + +int ikev2_pld_eap(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { - struct eap_header *hdr; + struct eap_header hdr; struct eap_message *eap = NULL; struct iked_sa *sa = msg->msg_sa; size_t len; - if ((hdr = ibuf_seek(msg->msg_data, offset, sizeof(*hdr))) == NULL) { - log_debug("%s: failed to get EAP header", __func__); + if (ikev2_validate_eap(msg, offset, left, pld, &hdr)) return (-1); - } - - len = betoh16(hdr->eap_length); + len = betoh16(hdr.eap_length); if (len < sizeof(*eap)) { log_info("%s: %s id %d length %d", __func__, - print_map(hdr->eap_code, eap_code_map), - hdr->eap_id, betoh16(hdr->eap_length)); + print_map(hdr.eap_code, eap_code_map), + hdr.eap_id, betoh16(hdr.eap_length)); } else { /* Now try to get the indicated length */ if ((eap = ibuf_seek(msg->msg_data, offset, len)) == NULL) { @@ -1138,7 +1695,7 @@ ikev2_pld_eap(struct iked *env, struct ikev2_payload *pld, print_map(eap->eap_type, eap_type_map)); } - if (eap_parse(env, sa, hdr, msg->msg_response) == -1) + if (eap_parse(env, sa, &hdr, msg->msg_response) == -1) return (-1); return (0); |