summaryrefslogtreecommitdiff
path: root/sys/netinet/in.c
diff options
context:
space:
mode:
authorAlexander Bluhm <bluhm@cvs.openbsd.org>2019-11-07 13:25:45 +0000
committerAlexander Bluhm <bluhm@cvs.openbsd.org>2019-11-07 13:25:45 +0000
commitb3f189e0ccc9a6a7e5a6a61ac3e486a16e04ece9 (patch)
treed69a297d35ee577238c41703e1cca7c564f5bd61 /sys/netinet/in.c
parentc7ab36b1d9e426d7e5f3ffc3b4ddb0a9ec7a1ea2 (diff)
Do propper kernel input validation for in_control() ioctl(2)
SIOCGIFADDR, SIOCGIFNETMASK, SIOCGIFDSTADDR, SIOCGIFBRDADDR, SIOCSIFADDR, SIOCSIFNETMASK, SIOCSIFDSTADDR, and SIOCSIFBRDADDR. Name in_ioctl_set_ifaddr() consistently. Use in_sa2sin() to validate inet address. Combine if_addrlist loops and add comment. Although netmask is not a inet address, length must be valid. Reported-by: syzbot+5fc6da002fc4e8d994be@syzkaller.appspotmail.com OK visa@
Diffstat (limited to 'sys/netinet/in.c')
-rw-r--r--sys/netinet/in.c103
1 files changed, 63 insertions, 40 deletions
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
index 9263a59df8a..a9a56e2b325 100644
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in.c,v 1.164 2019/10/23 19:58:32 bluhm Exp $ */
+/* $OpenBSD: in.c,v 1.165 2019/11/07 13:25:44 bluhm Exp $ */
/* $NetBSD: in.c,v 1.26 1996/02/13 23:41:39 christos Exp $ */
/*
@@ -84,7 +84,7 @@
void in_socktrim(struct sockaddr_in *);
-int in_ioctl_sifaddr(u_long, caddr_t, struct ifnet *, int);
+int in_ioctl_set_ifaddr(u_long, caddr_t, struct ifnet *, int);
int in_ioctl_change_ifaddr(u_long, caddr_t, struct ifnet *, int);
int in_ioctl_get(u_long, caddr_t, struct ifnet *);
void in_purgeaddr(struct ifaddr *);
@@ -227,7 +227,7 @@ in_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, int privileged)
struct ifreq *ifr = (struct ifreq *)data;
struct ifaddr *ifa;
struct in_ifaddr *ia = NULL;
- struct sockaddr_in oldaddr;
+ struct sockaddr_in *sin = NULL, oldaddr;
int error = 0;
if (ifp == NULL)
@@ -240,7 +240,7 @@ in_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, int privileged)
case SIOCGIFBRDADDR:
return in_ioctl_get(cmd, data, ifp);
case SIOCSIFADDR:
- return in_ioctl_sifaddr(cmd, data, ifp, privileged);
+ return in_ioctl_set_ifaddr(cmd, data, ifp, privileged);
case SIOCAIFADDR:
case SIOCDIFADDR:
return in_ioctl_change_ifaddr(cmd, data, ifp, privileged);
@@ -252,28 +252,31 @@ in_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, int privileged)
return (EOPNOTSUPP);
}
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ error = in_sa2sin(&ifr->ifr_addr, &sin);
+ if (error)
+ return (error);
+ }
+
NET_LOCK();
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
- if (ifa->ifa_addr->sa_family == AF_INET) {
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ /* find first address or exact match */
+ if (ia == NULL)
+ ia = ifatoia(ifa);
+ if (sin == NULL || sin->sin_addr.s_addr == INADDR_ANY)
+ break;
+ if (ifatoia(ifa)->ia_addr.sin_addr.s_addr ==
+ sin->sin_addr.s_addr) {
ia = ifatoia(ifa);
break;
- }
- }
-
- if (ia && satosin(&ifr->ifr_addr)->sin_addr.s_addr) {
- for (; ifa != NULL; ifa = TAILQ_NEXT(ifa, ifa_list)) {
- if ((ifa->ifa_addr->sa_family == AF_INET) &&
- ifatoia(ifa)->ia_addr.sin_addr.s_addr ==
- satosin(&ifr->ifr_addr)->sin_addr.s_addr) {
- ia = ifatoia(ifa);
- break;
- }
}
}
if (ia == NULL) {
- NET_UNLOCK();
- return (EADDRNOTAVAIL);
+ error = EADDRNOTAVAIL;
+ goto err;
}
switch (cmd) {
@@ -287,8 +290,11 @@ in_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, int privileged)
error = EINVAL;
break;
}
+ error = in_sa2sin(&ifr->ifr_dstaddr, &sin);
+ if (error)
+ break;
oldaddr = ia->ia_dstaddr;
- ia->ia_dstaddr = *satosin(&ifr->ifr_dstaddr);
+ ia->ia_dstaddr = *sin;
error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, (caddr_t)ia);
if (error) {
ia->ia_dstaddr = oldaddr;
@@ -308,7 +314,10 @@ in_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, int privileged)
error = EINVAL;
break;
}
- ifa_update_broadaddr(ifp, &ia->ia_ifa, &ifr->ifr_broadaddr);
+ error = in_sa2sin(&ifr->ifr_broadaddr, &sin);
+ if (error)
+ break;
+ ifa_update_broadaddr(ifp, &ia->ia_ifa, sintosa(sin));
break;
case SIOCSIFNETMASK:
@@ -317,21 +326,27 @@ in_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, int privileged)
break;
}
+ if (ifr->ifr_addr.sa_len < 8) {
+ error = EINVAL;
+ break;
+ }
ia->ia_netmask = ia->ia_sockmask.sin_addr.s_addr =
satosin(&ifr->ifr_addr)->sin_addr.s_addr;
break;
}
-
+err:
NET_UNLOCK();
return (error);
}
int
-in_ioctl_sifaddr(u_long cmd, caddr_t data, struct ifnet *ifp, int privileged)
+in_ioctl_set_ifaddr(u_long cmd, caddr_t data, struct ifnet *ifp,
+ int privileged)
{
struct ifreq *ifr = (struct ifreq *)data;
struct ifaddr *ifa;
struct in_ifaddr *ia = NULL;
+ struct sockaddr_in *sin;
int error = 0;
int newifaddr;
@@ -341,15 +356,19 @@ in_ioctl_sifaddr(u_long cmd, caddr_t data, struct ifnet *ifp, int privileged)
if (!privileged)
return (EPERM);
+ error = in_sa2sin(&ifr->ifr_addr, &sin);
+ if (error)
+ return (error);
+
NET_LOCK();
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
- if (ifa->ifa_addr->sa_family == AF_INET) {
- ia = ifatoia(ifa);
- break;
- }
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ /* find first address */
+ ia = ifatoia(ifa);
+ break;
}
-
if (ia == NULL) {
ia = malloc(sizeof *ia, M_IFADDR, M_WAITOK | M_ZERO);
ia->ia_addr.sin_family = AF_INET;
@@ -369,7 +388,7 @@ in_ioctl_sifaddr(u_long cmd, caddr_t data, struct ifnet *ifp, int privileged)
newifaddr = 0;
in_ifscrub(ifp, ia);
- error = in_ifinit(ifp, ia, satosin(&ifr->ifr_addr), newifaddr);
+ error = in_ifinit(ifp, ia, sin, newifaddr);
if (!error)
dohooks(ifp->if_addrhooks, 0);
@@ -521,25 +540,29 @@ in_ioctl_get(u_long cmd, caddr_t data, struct ifnet *ifp)
struct ifreq *ifr = (struct ifreq *)data;
struct ifaddr *ifa;
struct in_ifaddr *ia = NULL;
+ struct sockaddr_in *sin = NULL;
int error = 0;
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ error = in_sa2sin(&ifr->ifr_addr, &sin);
+ if (error)
+ return (error);
+ }
+
NET_RLOCK();
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
- if (ifa->ifa_addr->sa_family == AF_INET) {
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ /* find first address or exact match */
+ if (ia == NULL)
+ ia = ifatoia(ifa);
+ if (sin == NULL || sin->sin_addr.s_addr == INADDR_ANY)
+ break;
+ if (ifatoia(ifa)->ia_addr.sin_addr.s_addr ==
+ sin->sin_addr.s_addr) {
ia = ifatoia(ifa);
break;
- }
- }
-
- if (ia && satosin(&ifr->ifr_addr)->sin_addr.s_addr) {
- for (; ifa != NULL; ifa = TAILQ_NEXT(ifa, ifa_list)) {
- if ((ifa->ifa_addr->sa_family == AF_INET) &&
- ifatoia(ifa)->ia_addr.sin_addr.s_addr ==
- satosin(&ifr->ifr_addr)->sin_addr.s_addr) {
- ia = ifatoia(ifa);
- break;
- }
}
}
if (ia == NULL) {