On 03/31/16 14:07, Alexander Bluhm wrote:
> On Wed, Mar 30, 2016 at 10:44:14PM +0200, Vincent Gross wrote:
>> This diff moves the "are we binding to a privileged port while not being 
>> root ?"
>> check from in(6)_pcbaddrisavail() to in_pcbbind().
> 
>> --- sys/netinet/in_pcb.c     26 Mar 2016 21:56:04 -0000      1.198
>> +++ sys/netinet/in_pcb.c     30 Mar 2016 20:33:00 -0000
>> @@ -341,9 +341,14 @@ in_pcbbind(struct inpcb *inp, struct mbu
>>              }
>>      }
>>  
>> -    if (lport == 0)
>> +    if (lport == 0) {
>>              if ((error = in_pcbpickport(&lport, wild, inp, p)))
>>                      return (error);
>> +    } else {
>> +            if (ntohs(lport) < IPPORT_RESERVED &&
>> +                (error = suser(p, 0)))
>> +                    return (EACCES);
>> +    }
>>      inp->inp_lport = lport;
> 
> At this point inp has already been modified.  So when we bail out
> with EACCES here, we have a partially successful system call.
> 
> Move the assignments
>                         inp->inp_laddr6 = sin6->sin6_addr;
>                         inp->inp_laddr = sin->sin_addr;
> down after the return (EACCES).
> 
> Looks like that return (error) was wrong before.

in_pcbpickport() need the local address, so I extend the prototype and
keep a void * to the sin(6)_addr or zeroin46_addr. And while at it, I set
the INPLOOKUP_IPV6 flag which will be needed in in_pcbpickport().

Ok ?

Index: netinet/in_pcb.c
===================================================================
RCS file: /cvs/src/sys/netinet/in_pcb.c,v
retrieving revision 1.198
diff -u -p -r1.198 in_pcb.c
--- netinet/in_pcb.c    26 Mar 2016 21:56:04 -0000      1.198
+++ netinet/in_pcb.c    3 Apr 2016 19:16:37 -0000
@@ -286,6 +286,7 @@ in_pcbbind(struct inpcb *inp, struct mbu
        struct socket *so = inp->inp_socket;
        u_int16_t lport = 0;
        int wild = 0;
+       void *laddr = &zeroin46_addr;
        int error;
 
        if (inp->inp_lport)
@@ -312,9 +313,10 @@ in_pcbbind(struct inpcb *inp, struct mbu
                        if (sin6->sin6_family != AF_INET6)
                                return (EAFNOSUPPORT);
 
+                       wild |= INPLOOKUP_IPV6;
                        if ((error = in6_pcbaddrisavail(inp, sin6, wild, p)))
                                return (error);
-                       inp->inp_laddr6 = sin6->sin6_addr;
+                       laddr = &sin6->sin6_addr;
                        lport = sin6->sin6_port;
                        break;
                }
@@ -332,7 +334,7 @@ in_pcbbind(struct inpcb *inp, struct mbu
 
                        if ((error = in_pcbaddrisavail(inp, sin, wild, p)))
                                return (error);
-                       inp->inp_laddr = sin->sin_addr;
+                       laddr = &sin->sin_addr;
                        lport = sin->sin_port;
                        break;
                }
@@ -342,8 +344,20 @@ in_pcbbind(struct inpcb *inp, struct mbu
        }
 
        if (lport == 0)
-               if ((error = in_pcbpickport(&lport, wild, inp, p)))
+               if ((error = in_pcbpickport(&lport, laddr, wild, inp, p)))
                        return (error);
+       if (nam) {
+               switch (sotopf(so)) {
+#ifdef INET6
+               case PF_INET6:
+                       inp->inp_laddr6 = *(struct in6_addr *)laddr;
+                       break;
+#endif
+               case PF_INET:
+                       inp->inp_laddr = *(struct in_addr *)laddr;
+                       break;
+               }
+       }
        inp->inp_lport = lport;
        in_pcbrehash(inp);
        return (0);
@@ -418,12 +432,12 @@ in_pcbaddrisavail(struct inpcb *inp, str
 }
 
 int
-in_pcbpickport(u_int16_t *lport, int wild, struct inpcb *inp, struct proc *p)
+in_pcbpickport(u_int16_t *lport, void *laddr, int wild, struct inpcb *inp,
+    struct proc *p)
 {
        struct socket *so = inp->inp_socket;
        struct inpcbtable *table = inp->inp_table;
        u_int16_t first, last, lower, higher, candidate, localport;
-       void *laddr;
        int count;
 
        if (inp->inp_flags & INP_HIGHPORT) {
@@ -453,10 +467,6 @@ in_pcbpickport(u_int16_t *lport, int wil
 
        count = higher - lower;
        candidate = lower + arc4random_uniform(count);
-       if (sotopf(so) == PF_INET6)
-               laddr = &inp->inp_laddr6;
-       else
-               laddr = &inp->inp_laddr;
 
        do {
                if (count-- < 0)        /* completely used? */
Index: netinet/in_pcb.h
===================================================================
RCS file: /cvs/src/sys/netinet/in_pcb.h,v
retrieving revision 1.96
diff -u -p -r1.96 in_pcb.h
--- netinet/in_pcb.h    23 Mar 2016 15:50:36 -0000      1.96
+++ netinet/in_pcb.h    3 Apr 2016 19:16:37 -0000
@@ -290,6 +290,6 @@ int in6_pcbnotify(struct inpcbtable *, s
        u_int, const struct sockaddr_in6 *, u_int, u_int, int, void *,
        void (*)(struct inpcb *, int));
 int    in6_selecthlim(struct inpcb *);
-int    in_pcbpickport(u_int16_t *, int, struct inpcb *, struct proc *);
+int    in_pcbpickport(u_int16_t *, void *, int, struct inpcb *, struct proc *);
 #endif /* _KERNEL */
 #endif /* _NETINET_IN_PCB_H_ */

Reply via email to