Reputation: 794
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.
Upvotes: 6
Views: 5297
Reputation: 36318
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.)
Upvotes: 4
Reputation: 33744
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
Upvotes: 0