Here's a fixed version of my previous diff that does not accidentally
add a walk over the ifa_list to SIOCSIFADDR.

Instead, start with a merged version of SIOCSIFADDR, add a copy of the
walk to both SIOCAIFADDR and SIOCDIFADDR and add the allocation of the
ia to SIACAIFADDR. As before, start each case with a privilege check.

Index: sys/netinet/in.c
===================================================================
RCS file: /var/cvs/src/sys/netinet/in.c,v
retrieving revision 1.157
diff -u -p -r1.157 in.c
--- sys/netinet/in.c    31 May 2018 17:17:01 -0000      1.157
+++ sys/netinet/in.c    31 May 2018 17:20:17 -0000
@@ -334,26 +334,10 @@ in_ioctl_change_ifaddr(u_long cmd, caddr
        }
 
        switch (cmd) {
-       case SIOCAIFADDR:
-       case SIOCDIFADDR:
-               if (ifra->ifra_addr.sin_family == AF_INET) {
-                       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 ==
-                                   ifra->ifra_addr.sin_addr.s_addr)
-                                       break;
-                       }
-                       ia = ifatoia(ifa);
-               }
-               if (cmd == SIOCDIFADDR && ia == NULL) {
-                       error = EADDRNOTAVAIL;
-                       goto err;
-               }
-               /* FALLTHROUGH */
        case SIOCSIFADDR:
                if (!privileged) {
                        error = EPERM;
-                       goto err;
+                       break;
                }
 
                if (ia == NULL) {
@@ -373,10 +357,7 @@ in_ioctl_change_ifaddr(u_long cmd, caddr
                        newifaddr = 1;
                } else
                        newifaddr = 0;
-               break;
-       }
-       switch(cmd) {
-       case SIOCSIFADDR:
+
                in_ifscrub(ifp, ia);
                error = in_ifinit(ifp, ia, satosin(&ifr->ifr_addr), newifaddr);
                if (error)
@@ -387,6 +368,38 @@ in_ioctl_change_ifaddr(u_long cmd, caddr
        case SIOCAIFADDR: {
                int needinit = 0;
 
+               if (!privileged) {
+                       error = EPERM;
+                       break;
+               }
+
+               if (ifra->ifra_addr.sin_family == AF_INET) {
+                       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 ==
+                                   ifra->ifra_addr.sin_addr.s_addr)
+                                       break;
+                       }
+                       ia = ifatoia(ifa);
+               }
+               if (ia == NULL) {
+                       ia = malloc(sizeof *ia, M_IFADDR, M_WAITOK | M_ZERO);
+                       ia->ia_addr.sin_family = AF_INET;
+                       ia->ia_addr.sin_len = sizeof(ia->ia_addr);
+                       ia->ia_ifa.ifa_addr = sintosa(&ia->ia_addr);
+                       ia->ia_ifa.ifa_dstaddr = sintosa(&ia->ia_dstaddr);
+                       ia->ia_ifa.ifa_netmask = sintosa(&ia->ia_sockmask);
+                       ia->ia_sockmask.sin_len = 8;
+                       if (ifp->if_flags & IFF_BROADCAST) {
+                               ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr);
+                               ia->ia_broadaddr.sin_family = AF_INET;
+                       }
+                       ia->ia_ifp = ifp;
+
+                       newifaddr = 1;
+               } else
+                       newifaddr = 0;
+
                if (ia->ia_addr.sin_family == AF_INET) {
                        if (ifra->ifra_addr.sin_len == 0)
                                ifra->ifra_addr = ia->ia_addr;
@@ -423,6 +436,25 @@ in_ioctl_change_ifaddr(u_long cmd, caddr
                break;
                }
        case SIOCDIFADDR:
+               if (!privileged) {
+                       error = EPERM;
+                       break;
+               }
+
+               if (ifra->ifra_addr.sin_family == AF_INET) {
+                       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 ==
+                                   ifra->ifra_addr.sin_addr.s_addr)
+                                       break;
+                       }
+                       ia = ifatoia(ifa);
+               }
+               if (ia == NULL) {
+                       error = EADDRNOTAVAIL;
+                       break;
+               }
+
                /*
                 * Even if the individual steps were safe, shouldn't
                 * these kinds of changes happen atomically?  What
@@ -437,7 +469,6 @@ in_ioctl_change_ifaddr(u_long cmd, caddr
                panic("invalid ioctl %lu", cmd);
        }
 
-err:
        NET_UNLOCK();
        return (error);
 }

Reply via email to