Windows Named Pipe Access control - windows

My process (server) creates a child process (client) by CreateProcess and I am doing IPC between these processes. I begin with anonymous pipe, but soon I find that it does not support overlapped operations as explained here.
So, named-pipe is my second choice. My confusion is: if I create a named-pipe, is it possible to limit the access of this pipe only to my child process created by previously call to CreateProcess? Thus, even if another process obtains the Pipe's Name, it still cannot read or write to the pipe.
My IPC usage only limits to local machine and single platform (Windows).
BTW, I can change both codes for these processes.

You could explicitly assign an ACL to the new pipe by using the lpSecurityAttributes parameter. This would allow you to ensure that, if another user is logged on, they can't connect to the pipe.
However, if you create both ends of the pipe in the parent process there is very little scope for malfeasance, so in general explicitly setting an ACL is not necessary. Once you have opened the client end of the pipe, no other process can connect to the pipe anyway (you would have to create a second instance if you wanted them to do so) so there is only a very brief interval during which another process could interfere; and even if that happened, you wouldn't be able to connect the client end, so you would know something had gone wrong.
In other words, the scope for attack is limited to denial of service, and since the attacking process would need to be running on the same machine, it can achieve a much more effective denial of service simply by tanking the CPU.
Note that:
You should use the FILE_FLAG_FIRST_PIPE_INSTANCE flag when creating the pipe, to ensure that you know if there is a name collision.
You should also use PIPE_REJECT_REMOTE_CLIENTS for obvious reasons.
The default permissions on a named pipe do not allow other non-administrative users to create a new instance, so a man-in-the-middle style attack is not a risk in this case.
A malicious process running as the same user, or as an administrative user, could potentially man-in-the-middle your connection (regardless of whether you set an ACL or not) but since any such malicious process could also inject malicious code directly into the parent and/or child there is little point in worrying about it. The attacker is already on the wrong side of the air-tight hatchway; locking the windows won't do you any good.
If your process is running with elevated privilege, you probably should set an ACL on the pipe. The default ACL would potentially allow non-elevated processes running as the same user context to man-in-the-middle the connection. You can resolve this by setting an ACL that grants full access only to Administrators. The risk is still minimal, but in this particular case a defense-in-depth measure is probably appropriate.
An anonymous pipe is implemented as a named pipe with a unique name, so you haven't actually lost anything by using a named pipe. An attacker could in principle man-in-the-middle an anonymous pipe just as easily as a named one. (Edit: according to RbMm, this is no longer true.)

Asynchronous (overlapped) operations of course full supported by anonymous pipes. supported asynchronous operations or no - depending only from are FILE_SYNCHRONOUS_IO_[NO]NALERT used in call ZwCreateNamedPipeFile and ZwOpenFile, but not from which name (or empty) have pipe. CreatePipe create pipe pair with FILE_SYNCHRONOUS_IO_NONALERT option - only because this handles returned from this api can not be used in asynchronous operation. unfortunately CreatePipe have no parameters to change this behavior, but we can yourself do this task
begin from vista we can create anonymous (unnamed) and asynchronous pipe pair, but for this you need use ndll api. next code is almost similar CreatePipe internal code, except i create asynchronous pipe pair.
NTSTATUS CreatePipeAnonymousPair(PHANDLE phServerPipe, PHANDLE phClientPipe)
{
HANDLE hFile;
IO_STATUS_BLOCK iosb;
static UNICODE_STRING NamedPipe = RTL_CONSTANT_STRING(L"\\Device\\NamedPipe\\");
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &NamedPipe, OBJ_CASE_INSENSITIVE };
NTSTATUS status;
if (0 <= (status = ZwOpenFile(&hFile, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_VALID_FLAGS, 0)))
{
oa.RootDirectory = hFile;
static LARGE_INTEGER timeout = { 0, MINLONG };
static UNICODE_STRING empty = {};
oa.ObjectName = ∅
if (0 <= (status = ZwCreateNamedPipeFile(phServerPipe,
FILE_READ_ATTRIBUTES|FILE_READ_DATA|
FILE_WRITE_ATTRIBUTES|FILE_WRITE_DATA|
FILE_CREATE_PIPE_INSTANCE,
&oa, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_CREATE, 0, FILE_PIPE_BYTE_STREAM_TYPE, FILE_PIPE_BYTE_STREAM_MODE,
FILE_PIPE_QUEUE_OPERATION, 1, 0, 0, &timeout)))
{
oa.RootDirectory = *phServerPipe;
oa.Attributes = OBJ_CASE_INSENSITIVE|OBJ_INHERIT;
if (0 > (status = ZwOpenFile(phClientPipe, FILE_READ_ATTRIBUTES|FILE_READ_DATA|
FILE_WRITE_ATTRIBUTES|FILE_WRITE_DATA, &oa, &iosb, FILE_SHARE_VALID_FLAGS, 0)))
{
ZwClose(oa.RootDirectory);
*phServerPipe = 0;
}
}
ZwClose(hFile);
}
return status;
}
note that hClientPipe created as Inherited - so can pass it to child process. also when you will be use hServerPipe in ConnectNamedPipe you got FALSE with GetLastError() == ERROR_PIPE_CONNECTED (because client is already connected)/ or if you will be use FSCTL_PIPE_LISTEN - you got STATUS_PIPE_CONNECTED - this is really not error but ok code

