Make the default behavior of local port assignment match traditional

systems (my last change did not mix well with some firewall
configurations).  As much as I dislike firewalls, this is one thing I
I was not prepared to break by default.. :-)

Allow the user to nominate one of three ranges of port numbers as
candidates for selecting a local address to replace a zero port number.
The ranges are selected via a setsockopt(s, IPPROTO_IP, IP_PORTRANGE, &arg)
call.  The three ranges are: default, high (to bypass firewalls) and
low (to get a port below 1024).

The default and high port ranges are sysctl settable under sysctl
net.inet.ip.portrange.*

This code also fixes a potential deadlock if the system accidently ran out
of local port addresses. It'd drop into an infinite while loop.

The secure port selection (for root) should reduce overheads and increase
reliability of rlogin/rlogind/rsh/rshd if they are modified to take
advantage of it.

Partly suggested by: pst
Reviewed by: wollman
This commit is contained in:
Peter Wemm 1996-02-22 21:32:23 +00:00
parent e7f6729d6c
commit 33b3ac0633
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=14195
4 changed files with 163 additions and 30 deletions

View File

@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)in.h 8.3 (Berkeley) 1/3/94
* $Id: in.h,v 1.13 1995/11/14 20:33:57 phk Exp $
* $Id: in.h,v 1.14 1996/01/19 08:00:57 peter Exp $
*/
#ifndef _NETINET_IN_H_
@ -66,27 +66,52 @@
/*
* Local port number conventions:
*
* When a user does a bind(2) or connect(2) with a port number of zero,
* a non-conflicting local port address is chosen.
* The default range is IPPORT_RESERVED through
* IPPORT_USERRESERVED, although that is settable by sysctl.
*
* A user may set the IPPROTO_IP option IP_PORTRANGE to change this
* default assignment range.
*
* The value IP_PORTRANGE_DEFAULT causes the default behavior.
*
* The value IP_PORTRANGE_HIGH changes the range of candidate port numbers
* into the "high" range. These are reserved for client outbound connections
* which do not want to be filtered by any firewalls.
*
* The value IP_PORTRANGE_LOW changes the range to the "low" are
* that is (by convention) restricted to privileged processes. This
* convention is based on "vouchsafe" principles only. It is only secure
* if you trust the remote host to restrict these ports.
*
* The default range of ports and the high range can be changed by
* sysctl(3). (net.inet.ip.port{hi}{first,last}_auto)
*
* Changing those values has bad security implications if you are
* using a a stateless firewall that is allowing packets outside of that
* range in order to allow transparent outgoing connections.
*
* Such a firewall configuration will generally depend on the use of these
* default values. If you change them, you may find your Security
* Administrator looking for you with a heavy object.
*/
/*
* Ports < IPPORT_RESERVED are reserved for
* privileged processes (e.g. root).
* privileged processes (e.g. root). (IP_PORTRANGE_LOW)
* Ports > IPPORT_USERRESERVED are reserved
* for servers, not necessarily privileged.
* for servers, not necessarily privileged. (IP_PORTRANGE_DEFAULT)
*/
#define IPPORT_RESERVED 1024
#define IPPORT_USERRESERVED 5000
/*
* Range of ports for automatic assignment to local addresses that
* have not explicitly specified an address.
*
* These can be overridden at kernel config time, and are used to init
* sysctl variables. The sysctl variables can be changed at runtime.
* Default local port range to use by setting IP_PORTRANGE_HIGH
*/
#ifndef IPPORT_FIRSTAUTO
#define IPPORT_FIRSTAUTO 20000
#endif
#ifndef IPPORT_LASTAUTO
#define IPPORT_LASTAUTO 30000
#endif
#define IPPORT_HIFIRSTAUTO 40000
#define IPPORT_HILASTAUTO 44999
/*
* Internet address (a structure for historical reasons)
@ -183,6 +208,7 @@ struct ip_opts {
#define IP_RSVP_OFF 16 /* disable RSVP in kernel */
#define IP_RSVP_VIF_ON 17 /* set RSVP per-vif socket */
#define IP_RSVP_VIF_OFF 18 /* unset RSVP per-vif socket */
#define IP_PORTRANGE 19 /* int; range to choose for unspec port */
/*
* Defaults and limits for options
@ -199,6 +225,14 @@ struct ip_mreq {
struct in_addr imr_interface; /* local IP address of interface */
};
/*
* Argument for IP_PORTRANGE:
* - which range to search when port is unspecified at bind() or connect()
*/
#define IP_PORTRANGE_DEFAULT 0 /* default range */
#define IP_PORTRANGE_HIGH 1 /* "high" - request firewall bypass */
#define IP_PORTRANGE_LOW 2 /* "low" - vouchsafe security */
/*
* Definitions for inet sysctl operations.
*

View File

@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)in_pcb.c 8.4 (Berkeley) 5/24/95
* $Id: in_pcb.c,v 1.15 1995/11/14 20:33:59 phk Exp $
* $Id: in_pcb.c,v 1.16 1996/01/19 08:00:58 peter Exp $
*/
#include <sys/param.h>
@ -65,13 +65,21 @@ struct in_addr zeroin_addr;
* These configure the range of local port addresses assigned to
* "unspecified" outgoing connections/packets/whatever.
*/
static int ipport_firstauto = IPPORT_FIRSTAUTO;
static int ipport_lastauto = IPPORT_LASTAUTO;
static int ipport_firstauto = IPPORT_RESERVED; /* 1024 */
static int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */
static int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 40000 */
static int ipport_hilastauto = IPPORT_HILASTAUTO; /* 44999 */
SYSCTL_INT(_net_inet_ip, OID_AUTO, port_first_auto, CTLFLAG_RW,
SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports");
SYSCTL_INT(_net_inet_ip_portrange, OID_AUTO, first, CTLFLAG_RW,
&ipport_firstauto, 0, "");
SYSCTL_INT(_net_inet_ip, OID_AUTO, port_last_auto, CTLFLAG_RW,
SYSCTL_INT(_net_inet_ip_portrange, OID_AUTO, last, CTLFLAG_RW,
&ipport_lastauto, 0, "");
SYSCTL_INT(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLFLAG_RW,
&ipport_hifirstauto, 0, "");
SYSCTL_INT(_net_inet_ip_portrange, OID_AUTO, hilast, CTLFLAG_RW,
&ipport_hilastauto, 0, "");
static void in_pcbinshash __P((struct inpcb *));
static void in_rtchange __P((struct inpcb *, int));
@ -162,15 +170,61 @@ in_pcbbind(inp, nam)
}
inp->inp_laddr = sin->sin_addr;
}
if (lport == 0)
do {
++*lastport;
if (*lastport < ipport_firstauto ||
*lastport > ipport_lastauto)
*lastport = ipport_firstauto;
lport = htons(*lastport);
} while (in_pcblookup(head,
zeroin_addr, 0, inp->inp_laddr, lport, wild));
if (lport == 0) {
ushort first, last;
int count;
if (inp->inp_flags & INP_HIGHPORT) {
first = ipport_hifirstauto; /* sysctl */
last = ipport_hilastauto;
} else if (inp->inp_flags & INP_LOWPORT) {
if (error = suser(p->p_ucred, &p->p_acflag))
return (EACCES);
first = IPPORT_RESERVED - 1; /* 1023 */
last = 1;
} else {
first = ipport_firstauto; /* sysctl */
last = ipport_lastauto;
}
/*
* Simple check to ensure all ports are not used up causing
* a deadlock here.
*
* We split the two cases (up and down) so that the direction
* is not being tested on each round of the loop.
*/
if (first > last) {
/*
* counting down
*/
count = first - last;
do {
if (count-- <= 0) /* completely used? */
return (EADDRNOTAVAIL);
--*lastport;
if (*lastport > first || *lastport < last)
*lastport = first;
lport = htons(*lastport);
} while (in_pcblookup(head,
zeroin_addr, 0, inp->inp_laddr, lport, wild));
} else {
/*
* counting up
*/
count = last - first;
do {
if (count-- <= 0) /* completely used? */
return (EADDRNOTAVAIL);
++*lastport;
if (*lastport < first || *lastport > last)
*lastport = first;
lport = htons(*lastport);
} while (in_pcblookup(head,
zeroin_addr, 0, inp->inp_laddr, lport, wild));
}
}
inp->inp_lport = lport;
in_pcbrehash(inp);
return (0);

