Reputation: 3713
I'm trying to create a duplex named pipe using the windows API CreateNamedPipe
to use for IPC between my shell extension and my main desktop application.
There's a flag you can pass that function for Vista and above that prevents remote connections (PIPE_REJECT_REMOTE_CLIENTS
). From what I understand, that means the pipe is only connectable on the same machine. Does anybody know how get the same functionality in earlier versions of Windows? I've tried to create a SECURITY_ATTRIBUTES
object with the following code but I'm not entirely sure it's working correctly:
static bool GetLocalMachineOnlySecurityAttributes (SECURITY_ATTRIBUTES& sa)
{
PSID plocalsid = NULL;
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_LOCAL_SID_AUTHORITY;
if(!::AllocateAndInitializeSid (&SIDAuthWorld, 1, SECURITY_LOCAL_RID, 0, 0, 0, 0, 0, 0, 0, &plocalsid))
return false;
EXPLICIT_ACCESS ea = {0};
ea.grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;
ea.grfAccessMode = SET_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea.Trustee.ptstrName = reinterpret_cast<LPWSTR>(plocalsid);
PACL acl = NULL;
if(!::SetEntriesInAcl (1, &ea, NULL, &acl))
return false;
//PSECURITY_DESCRIPTOR sd = reinterpret_cast<PSECURITY_DESCRIPTOR>(::LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH));
static SECURITY_DESCRIPTOR sd = {0};
if(!::InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION))
return false;
if(!::SetSecurityDescriptorDacl(&sd, TRUE, acl, FALSE))
return false;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = FALSE;
return true;
}
If there's anyone out there that can tell me if I'm doing the right thing or somewhere I can look for a definitive explanation of SECURITY_ATTRIBUTES
, I'd be very grateful.
Upvotes: 2
Views: 2642
Reputation: 12135
You can indeed prevent remote connections by creating an appropriate Discretionary Access Control List (DACL) for the pipe.
Your code is trying but failing to do that, the first reason being at this line:
if(!::SetEntriesInAcl (1, &ea, NULL, &acl))
SetEntriesInAcl returns a DWORD code, not a BOOL: on success the returned code is ERROR_SUCCESS
, which has the value 0L, so your function always exits at this point, leaving the SECURITY_ATTRIBUTES structure empty.
Your code also leaks memory since it fails to deallocate buffers created by some of the APIs, including SetEntriesInAcl
. I suggest you use the example in MSDN as a guide to ensure you do all necessary clean up.
Turning more to the strategy of your code, you are currently trying to solve your problem using a single Access Control Entry (ACE) allowing all access for the Local security group. Because of the way DACLs work, that is not the right way to do it... you should instead deny remote access - i.e. black-list it - rather than trying to white-list local access. This is for at least two reasons:
So, your code needs to be amended so that you build a DACL containing the following ACEs:
The first of these is what will prevent remote access to the pipe, because all logon tokens created by remote access protocols, including the SMB-based remote named pipe protocol, automatically contain a group membership of the NETWORK USERS group (the well-known SID S-1-5-2). This deny ACE must come before the allow ACEs in the DACL.
You don't say which of your applications is the pipe server and which the client. Maybe it doesn't matter, if both run in the interactive user's session: in this case you may be able to use just one allow ACE which grants all access to the SID for the user's session.
Without more details of your security requirement it is difficult to be prescriptive as to how you should set up the server and client ACEs. Almost certainly, however, you will want to restrict the access right FILE_CREATE_PIPE_INSTANCE so that only the pipe server has it.
Upvotes: 4