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:
parent
e7f6729d6c
commit
33b3ac0633
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=14195
@ -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.
|
||||
*
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user