View File

@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)in_pcb.h 8.1 (Berkeley) 6/10/93
* $Id: in_pcb.h,v 1.9 1995/11/14 20:34:03 phk Exp $
* $Id: in_pcb.h,v 1.10 1995/12/05 21:26:34 bde Exp $
*/
#ifndef _NETINET_IN_PCB_H_
@ -78,6 +78,8 @@ struct inpcbinfo {
#define INP_RECVDSTADDR 0x04 /* receive IP dst address */
#define INP_CONTROLOPTS (INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR)
#define INP_HDRINCL 0x08 /* user supplies entire IP header */
#define INP_HIGHPORT 0x10 /* user wants "high" port binding */
#define INP_LOWPORT 0x20 /* user wants "low" port binding */
#define INPLOOKUP_WILDCARD 1

View File

@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)ip_output.c 8.3 (Berkeley) 1/21/94
* $Id: ip_output.c,v 1.26 1995/12/05 17:46:15 wollman Exp $
* $Id: ip_output.c,v 1.27 1995/12/19 21:24:19 wollman Exp $
*/
#include <sys/param.h>
@ -635,6 +635,35 @@ ip_ctloutput(op, so, level, optname, mp)
error = ip_setmoptions(optname, &inp->inp_moptions, m);
break;
case IP_PORTRANGE:
if (m == 0 || m->m_len != sizeof(int))
error = EINVAL;
else {
optval = *mtod(m, int *);
switch (optval) {
case IP_PORTRANGE_DEFAULT:
inp->inp_flags &= ~(INP_LOWPORT);
inp->inp_flags &= ~(INP_HIGHPORT);
break;
case IP_PORTRANGE_HIGH:
inp->inp_flags &= ~(INP_LOWPORT);
inp->inp_flags |= INP_HIGHPORT;
break;
case IP_PORTRANGE_LOW:
inp->inp_flags &= ~(INP_HIGHPORT);
inp->inp_flags |= INP_LOWPORT;
break;
default:
error = EINVAL;
break;
}
}
default:
error = ENOPROTOOPT;
break;
@ -699,6 +728,20 @@ ip_ctloutput(op, so, level, optname, mp)
error = ip_getmoptions(optname, inp->inp_moptions, mp);
break;
case IP_PORTRANGE:
*mp = m = m_get(M_WAIT, MT_SOOPTS);
m->m_len = sizeof(int);
if (inp->inp_flags & INP_HIGHPORT)
optval = IP_PORTRANGE_HIGH;
else if (inp->inp_flags & INP_LOWPORT)
optval = IP_PORTRANGE_LOW;
else
optval = 0;
*mtod(m, int *) = optval;
break;
default:
error = ENOPROTOOPT;
break;