summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2019-05-03 14:44:06 +0100
committerRoy Marples <roy@marples.name>2019-05-04 10:05:40 +0100
commit588aee42c5116573531ef83f5a3c28cd61126f38 (patch)
treedbb22d586d87d57352bfea4b118b878c5f04ea2d
parent9dbef10132b921bfe5379d22f81b349dd38aaa99 (diff)
downloaddhcpcd-588aee42c5116573531ef83f5a3c28cd61126f38.tar.xz
DHCPv6: Fix a potential read overflow with D6_OPTION_PD_EXCLUDE
dhcpcd only checks that the prefix length of the exclusion matches the prefix length of the ia and equals the length of the data in the option. This could potentially overrun the in6_addr structure. This is fixed by enforcing RFC 6603 section 4.2 option limits more clearly. Thanks to Maxime Villard <max@m00nbsd.net> for finding this.
-rw-r--r--dhcp6.c39
1 files changed, 26 insertions, 13 deletions
diff --git a/dhcp6.c b/dhcp6.c
index fd099032..ed431be6 100644
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -1963,21 +1963,9 @@ dhcp6_findpd(struct interface *ifp, const uint8_t *iaid,
ex = dhcp6_findoption(D6_OPTION_PD_EXCLUDE, p, ol);
a->prefix_exclude_len = 0;
memset(&a->prefix_exclude, 0, sizeof(a->prefix_exclude));
-#if 0
- if (ex == NULL) {
- struct dhcp6_option *w;
- uint8_t *wp;
-
- w = calloc(1, 128);
- w->len = htons(2);
- wp = D6_OPTION_DATA(w);
- *wp++ = 64;
- *wp++ = 0x78;
- ex = w;
- }
-#endif
if (ex == NULL)
continue;
+
ol = ntohs(ex->len);
if (ol < 2) {
logger(ifp->ctx, LOG_ERR,
@@ -1985,6 +1973,31 @@ dhcp6_findpd(struct interface *ifp, const uint8_t *iaid,
continue;
}
op = D6_COPTION_DATA(ex);
+
+ /* RFC 6603 4.2 says option length MUST be between 2 and 17.
+ * This allows 1 octet for prefix length and 16 for the
+ * subnet ID. */
+ if (ol < 2 || ol > 17) {
+ logger(ifp->ctx, LOG_ERR,
+ "%s: invalid PD Exclude option", ifp->name);
+ continue;
+ }
+
+ /* RFC 6603 4.2 says prefix length MUST be between the
+ * length of the IAPREFIX prefix length + 1 and 128. */
+ if (*op < a->prefix_len + 1 || *op > 128) {
+ logger(ifp->ctx, LOG_ERR,
+ "%s: invalid PD Exclude length", ifp->name);
+ continue;
+ }
+
+ /* Check option length matches prefix length. */
+ if (((*op - a->prefix_len - 1) / NBBY) + 1 != ol) {
+ logger(ifp->ctx, LOG_ERR,
+ "%s: PD Exclude length mismatch", ifp->name);
+ continue;
+ }
+
a->prefix_exclude_len = *op++;
ol--;
if (((a->prefix_exclude_len - a->prefix_len - 1) / NBBY) + 1