0

I'm writing a daemon which needs to accept unnamed pipe connections identified by a PID from potentially untrusted applications. With Unix domain sockets, this is not a complex task: create a socket on the filesystem, start listening, accept incoming connections, receive the file descriptors for the reading end of the unnamed pipes via ancillary data, make sure that the application sending the file descriptor is what it pretends to be (only the PID is needed to figure out the rest, which is handy), then start receiving data from the application using the private unnamed pipe connection while still listening to the socket for new connections.

On Windows, however, the only IPC method which can be recognizable by third-party applications (and thus can be used for establishing connections via other methods of IPC without having a parent-child relationship) is named pipes (known in Unix terminology as FIFO files). Thanks to DuplicateHandle(), there's no need to use some kind of ancillary data transmission system to get a file descriptor from an application any process can send any of its handles to any other process, provided that the target process knows the handle value via some form of IPC, which is, in my case, a named pipe. However, a named pipe does not identify writers, i.e. if an application writes a handle x there and pretends to have a certain PID a, there's no way to know that whoever is writing on the unnamed pipe on the handle x is actually the process with PID a and not PID b trying to mess up records management in the daemon.

TL;DR how can I create an unnamed pipe connection between a daemon and a non-child process on Windows and be absolutely sure that the pipe handle sent by that process indeed belongs to it and not to an imposter process?

Kotauskas
  • 1,239
  • 11
  • 31
  • 1
    [`GetNamedPipeClientProcessId`](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getnamedpipeclientprocessid) gets the PID of the process that opened the client side. The client's handle might be duplicated or inherited to another process, but the duplicating process needs `PROCESS_DUP_HANDLE` discretionary access to the client. By default this access is only granted to SYSTEM and the security principal that owns a process. Mandatory access control on a process also denies read-up access for lower integrity processes, so it's doubly protected. – Eryk Sun Jul 05 '20 at 00:16

2 Answers2

1

The correct answer to the question today is that Windows has an analogue to UDS called Unix Domain Sockets. They are used in the same way as in Unix.

Aron
  • 15,464
  • 3
  • 31
  • 64
0

I decided to approach this problem completely differently, since even if I knew the PID of the other process (which would allow me to confirm the identity of the other process by finding the .exe file of the process and reading its headers/matching against its file path), I wouldn't be confident that the program isn't trying to sabotage the daemon, since it can send an arbitrary handle value instead of creating a pipe and sending the real reading handle to the daemon. Best case — the daemon gracefully rejects the connection, worst case — it crashes or fills up the entire hard drive with garbage from an unrelated file. This is definitely a serious problem for a system-wide daemon.

To fix this, I switched the sides around. The client sends an initialization request packet (something like "Hello, please register me in the daemon"), the daemon uses GetNamedPipeClientProcessId, as suggested by this comment, to fetch the PID and identify the application, creates an unnamed pipe and uses DuplicateHandle to send the handle to the client, thanks to the GetCurrentProcess pseudohandle having full permissions, and then sends the writing handle to the application. This way, the responsibility of sending a valid handle is on the daemon, and the choice to trust it is for the client application, especially since it's open source, allowing the application developers who want to use the daemon to be sure that it's secure to receive handles from it.

Kotauskas
  • 1,239
  • 11
  • 31