Related

Execution abnormalities depending on process creation (ShellExecute vs CreateProcess)

We're running a Windows Service which is responsible for monitoring a set of processes. The service is basically just responsible for (a) checking if the defined jobs are running, and (b) starting the jobs if they are not.
The service is created via the following command (sc: https://technet.microsoft.com/en-us/library/bb490995.aspx):
sc create "My Service" binPath= C:\heyoo\myservice.exe type= own start= auto error= normal
sc start "SCF Service"
One of the jobs the service is responsible for creating is 'Camera.exe'. Camera.exe retrieves a video feed from the connected cameras (FireWire 1394), and does some processing on them.
A week ago, the service was rewritten from using ShellExecute to use CreateProcess so it is able to better monitor the defined jobs (as it gets the HANDLE to the process).
ShellExecute call (old method):
bool Execute()
{
int result = (int)ShellExecute(NULL, "open", "C:\\bin\\Camera.exe", NULL, NULL, SW_SHOWDEFAULT);
return result > 32;
}
CreateProcess call (new method):
// Called with Execute("C:\\bin\\Camera.exe", "");
bool Execute(std::string prog, std::string args)
{
std::string cmd = std::string(prog) + " " + args;
char *path = new char[cmd.length()+1];
strcpy(path, cmd.c_str());
STARTUPINFO si = {0};
si.cb = sizeof(STARTUPINFO);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
DWORD creationFlags = REALTIME_PRIORITY_CLASS;
BOOL result = CreateProcess(NULL, path, NULL, NULL, FALSE, creationFlags, NULL, NULL, &si, &pi);
delete[] path;
if (result) {
SetProcInfo(pi);
}
return result;
}
With the new CreateProcess method, we noticed that (A) the network systematically fails after a certain interval, and (B) the images retrieved from the cameras contain invalid timestamps (correct timestamps are crucial to us).
A frequently takes down the entire network connection, and requires a reboot to get back online. B causes the processing of images to fail, as we are highly dependent on valid timestamps.
The problems (A & B) does only arise when Service.exe is run as a service. When running Service.exe or Camera.exe from the command line, none of the problems occurr.
Today I removed the CreateProcess calls from the service (went back to ShellExecute), and the problems disappeared again. What am I doing wrong with the API call?
DWORD creationFlags = REALTIME_PRIORITY_CLASS;
This is the most obvious difference. When you call ShellExecute, the process will be created with normal priority. The documentation for real time priority says:
Process that has the highest possible priority. The threads of a real-time priority class process preempt the threads of all other processes, including operating system processes performing important tasks. For example, a real-time process that executes for more than a very brief interval can cause disk caches not to flush or cause the mouse to be unresponsive.
You really don't want to do this!
Pass 0 as the creation flags. The documentation says:
If none of the priority class flags is specified, the priority class defaults to NORMAL_PRIORITY_CLASS unless the priority class of the creating process is IDLE_PRIORITY_CLASS or BELOW_NORMAL_PRIORITY_CLASS. In this case, the child process receives the default priority class of the calling process.
For what it is worth, you can obtain a process handle by using ShellExecuteEx rather than ShellExecute. In fact you should always prefer ShellExecuteEx to ShellExecute since the latter cannot report errors properly. Even so, you are creating a new process, and so CreateProcess is the right function for that task.

How to reregister for IO Completion ports for a handle

I have a Windows named pipe that I create with CreateFile (the server side was created using CreateNamedPipe). I use IO completion ports to read/write data asynchronously on both ends.
I need to send these handles to other processes after they've been opened. I tried to call CloseHandle on the handle returned from CreateIoCompletionPort, and then in the other process call CreateIoCompletionPort again. However it always fails and GetLastError returns 87 (ERROR_INVALID_PARAMETER).
I can also reproduce this in just one process, see below. Note there are no outstanding reads/write to the object before I send it.
std::wstring pipe_name = L"\\\\.\\pipe\\test.12345";
HANDLE server = CreateNamedPipeW(
pipe_name.c_str(),
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
1,
4096,
4096,
10000,
NULL);
SECURITY_ATTRIBUTES security_attributes = {
sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
HANDLE client = CreateFileW(
pipe_name.c_str(), GENERIC_READ | GENERIC_WRITE,
0,
&security_attributes,
OPEN_EXISTING,
SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED,
NULL);
ULONG_PTR key = 1;
HANDLE comp_port = CreateIoCompletionPort(client, NULL, key, 1);
BOOL b1 = CloseHandle(comp_port);
comp_port = CreateIoCompletionPort(client, NULL, key, 1);
if (comp_port == NULL) {
int last_err = GetLastError();
}
Referring to the documentation for CreateIoCompletionPort:
A handle can be associated with only one I/O completion port, and after the association is made, the handle remains associated with that I/O completion port until it [the handle] is closed.
[...] The I/O completion port handle and every file handle associated with that particular I/O completion port are known as references to the I/O completion port. The I/O completion port is released when there are no more references to it.
In other words, closing the I/O completion port handle doesn't achieve anything. The I/O completion port still exists and is permanently associated with the pipe handle. What you're attempting simply isn't possible; you will need to rearchitecture.
Note also:
It is best not to share a file handle associated with an I/O completion port by using either handle inheritance or a call to the DuplicateHandle function. Operations performed with such duplicate handles generate completion notifications. Careful consideration is advised.
The documentation for CreateIoCompletionPort suggests what you're trying to accomplish isn't possible. All handles associated with an I/O completion port refer to the port and as long one is still open the port remains alive:
The I/O completion port handle and every file handle associated with that particular I/O completion port are known as references to the I/O completion port. The I/O completion port is released when there are no more references to it. Therefore, all of these handles must be properly closed to release the I/O completion port and its associated system resources. After these conditions are satisfied, close the I/O completion port handle by calling the CloseHandle function.
It should work if you create a new handle that's not associated with the I/O completion port with CreateFile and then pass it to the other processes with DuplicateHandle. Or just call CreateFile in the other process directly.

Named pipes efficient asynchronous design

The problem:
To design an efficient and very fast named-pipes client server framework.
Current state:
I already have battle proven production tested framework. It is fast, however it uses one thread per one pipe connection and if there are many clients the number of threads could fast be to high. I already use smart thread pool (task pool in fact) that can scale with need.
I already use OVERLAPED mode for pipes, but then I block with WaitForSingleObject or WaitForMultipleObjects so that is why I need one thread per connection on the server side
Desired solution:
Client is fine as it is, but on the server side I would like to use one thread only per client request and not per connection. So instead of using one thread for the whole lifecycle of client (connect / disconnect) I would use one thread per task. So only when client requests data and no more.
I saw an example on MSDN that uses array of OVERLAPED structures and then uses WaitForMultipleObjects to wait on them all. I find this a bad design. Two problems I see here. First you have to maintain an array that can grow quite large and deletions will be costly. Second, you have a lot of events, one for each array member.
I also saw completion ports, like CreateIoCompletionPort and GetQueuedCompletionStatus, but I don't see how they are any better.
What I would like is something ReadFileEx and WriteFileEx do, they call a callback routine
when the operation is completed. This is a true async style of programming. But the problem is that ConnectNamedPipe does not support that and furthermore I saw that the thread needs to be in alertable state and you need to call some of the *Ex functions to have that.
So how is such a problem best solved?
Here is how MSDN does it: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365603(v=vs.85).aspx
The problem I see with this approach is that I can't see how you could have 100 clients connected at once if the limit to WaitForMultipleObjects is 64 handles. Sure I can disconnect the pipe after each request, but the idea is to have a permanent client connection just like in TCP server and to track the client through whole life-cycle with each client having unique ID and client specific data.
The ideal pseudo code should be like this:
repeat
// wait for the connection or for one client to send data
Result = ConnectNamedPipe or ReadFile or Disconnect;
case Result of
CONNECTED: CreateNewClient; // we create a new client
DATA: AssignWorkerThread; // here we process client request in a thread
DISCONNECT: CleanupAndDeleteClient // release the client object and data
end;
until Aborted;
This way we have only one listener thread that accepts connect / disconnect / onData events. Thread pool (worker thread) only process the actual request. This way 5 worker threads can serve a lot of clients that are connected.
P.S.
My current code should not be important. I code this in Delphi but its pure WinAPI so the language does not matter.
EDIT:
For now IOCP look like the solution:
I/O completion ports provide an efficient threading model for
processing multiple asynchronous I/O requests on a multiprocessor
system. When a process creates an I/O completion port, the system
creates an associated queue object for requests whose sole purpose is
to service these requests. Processes that handle many concurrent
asynchronous I/O requests can do so more quickly and efficiently by
using I/O completion ports in conjunction with a pre-allocated thread
pool than by creating threads at the time they receive an I/O request.
If server must handle more than 64 events (read/writes) then any solution using WaitForMultipleObjects becomes unfeasible. This is the reason the Microsoft introduced IO completion ports to Windows. It can handle very high number of IO operations using the most appropriate number of threads (usually it's the number of processors/cores).
The problem with IOCP is that it is very difficult to implement right. Hidden issues are spread like mines in the field: [1], [2] (section 3.6). I would recommend using some framework. Little googling suggests something called Indy for Delphi developers. There are maybe others.
At this point I would disregard the requirement for named pipes if that means coding my own IOCP implementation. It's not worth the grief.
I think what you're overlooking is that you only need a few listening named pipe instances at any given time. Once a pipe instance has connected, you can spin that instance off and create a new listening instance to replace it.
With MAXIMUM_WAIT_OBJECTS (or fewer) listening named pipe instances, you can have a single thread dedicated to listening using WaitForMultipleObjectsEx. The same thread can also handle the rest of the I/O using ReadFileEx and WriteFileEx and APCs. The worker threads would queue APCs to the I/O thread in order to initiate I/O, and the I/O thread can use the task pool to return the results (as well as letting the worker threads know about new connections).
The I/O thread main function would look something like this:
create_events();
for (index = 0; index < MAXIMUM_WAIT_OBJECTS; index++) new_pipe_instance(i);
for (;;)
{
if (service_stopping && active_instances == 0) break;
result = WaitForMultipleObjectsEx(MAXIMUM_WAIT_OBJECTS, connect_events,
FALSE, INFINITE, TRUE);
if (result == WAIT_IO_COMPLETION)
{
continue;
}
else if (result >= WAIT_OBJECT_0 &&
result < WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS)
{
index = result - WAIT_OBJECT_0;
ResetEvent(connect_events[index]);
if (GetOverlappedResult(
connect_handles[index], &connect_overlapped[index],
&byte_count, FALSE))
{
err = ERROR_SUCCESS;
}
else
{
err = GetLastError();
}
connect_pipe_completion(index, err);
continue;
}
else
{
fail();
}
}
The only real complication is that when you call ConnectNamedPipe it may return ERROR_PIPE_CONNECTED to indicate that the call succeeded immediately or an error other than ERROR_IO_PENDING if the call failed immediately. In that case you need to reset the event and then handle the connection:
void new_pipe(ULONG_PTR dwParam)
{
DWORD index = dwParam;
connect_handles[index] = CreateNamedPipe(
pipe_name,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_WAIT | PIPE_ACCEPT_REMOTE_CLIENTS,
MAX_INSTANCES,
512,
512,
0,
NULL);
if (connect_handles[index] == INVALID_HANDLE_VALUE) fail();
ZeroMemory(&connect_overlapped[index], sizeof(OVERLAPPED));
connect_overlapped[index].hEvent = connect_events[index];
if (ConnectNamedPipe(connect_handles[index], &connect_overlapped[index]))
{
err = ERROR_SUCCESS;
}
else
{
err = GetLastError();
if (err == ERROR_SUCCESS) err = ERROR_INVALID_FUNCTION;
if (err == ERROR_PIPE_CONNECTED) err = ERROR_SUCCESS;
}
if (err != ERROR_IO_PENDING)
{
ResetEvent(connect_events[index]);
connect_pipe_completion(index, err);
}
}
The connect_pipe_completion function would create a new task in the task pool to handle the newly connected pipe instance, and then queue an APC to call new_pipe to create a new listening pipe at the same index.
It is possible to reuse existing pipe instances once they are closed but in this situation I don't think it's worth the hassle.

Checking Win32 file streams for available input

I have a simple tunnel program that needs to simultaneously block on standard input and a socket. I currently have a program that looks like this (error handling and boiler plate stuff omitted):
HANDLE host = GetStdHandle(STD_INPUT_HANDLE);
SOCKET peer = ...; // socket(), connect()...
WSAEVENT gate = WSACreateEvent();
OVERLAPPED xfer;
ZeroMemory(&xfer, sizeof(xfer));
xfer.hEvent = gate;
WSABUF pbuf = ...; // allocate memory, set size.
// start an asynchronous transfer.
WSARecv(peer, &pbuf, 1, 0, &xfer, 0);
while ( running )
{
// wait until standard input has available data or the event
// is signaled to inform that socket read operation completed.
HANDLE handles[2] = { host, gate };
const DWORD which = WaitForMultipleObjects
(2, handles, FALSE, INFINITE) - WAIT_OBJECT_0;
if (which == 0)
{
// read stuff from standard input.
ReadFile(host, ...);
// process stuff received from host.
// ...
}
if (which == 1)
{
// process stuff received from peer.
// ...
// start another asynchronous transfer.
WSARecv(peer, &pbuf, 1, 0, &xfer, 0);
}
}
The program works like a charm, I can transfer stuff through this tunnel program without a hitch. The thing is that it has a subtle bug.
If I start this program in interactive mode from cmd.exe and standard input is attached to the keyboard, pressing a key that does not produce input (e.g. the Ctrl key) makes this program block and ignore data received on the socket. I managed to realize that this is because pressing any key signals the standard input handle and WaitForMultipleObjects() returns. As expected, control enters the if (which == 0) block and the call to ReadFile() blocks because there is no input available.
Is there a means to detect how much input is available on a Win32 stream? If so, I could use this to check if any input is available before calling ReadFile() to avoid blocking.
I know of a few solutions for specific types of streams (notably ClearCommError() for serial ports and ioctlsocket(socket,FIONBIO,&count) for sockets), but none that I know of works with the CONIN$ stream.
Use overlapped I/O. Then test the event attached to the I/O operation, instead of the handle.
For CONIN$ specifically, you might also look at the Console Input APIs, such as PeekConsoleInput and GetNumberOfConsoleInputEvents
But I really recommend using OVERLAPPED (background) reads wherever possible and not trying to treat WaitForMultipleObjects like select.
Since the console can't be overlapped in overlapped mode, your simplest options are to wait on the console handle and use ReadConsoleInput (then you have to process control sequences manually), or spawn a dedicated worker thread for synchronous ReadFile. If you choose a worker thread, you may want to then connect a pipe between that worker and the main I/O loop, using overlapped pipe reads.
Another possibility, which I've never tried, would be to wait on the console handle and use PeekConsoleInput to find out whether to call ReadFile or ReadConsoleInput. That way you should be able to get non-blocking along with the cooked terminal processing. OTOH, passing control sequences to ReadConsoleInput might inhibit the buffer-manipulation actions they were supposed to take.
If the two streams are processed independently, or nearly so, it may make more sense to start a thread for each one. Then you can use a blocking read from standard input.

Using event object in inter-process

I'm trying to use event object in win32 environment to synchronize two processes. Below are the simplified code of two programs.
// process1
int main()
{
HANDLE h = CreateEvent(NULL, FALSE, FALSE, TEXT("Hello"));
WaitForSingleObject(h, INFINITE);
// RunProcess(L"process2.exe", L"");
}
// process2
int main()
{
HANDLE h = OpenEvent(EVENT_MODIFY_STATE, FALSE, TEXT("Hello"));
SetEvent(h);
}
It's quite simple, and works well when two processes are launched independently. However it does not work when the process 1 launches process 2 as a child process (which is commented in the above code) - the SetEvent call fails. What is the reason and solution of this problem?
Your code needs to check and handle errors. Both CreateEvent and OpenEvent will return NULL if they fail, in that case you need to check the error using GetLastError.
Your calls to WaitForSingleObject and SetEvent should be checked per the MSDN docs as well.
The order in which you need to do things in the parent process is:
CreateEvent
Start child process
WaitForSingleObject.
Otherwise you will hit the problem called out by #Mark Tolonen.
It would also be best to have a timeout on your wait, to handle the case where the child process fails to start, exits unexpectedly, or hangs.
An alternative approach if you intend to use this parent/child relationship would be to allow inheritance of the event handle. Then the event does not need to be named, and nobody else can 'squat' on it in a DoS attack on your apps. You can pass the handle value to the child as a command-line parameter. You do this using the bInheritHandle field on the eventAttributes parameter to CreateEvent.
A Boolean value that specifies whether
the returned handle is inherited when
a new process is created. If this
member is TRUE, the new process
inherits the handle.
Are you sure? As written, if process1 creates process2 in the current location, it will never create process2 because it will wait forever for the event to be fired. Create process2 first, then wait for the event to be set.
You have a NULL security descriptor, which the documentation says cannot allow the handle to be inherited by children processes, specifically:
If this parameter is NULL, the handle cannot be inherited by child processes
Maybe you need to create a proper security descriptor?

Resources