The BSD r* commands and the history of privileged TCP ports
Once upon a time, UCB was adding TCP/IP to (BSD) Unix. They had multiple Unix machines, and one obvious thing to want when you have networking and multiple Unix machines is a way to log in and transfer files from one machine to another. Fortunately for the UCB BSD developers, TCP/IP already had well-specified programs (and protocols) to do this, namely telnet and FTP. So all they had to do was implement telnet and FTP clients and servers and they were done, right?
The USB BSD people did implement telnet and FTP, but they weren't satisfied with just that, apparently because neither was convenient and flexible enough. In particular, telnet and FTP have baked into them the requirement for a password. Obviously you need to ask people to authenticate themselves (with a password) when you're accepting remote connections from who knows where, but the UCB people were just logging in and transferring files and so on between their own local collection of Vaxes. So the BSD people wound up creating a better way, in the form of the r* commands: rlogin, rsh, and rcp. The daemons that implemented these were rlogind and rshd.
(See also rcmd(3).)
The authentication method was pretty simple; it relied on checking
/etc/hosts.equiv
to see if the client host was trusted in general,
or ~/.rhosts
to see if you were allowing logins from that particular
remote user on the remote host. As part of this, these daemons
obviously relied on the client not lying about who the remote user
was (and what their login name was). How did they have some assurance
about this? The answer is that the BSD developers added a hack to
their TCP/UDP implementation, namely a new idea of 'privileged ports'.
Privileged ports were ports under 1024 (aka IPPORT_RESERVED
),
and the hack was that the kernel only allowed them to be used by
UID 0. If you asked for 'give me any port', they were skipped, and
if you weren't root and tried to bind(2)
to such a port, the
kernel rejected you. Rlogind and rshd insisted that client connections
come from privileged ports, at which point they knew that it was a
root-owned process talking to them from the client and they could
trust its claims of what the remote login name was. Ordinary users
on the client couldn't make their own connection and claim to be
someone else, because they wouldn't be allowed to use a privileged
port.
(Sun later reused this port-based authentication mechanism as part of NFS security.)
Based on the Unix versions available on tuhs.org, all of this appears to
have been introduced in 4.1c BSD. This is the
version that adds IPPORT_RESERVED
to netinet/in.h
and has the TCP/UDP port binding code check it in in_pcbbind
in netinet/in_pcb.c.
In case you think the BSD people thought that this was an elegant idea,
let me show you the code:
if (lport) { u_short aport = htons(lport); int wild = 0; /* GROSS */ if (aport < IPPORT_RESERVED && u.u_uid != 0) return (EACCES); [...]
The BSD people knew this was a hack; they just did it anyway, probably because it was a very handy hack in their trusted local network environment. Unix has quietly inherited it ever since.
(Privileged ports are often called 'reserved ports', as in 'reserved
for root only'. Even the 4.1c BSD usage here is inconsistent; the
actual #define
is IPPORT_RESERVED
, but things like the
rlogind manpage
talk about 'privileged port numbers'. Interestingly, in 4.1c BSD
the source code for the r* programs is hanging out in an odd place,
in /usr/src/ucb/netser,
along with several other things. By the release of 4.2 BSD, they had
all been relocated to /usr/src/ucb
and /usr/src/etc,
where you'd expect.)
PS: Explicitly using a privileged port when connecting to a server
is one of the rare cases when you need to call bind()
for an
outgoing socket, which is usually something you want to avoid.
Sidebar: The BUGS section of rlogind and rshd is wise
In the style of Unix manpages admitting big issues that they don't have any good way of dealing with at the moment, the manpages of both rlogind and rshd end with:
.SH BUGS The authentication procedure used here assumes the integrity of each client machine and the connecting medium. This is insecure, but is useful in an ``open'' environment. .PP A facility to allow all data exchanges to be encrypted should be present.
These issues would stay for a decade or so, getting slowly more significant over time, until they were finally fixed by SSH in 1995. Well, starting in 1995, the switch wasn't exactly instant and even now the process of replacing rsh and rlogin is sort of ongoing (and also).
Comments on this page:
|
|