changeset 4502:5d361d74621c draft

BSD: Validate RTM message lengths received Doubtful these lengths would be invalid, but you never know.
author Roy Marples <roy@marples.name>
date Fri, 03 May 2019 15:44:51 +0100
parents be15f5b1966c
children 9784c3171a22
files src/if-bsd.c
diffstat 1 files changed, 74 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/src/if-bsd.c	Fri May 03 14:50:28 2019 +0100
+++ b/src/if-bsd.c	Fri May 03 15:44:51 2019 +0100
@@ -360,20 +360,28 @@
 #endif
 }
 
-static void
-get_addrs(int type, const void *data, const struct sockaddr **sa)
+static int
+get_addrs(int type, const void *data, size_t data_len,
+    const struct sockaddr **sa)
 {
-	const char *cp;
+	const char *cp, *ep;
 	int i;
 
 	cp = data;
+	ep = cp + data_len;
 	for (i = 0; i < RTAX_MAX; i++) {
 		if (type & (1 << i)) {
+			if (cp >= ep) {
+				errno = EINVAL;
+				return -1;
+			}
 			sa[i] = (const struct sockaddr *)cp;
 			RT_ADVANCE(cp, sa[i]);
 		} else
 			sa[i] = NULL;
 	}
+
+	return 0;
 }
 
 static struct interface *
@@ -647,7 +655,9 @@
 	}
 #endif
 
-	get_addrs(rtm->rtm_addrs, rtm + 1, rti_info);
+	if (get_addrs(rtm->rtm_addrs, rtm + 1,
+		      rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1)
+		return -1;
 	memset(rt, 0, sizeof(*rt));
 
 	rt->rt_flags = (unsigned int)rtm->rtm_flags;
@@ -713,13 +723,17 @@
 	end = buf + needed;
 	for (p = buf; p < end; p += rtm->rtm_msglen) {
 		rtm = (void *)p;
+		if (p + rtm->rtm_msglen >= end) {
+			errno = EINVAL;
+			break;
+		}
 		if (if_copyrt(ctx, &rt, rtm) == 0) {
 			rt.rt_dflags |= RTDF_INIT;
 			rt_recvrt(RTM_ADD, &rt, rtm->rtm_pid);
 		}
 	}
 	free(buf);
-	return 0;
+	return p == end ? 0 : -1;
 }
 
 #ifdef INET
@@ -981,28 +995,38 @@
 }
 #endif
 
-static void
+static int
 if_announce(struct dhcpcd_ctx *ctx, const struct if_announcemsghdr *ifan)
 {
 
+	if (ifan->ifan_msglen < sizeof(*ifan)) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	switch(ifan->ifan_what) {
 	case IFAN_ARRIVAL:
-		dhcpcd_handleinterface(ctx, 1, ifan->ifan_name);
-		break;
+		return dhcpcd_handleinterface(ctx, 1, ifan->ifan_name);
 	case IFAN_DEPARTURE:
-		dhcpcd_handleinterface(ctx, -1, ifan->ifan_name);
-		break;
+		return dhcpcd_handleinterface(ctx, -1, ifan->ifan_name);
 	}
+
+	return 0;
 }
 
-static void
+static int
 if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm)
 {
 	struct interface *ifp;
 	int link_state;
 
+	if (ifm->ifm_msglen < sizeof(*ifm)) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	if ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL)
-		return;
+		return 0;
 
 	switch (ifm->ifm_data.ifi_link_state) {
 	case LINK_STATE_UNKNOWN:
@@ -1018,19 +1042,25 @@
 
 	dhcpcd_handlecarrier(ctx, link_state,
 	    (unsigned int)ifm->ifm_flags, ifp->name);
+	return 0;
 }
 
-static void
+static int
 if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
 {
 	struct rt rt;
 
+	if (rtm->rtm_msglen < sizeof(*rtm)) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	/* Ignore errors. */
 	if (rtm->rtm_errno != 0)
-		return;
+		return 0;
 
 	if (if_copyrt(ctx, &rt, rtm) == -1)
-		return;
+		return -1;
 
 #ifdef INET6
 	/*
@@ -1054,9 +1084,10 @@
 #endif
 
 	rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);
+	return 0;
 }
 
-static void
+static int
 if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
 {
 	struct interface *ifp;
@@ -1064,11 +1095,18 @@
 	int addrflags;
 	pid_t pid;
 
+	if (ifam->ifam_msglen < sizeof(*ifam)) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	if ((ifp = if_findindex(ctx->ifaces, ifam->ifam_index)) == NULL)
-		return;
-	get_addrs(ifam->ifam_addrs, ifam + 1, rti_info);
+		return 0;
+	if (get_addrs(ifam->ifam_addrs, ifam + 1,
+		      ifam->ifam_msglen - sizeof(*ifam), rti_info) == -1)
+		return -1;
 	if (rti_info[RTAX_IFA] == NULL)
-		return;
+		return 0;
 
 #ifdef HAVE_IFAM_PID
 	pid = ifam->ifam_pid;
@@ -1168,7 +1206,7 @@
 			}
 			freeifaddrs(ifaddrs);
 			if (ifa != NULL)
-				return;
+				return 0;
 #endif
 		}
 
@@ -1231,40 +1269,37 @@
 	}
 #endif
 	}
+
+	return 0;
 }
 
-static void
+static int
 if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
 {
 
 	if (rtm->rtm_version != RTM_VERSION)
-		return;
+		return 0;
 
 	switch(rtm->rtm_type) {
 #ifdef RTM_IFANNOUNCE
 	case RTM_IFANNOUNCE:
-		if_announce(ctx, (const void *)rtm);
-		break;
+		return if_announce(ctx, (const void *)rtm);
 #endif
 	case RTM_IFINFO:
-		if_ifinfo(ctx, (const void *)rtm);
-		break;
+		return if_ifinfo(ctx, (const void *)rtm);
 	case RTM_ADD:		/* FALLTHROUGH */
 	case RTM_CHANGE:	/* FALLTHROUGH */
 	case RTM_DELETE:
-		if_rtm(ctx, (const void *)rtm);
-		break;
+		return if_rtm(ctx, (const void *)rtm);
 #ifdef RTM_CHGADDR
 	case RTM_CHGADDR:	/* FALLTHROUGH */
 #endif
 	case RTM_DELADDR:	/* FALLTHROUGH */
 	case RTM_NEWADDR:
-		if_ifa(ctx, (const void *)rtm);
-		break;
+		return if_ifa(ctx, (const void *)rtm);
 #ifdef RTM_DESYNC
 	case RTM_DESYNC:
-		dhcpcd_linkoverflow(ctx);
-		break;
+		return dhcpcd_linkoverflow(ctx);
 #endif
 	}
 }
@@ -1280,9 +1315,13 @@
 	len = recvmsg(ctx->link_fd, &msg, 0);
 	if (len == -1)
 		return -1;
-	if (len != 0)
-		if_dispatch(ctx, &rtm.hdr);
-	return 0;
+	if (len == 0)
+		return 0;
+	if (len < rtm.hdr.rtm_msglen) {
+		errno = EINVAL;
+		return -1;
+	}
+	return if_dispatch(ctx, &rtm.hdr);
 }
 
 #ifndef SYS_NMLN	/* OSX */