Bugtraq mailing list archives

Re: (spoofed) RPC portmapper set/unset


From: wpaul () CTR COLUMBIA EDU (Bill Paul)
Date: Sun, 15 Nov 1998 11:38:39 -0500


Of all the gin joints in all the towns in all the world, Theo de Raadt
had to walk into mine and say:

Regarding:

-pmap_set : it is called when a rpc program wants to register itself in the
portmapper list (rpcinfo -p returns this list).

-pmap_unset : same as above but it's used to unregister a rpc program.
Again, Wietse' portmapper fixed almost all the holes related to pset/punset
rpc calls.

However, due to a restriction in the protocol, all the security problems
cannot be fixed easily. When a rpc program (such as rpc.mountd) wants to
un/register itself on the portmapper list, it sends an udp || tcp packet to
the portmapper (port 111) using the pmap_set or pmap_unset respectively.
[...]

There's really only one way to completely fix this problem with portmap,
and that is simply not to use TCP or UDP. Instead, you need to use a
local-only transport, such as an AF_LOCAL socket. It's actually very
easy to do this: I took copies of the TCP transport client and server
modules from the FreeBSD RPC library and converted them to use AF_LOCAL
stream sockets instead of AF_INET.

Additionally, if you use an AF_LOCAL socket in conjunction with SCM_CREDS
credential transfers, you can provide a much more secure mechanism for
preventing local users from setting/unsetting mappings that don't
belong to them.

As you say, this does require changes to libc, so it's not something
to be undertaken lightly and it would be difficult to manage with
commercial systems for which you don't have the source code.

With TI-RPC, you already have a special loopback transport available
which provides similar functionality. However TI-RPC is based on TLI,
and none of the BSD-based systems or Linux have support for TLI or
STREAMS (yet) so they use the older RPC 4.0 instead, which only
supports TCP and UDP. I had a choice between trying to implement all
of the necessary support code to make TI-RPC work in FreeBSD or add
an additional transport to the existing RPC library. Lacking the
time and understanding needed to do the former, I instead chose the
latter.

The advantage to using an AF_LOCAL socket is that right away the server
can tell if it's handling a remote or local request by checking the
family type of the socket in its transport handle. TCP or UDP sockets
will be AF_INET instead of AF_LOCAL. Procedures that really care
about enforcing access can choose to deny connections from AF_INET
sockets, which will lock out any non-local process. Also, by using
SCM_CREDS, the portmapper can check which local user is sending the
request (and can be relatively sure of the information since it's
provided by the kernel, not the caller, and is thus harder to spoof).
This means that if user A registers a service, the portmapper can
insure that only user A (or root) can unregister it later. Right
now, access checks are performed in rpcinfo, which is a bad idea
(access checks should be in the server, not the client).

You still need to allow UDP/TCP access to some of the proceedures
since remote hosts have to be able to reference the port/program number
mappings and use the callit routine, but restricting the set/unset
procedures by only allowing AF_LOCAL connections insures that no
remote process can ever fiddle with the mapping tables.

I had plans to do this for FreeBSD and actually added code to libc
to test for an AF_LOCAL socket special file and use that if it's
present, rolling over to the old mechanism if it isn't. However I
still need to modify portmap itself, which is a little tricky because
I want to store the EUID of the caller that registers a service for
access checks later, and the structure used to store the mappings
doesn't have room for this. Unfortunately, this structure is set in
stone in the portmap protocol specification. Before I was able to
get very far on this, I got sidetracked by other things.

My main reason for creating this extra transport and adding the SCM_CREDS
support to the kernel was for the Secure RPC keyserv program, which, in
RPC 4.0, also uses a terrible kludge not too different from the mess in
portmap to enforce access restrictions to its secret key tables. Keyserv
normally relies on the bletcherous keyenvoy program, which the client
has to invoke in order to talk to the key server. The problem here is
that a client can't use Secure RPC credentials and verifiers until
the user does a keylogin to load his secret key into the local keyserv,
but keyserv also needs to use an authentication mechanism to make sure
that another user can't spoof a request to steal the user's secret key
once it's been loaded. TI-RPC uses the loopback transport and a special
TLI routine called t_getinto() to do this, which does away with the need
for the keyenvoy program. However FreeBSD doesn't have TLI/STREAMS, so
I had to improvise.

-Bill

--
=============================================================================
-Bill Paul            (212) 854-6020 | System Manager, Master of Unix-Fu
Work:         wpaul () ctr columbia edu | Center for Telecommunications Research
Home:  wpaul () skynet ctr columbia edu | Columbia University, New York City
=============================================================================
"Mulder, toads just fell from the sky!" "I guess their parachutes didn't open."
=============================================================================



Current thread: