Subject: Re: Assigning low-numbered ports during connect()
To: None <tech-net@netbsd.org>
From: Christos Zoulas <christos@astron.com>
List: tech-net
Date: 11/15/2005 01:16:35
In article <20051114222417.GO12021@snowdrop.l8s.co.uk>,
David Laight <david@l8s.co.uk> wrote:
>I was writing some code to send UDP RPC requests and discovered that
>the code:
> sock = socket(d->ai->ai_family, SOCK_DGRAM, IPPROTO_UDP);
> range = IP_PORTRANGE_LOW;
> setsockopt(sock, IPPROTO_IP, IP_PORTRANGE, &range, sizeof(range));
> connect(sock, d->ai->ai_addr, d->ai->ai_addrlen);
>will transmit a UDP request with a source port of zero.
>(Whether executed by root or any other user.)
>
>Without the setsockopt() a high numbered port is used (as expected).
>
>Digging a little I discovered that in_pcbconnect() fails to pass the
>'proc' pointer to in_pcbbind() so that it cannot allocate a low-numbered
>port, and then fails to report the EACCESS error back to the user.
>It then blindly transmits the UDP datagram from an unbound port.
>
>Digging even further, it seems that when the IP_PORTRANGE_LOW stuff was
>added in rev 1.45 (Jan 1998 - from FreeBSD) no one noticed that the
>in_pcbbind() call in in_pcbconnect() could now fail (prior to that
>in_pcbbind() would only allocate ports above 1024). Later someone
>made it return EAGAIN if all the local port were used - but still didn't
>worry about the EACCESS return.
>
>Before this ports below 1024 had to be explicitely bound - requiring
>the applications to loop through the port numbers.
>
>To behave sensibly the code needs to either:
>1) Always allocate a high port
>or
>2) Allocate a low port for root and a high port for non-root
>or
>3) Allocate a low port for root and fail EACCESS for non-root
>or
>4) Always fail EACCESS
>
>With the code above issing a bind() request is quite hard:
> static char ad[64];
> ((struct sockaddr *)ad)->sa_family =
> ((struct sockaddr *)d->ai->ai_addr)->sa_family;
> bind(sock, (void *)ad, d->ai->ai_addrlen);
>seems to be enough, but you are close to needing to know the format
>of the address buffer for the protocol.
>
>The patch below implements (3) above.
>
> David
>
>Index: netinet/in_pcb.c
>===================================================================
>RCS file: /cvsroot/src/sys/netinet/in_pcb.c,v
>retrieving revision 1.100
>diff -u -p -r1.100 in_pcb.c
>--- netinet/in_pcb.c 29 May 2005 21:41:23 -0000 1.100
>+++ netinet/in_pcb.c 14 Nov 2005 21:59:38 -0000
>@@ -357,7 +357,7 @@ noname:
> * then pick one.
> */
> int
>-in_pcbconnect(void *v, struct mbuf *nam)
>+in_pcbconnect(void *v, struct mbuf *nam, struct proc *p)
> {
> struct inpcb *inp = v;
> struct in_ifaddr *ia = NULL;
>@@ -427,15 +427,14 @@ in_pcbconnect(void *v, struct mbuf *nam)
> return (EADDRINUSE);
> if (in_nullhost(inp->inp_laddr)) {
> if (inp->inp_lport == 0) {
>- error = in_pcbbind(inp, (struct mbuf *)0,
>- (struct proc *)0);
>+ error = in_pcbbind(inp, NULL, p);
> /*
> * This used to ignore the return value
> * completely, but we need to check for
> * ephemeral port shortage.
>- * XXX Should we check for other errors, too?
>+ * And attempts to request low ports if not root.
> */
>- if (error == EAGAIN)
>+ if (error != 0)
> return (error);
> }
> inp->inp_laddr = ifaddr->sin_addr;
>Index: netinet/in_pcb.h
>===================================================================
>RCS file: /cvsroot/src/sys/netinet/in_pcb.h,v
>retrieving revision 1.39
>diff -u -p -r1.39 in_pcb.h
>--- netinet/in_pcb.h 12 Feb 2005 12:31:07 -0000 1.39
>+++ netinet/in_pcb.h 14 Nov 2005 21:59:38 -0000
>@@ -118,7 +118,7 @@ struct inpcb {
> void in_losing(struct inpcb *);
> int in_pcballoc(struct socket *, void *);
> int in_pcbbind(void *, struct mbuf *, struct proc *);
>-int in_pcbconnect(void *, struct mbuf *);
>+int in_pcbconnect(void *, struct mbuf *, struct proc *);
> void in_pcbdetach(void *);
> void in_pcbdisconnect(void *);
> void in_pcbinit(struct inpcbtable *, int, int);
>Index: netinet/tcp_input.c
>===================================================================
>RCS file: /cvsroot/src/sys/netinet/tcp_input.c,v
>retrieving revision 1.236
>diff -u -p -r1.236 tcp_input.c
>--- netinet/tcp_input.c 12 Aug 2005 14:41:00 -0000 1.236
>+++ netinet/tcp_input.c 14 Nov 2005 21:59:39 -0000
>@@ -3735,7 +3735,7 @@ syn_cache_get(struct sockaddr *src, stru
> am->m_len = src->sa_len;
> bcopy(src, mtod(am, caddr_t), src->sa_len);
> if (inp) {
>- if (in_pcbconnect(inp, am)) {
>+ if (in_pcbconnect(inp, am, NULL)) {
> (void) m_free(am);
> goto resetandabort;
> }
>@@ -3756,7 +3756,7 @@ syn_cache_get(struct sockaddr *src, stru
> &sin6->sin6_addr.s6_addr32[3],
> sizeof(sin6->sin6_addr.s6_addr32[3]));
> }
>- if (in6_pcbconnect(in6p, am)) {
>+ if (in6_pcbconnect(in6p, am, NULL)) {
> (void) m_free(am);
> goto resetandabort;
> }
>Index: netinet/tcp_usrreq.c
>===================================================================
>RCS file: /cvsroot/src/sys/netinet/tcp_usrreq.c,v
>retrieving revision 1.111
>diff -u -p -r1.111 tcp_usrreq.c
>--- netinet/tcp_usrreq.c 7 Sep 2005 17:58:13 -0000 1.111
>+++ netinet/tcp_usrreq.c 14 Nov 2005 21:59:39 -0000
>@@ -385,7 +385,7 @@ tcp_usrreq(struct socket *so, int req,
> if (error)
> break;
> }
>- error = in_pcbconnect(inp, nam);
>+ error = in_pcbconnect(inp, nam, p);
> }
> #endif
> #ifdef INET6
>@@ -396,7 +396,7 @@ tcp_usrreq(struct socket *so, int req,
> if (error)
> break;
> }
>- error = in6_pcbconnect(in6p, nam);
>+ error = in6_pcbconnect(in6p, nam, p);
> if (!error) {
> /* mapped addr case */
> if (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_faddr))
>Index: netinet/udp_usrreq.c
>===================================================================
>RCS file: /cvsroot/src/sys/netinet/udp_usrreq.c,v
>retrieving revision 1.142
>diff -u -p -r1.142 udp_usrreq.c
>--- netinet/udp_usrreq.c 3 Sep 2005 18:01:07 -0000 1.142
>+++ netinet/udp_usrreq.c 14 Nov 2005 21:59:39 -0000
>@@ -1226,7 +1226,7 @@ udp_usrreq(struct socket *so, int req, s
> break;
>
> case PRU_CONNECT:
>- error = in_pcbconnect(inp, nam);
>+ error = in_pcbconnect(inp, nam, p);
> if (error)
> break;
> soisconnected(so);
>@@ -1268,7 +1268,7 @@ udp_usrreq(struct socket *so, int req, s
> error = EISCONN;
> goto die;
> }
>- error = in_pcbconnect(inp, nam);
>+ error = in_pcbconnect(inp, nam, p);
> if (error)
> goto die;
> } else {
>Index: netinet6/in6_pcb.c
>===================================================================
>RCS file: /cvsroot/src/sys/netinet6/in6_pcb.c,v
>retrieving revision 1.67
>diff -u -p -r1.67 in6_pcb.c
>--- netinet6/in6_pcb.c 29 May 2005 21:43:51 -0000 1.67
>+++ netinet6/in6_pcb.c 14 Nov 2005 21:59:40 -0000
>@@ -344,9 +344,10 @@ in6_pcbbind(v, nam, p)
> * then pick one.
> */
> int
>-in6_pcbconnect(v, nam)
>+in6_pcbconnect(v, nam, p)
> void *v;
> struct mbuf *nam;
>+ struct proc *p;
> {
> struct in6pcb *in6p = v;
> struct in6_addr *in6a = NULL;
>@@ -449,8 +450,9 @@ in6_pcbconnect(v, nam)
> in6p->in6p_laddr.s6_addr32[3] == 0))
> {
> if (in6p->in6p_lport == 0) {
>- (void)in6_pcbbind(in6p, (struct mbuf *)0,
>- (struct proc *)0);
>+ error = in6_pcbbind(in6p, (struct mbuf *)0, p);
>+ if (error != 0)
>+ return error;
> }
> in6p->in6p_laddr = *in6a;
> }
>Index: netinet6/in6_pcb.h
>===================================================================
>RCS file: /cvsroot/src/sys/netinet6/in6_pcb.h,v
>retrieving revision 1.26
>diff -u -p -r1.26 in6_pcb.h
>--- netinet6/in6_pcb.h 29 May 2005 21:43:51 -0000 1.26
>+++ netinet6/in6_pcb.h 14 Nov 2005 21:59:40 -0000
>@@ -152,7 +152,7 @@ void in6_losing __P((struct in6pcb *));
> void in6_pcbinit __P((struct inpcbtable *, int, int));
> int in6_pcballoc __P((struct socket *, void *));
> int in6_pcbbind __P((void *, struct mbuf *, struct proc *));
>-int in6_pcbconnect __P((void *, struct mbuf *));
>+int in6_pcbconnect __P((void *, struct mbuf *, struct proc *));
> void in6_pcbdetach __P((struct in6pcb *));
> void in6_pcbdisconnect __P((struct in6pcb *));
> struct in6pcb *in6_pcblookup_port __P((struct inpcbtable *, struct in6_addr *,
>Index: netinet6/udp6_usrreq.c
>===================================================================
>RCS file: /cvsroot/src/sys/netinet6/udp6_usrreq.c,v
>retrieving revision 1.70
>diff -u -p -r1.70 udp6_usrreq.c
>--- netinet6/udp6_usrreq.c 28 Aug 2005 21:01:02 -0000 1.70
>+++ netinet6/udp6_usrreq.c 14 Nov 2005 21:59:40 -0000
>@@ -336,7 +336,7 @@ udp6_usrreq(so, req, m, addr6, control,
> break;
> }
> s = splsoftnet();
>- error = in6_pcbconnect(in6p, addr6);
>+ error = in6_pcbconnect(in6p, addr6, p);
> splx(s);
> if (error == 0)
> soisconnected(so);
This looks fine to me.
christos