From owner-freebsd-hackers Sun Nov 24 13:24:07 1996 Return-Path: owner-hackers Received: (from root@localhost) by freefall.freebsd.org (8.7.5/8.7.3) id NAA12788 for hackers-outgoing; Sun, 24 Nov 1996 13:24:07 -0800 (PST) Received: from skynet.ctr.columbia.edu (skynet.ctr.columbia.edu [128.59.64.70]) by freefall.freebsd.org (8.7.5/8.7.3) with SMTP id NAA12739 for ; Sun, 24 Nov 1996 13:23:54 -0800 (PST) Received: (from wpaul@localhost) by skynet.ctr.columbia.edu (8.6.12/8.6.9) id QAA02399 for hackers@freebsd.org; Sun, 24 Nov 1996 16:22:58 -0500 From: Bill Paul Message-Id: <199611242122.QAA02399@skynet.ctr.columbia.edu> Subject: looking for an idea To: hackers@freebsd.org Date: Sun, 24 Nov 1996 16:22:57 -0500 (EST) X-Mailer: ELM [version 2.4 PL24] Content-Type: text Sender: owner-hackers@freebsd.org X-Loop: FreeBSD.org Precedence: bulk Okay, here's a small problem: you have two processes, A and B, running on the same machine. Process A is a server and B is a client. A and B communicate via an AF_UNIX socket. B contacts A and sends it a message, which includes B's UID, however there is the possibility that B may lie about its identity, so A would like very much to be able to learn the UID of B without any possibility of deception on B's part. Now, the problem is to find a way for A to verify B's UID without resorting to grovelling in /dev/kmem. This is necessary since A would need privileges to open /dev/kmem, and A and B might not be run by privileged users. The specific problem I'm trying to solve here has to do with keyserv(8) from the Secure RPC distribution. The original way this problem was 'solved' by Sun in RPCSRC 4.0 was to use a program called keyenvoy(8). The keyserv daemon would refuse any connections from clients which a) did not originate on a port >= IPPORT_RESERVED and b) did not originate from INADDR_LOOPBACK. Meanwhile, the keyenvoy program was suid-root, and would be exec()ed as part of the key_call() procedure in the RPC library. The keyenvoy program would do a getuid() to learn the real UID of the caller and pass this on to keyserv. Keyserv would accept the supplied UID as valid since, supposedly, only keyenvoy could have sent the message and the caller could not coerce keyenvoy into lying. This approach is flawed mainly because RPC 4.0 only supports IP-based transports (tcp and udp) which can be spoofed: although keyserv may be able to determine that it received a request from 127.0.0.1, it can't be sure that the request really arrived via the loopback interface (unless it performs some system-dependent mucking about inside kernel memory). Also, keyenvoy is invoked using vfork()/exec() which results in a performance hit for processes that need to make many calls to keyserv. (Aside: if you're thinking of suggesting that I use identd, forget it. Maybe Linux developers would be satisfied with that; I'm not.) In TI-RPC, which is what's in Slowlaris 2.x, this problem was solved in a different way by taking advantage of the fact that the underlying code now uses STREAMS/TLI rather than sockets. There is apparently some way when using the loopback transport for keyserv to learn the UID of the process on the other side of the TLI endpoint. Duplicating this in BSD is hard. One thing I've done is to implement a third transport for the RPC library called "unix" which uses AF_UNIX stream sockets rather than AF_INET sockets. (This can be done pretty easily just by copying the clnt_tcp.c and svc_tcp.c modules and making some relatively minor changes.) When keyserv starts, it now creates three transport handles: one for tcp, one for udp and one for unix. Creating the unix transport also creates a socket special file called /var/run/keyservsock, which is owned by root and mode 000. The keyenvoy program uses this socket to communicate witht he server. The keyserv program can get the sockaddr structure for the connection and check the family type: if it's AF_UNIX, the connection is local and can be trusted; if it's AF_INET, the server logs a message and discards the request. This makes the keyenvoy mechanism work as intended: IP spoofing is no longer an issue, and local users _must_ use keyenvoy to contact keyserv. However I want to eliminate keyenvoy entirely. When the mechanism I have now, it's possible for processes with UID 0 to bypass keyenvoy and contact keyserv directly. This means that system servers that require AUTH_DES authentication (like, say, rpc.nisd) can avoid the performance hit, but unprivileged client programs are still forced to use keyenvoy. So far I've only found one potential solution to this problem, but it's not pretty. It occured to me that if process A can learn the PID of process B, then it can map that into a UID by examining the /proc filesystem (/proc//status). One way to do this is to use SysV IPC. If A creates a message queue, and B sends A a message with msgsnd() just before sending its usual RPC, then A can read the message and then do a msgctl(msgid, IPC_STAT, &mqid) and examine the mqid.msg_lspid member, which should tell it the PID of the last process to call msgsnd() on this particular message queue. This, in concert with /proc//status gets you the UID. (This can also be embedded in the RPC library and keyserv code so that it's invisible to the user.) But this will only work if PROCFS and SysV IPC are configured into the kernel and /proc is mounted. Its also possible that there may be a race condition involved (maybe I could solve that with a semaphore -- Gaaahhh!!). Previously, I also experimented with sending a file descriptor over the AF_UNIX socket from the client to the server using sendmsg()/revcmsg(), but this doesn't provide any useful (i.e. trustworthy) information either. I thought about having the client do an fcntl(s, F_SETOWN, getpid()) on the descriptor and then passing it to the server, which could then read the PID back with fcntl(s, F_GETOWN, 0), but this doesn't work because fcntl() basically allows you to specify any number as a PID. Of course, it's possible to hack the kernel such that it will copy the UID of the sending process into the message somewhere if you supply the right flag, but I'd like to avoid that; having the code fail on other *BSD systems would be bad. So: I'm open to suggestions. Is there any nice and easy way for a server process on one side of an AF_UNIX socket to learn the UID of the process on the other side? I really want to toss keyenvoy in the dumper. -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 ============================================================================= "If you're ever in trouble, go to the CTR. Ask for Bill. He will help you." =============================================================================