Hi, I'm using named pipes to communicate between processes. The processes run at different integrity levels - LOW (ie toolbar) + MEDIUM (tray icon) + HIGH + SYSTEM (service). I'm defining a LOW integrity SACL when creating the named pipe. Things work great when logged in as an Administrator. When logged in as a Standard User however, only processes of identical integrity levels can communicate with one another because CreateFile fails to open an existing pipe with erorr ACCESS DENIED (eg: MED to MED works, HIGH to HIGH works, ...) I need both READ and WRITE access to the pipe. What security attribute do I need to pass to CreateNamedPipe() to resolve this? Do I have to call SetSecurityInfo() after calling CreateNamedPipe() to resolve this somehow? I've spent a full day searching and trying various DACLs and SACLs and i've hit a brick wall. I'm crossing my fingers that someone can point me in the right direction. The problem is mentioned in this thread but no solution is offered: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/ce75cdbd-a7c9-422f-b8e6-49ea4b4aa97f Relevant Code (error checking removed): ========= LPCWSTR LOW_INTEGRITY_SDDL_SACL_W = L"S:(ML;;NW;;;LW)"
;
PSECURITY_DESCRIPTOR securitydescriptor;
ConvertStringSecurityDescriptorToSecurityDescriptorW(LOW_INTEGRITY_SDDL_SACL_W,SDDL_REVISION_1,&securitydescriptor,NULL);
sa.nLength = sizeof
(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = securitydescriptor;
sa.bInheritHandle = TRUE;
HANDLE pipe = CreateNamedPipe(pipename, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
PIPESERVER_OUTPUTBUFFERSIZE, // output buffer size
PIPESERVER_INPUTBUFFERSIZE, // input buffer size
0, // client time-out
&sa); // security attribute
<...create another LOW integrity psa...>
HANDLE pipe = CreateFile(pipename, // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
psa, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
Thanks, Ignac
11 Solutions Found
Solution 1 I had a similar problem in .NET. I assume you know C# since the link you provided was .NET. This allowed Users to contact a named pipe server running in a service:
pipeSecurity = new PipeSecurity();
var usersPipeAccessRule = new PipeAccessRule(
new SecurityIdentifier( WellKnownSidType.BuiltinUsersSid, null ),
PipeAccessRights.ReadWrite | PipeAccessRights.Synchronize,
AccessControlType.Allow
);
pipeSecurity.AddAccessRule(usersPipeAccessRule)
waitingPipe = new NamedPipeServerStream(
PipeName,
PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Message,
PipeOptions.Asynchronous,
ServicePipeProcessor.MaxBufferSize,
ServicePipeProcessor.MaxBufferSize,
pipeSecurity); I would assume you would want to use functions like CreateWellKnownSid and SetSecurityDescriptotDacl to do the same thing.
Solution 2 Thanks for your reply Eric! Based on your reply, I had some success using a similar technique to the one you provided - I'm actually working in C++. Everything works great, except for one critical thing: communication with LOW integrity processes is not working. Before, I would explicitly specify a LOW integrity SACL during pipe creation (see orignal code paste above). Now, I'm instead setting a DACL to allow "EVERYONE' access during pipe creation (see code below). But, I can't seem to get it to recognize both the LOW integrity SACL and the EVERYONE DACL. Here's what I've tried: 1) Call CreateNamedPipe() with a LOW integrity SACL and then try to add the Everyone DACL to the returned pipe handle 2) Call CreateNamedPipe() with the Everyone DACL and then try to add the LOW integrity SACL to the returned pipe handle Neither seem to work...it seems that the security attribute MUST be specified upfront when the named pipe is created. Would you know how I can augment the code below to specify a LOW integrity SACL? - I've tried defining another EXPLICIT_ACCESS structure using SET_AUDIT_SUCCESS without luck - I've also tried using ConvertStringSecurityDescripterToSecurityDescriptorW() rather than simply doing a LocalAlloc of SECURITY_DESCRIPTOR, but that doesn't seem to work either - I'm sure I'm doing something wrong: this SACL/DACL stuff is mind boggling (at least for me). Again, I really appreciate your reply Eric, Ignac, SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
PSID everyone_sid = NULL;
AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyone_sid);
EXPLICIT_ACCESS ea;
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
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 = (LPWSTR)everyone_sid;
PACL acl = NULL;
SetEntriesInAcl(1, &ea, NULL, &acl);
// HOW DO I ADD A LOW INTEGRITY SACL HERE THAT WILL NOT BE IGNORED ?
PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH);
InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = sd;
sa.bInheritHandle = FALSE;
CreateNamedPipe(...,&sa); Solution 3 Congratulations on the progress. It seems like it would work for low integrity since it's working for medium. Have you tried this using SECURITY_NULL_SID_AUTHORITY for the SID_IDENTIFIER_AUTHORITY or adding a second entry in the ACL? Solution 4 Here's what I have tried: hi Eric, - SECURITY_NULL_SID_AUTHORITY does not work (even non-low processes can't communicate) - tried merging in an SACL entry in the ACL using EXPLICIT_ACCESS and an access mode of SET_AUDIT_SUCCESS, but it doesn't seem to have any effect (or i'm doing something wrong). - tried getting SECURITY_NULL_SID_AUTHORITY privilege, calling CreateNamedPipe with ACCESS_SYSTEM_SECURITY , and then adding a low integrity SACL after the call to CreateNamedPipe in two ways: a) Using SetSecurityInfo() => fails with ACCESS_DENIED b) Using SetEntriesInAcl() => no errors, but no effect Also, if I grab ACCESS_SYSTEM_SECURITY it makes things wors. Without it communication is only broken from LOW integrity to higher integriy but the reverse works. With it, communication is broken in both directions. Thanks, Ignac Solution 5 I'm not sure if it would be any different, but try something like this:
bool AddWellKnowSid(WELL_KNOWN_SID_TYPE WellKnownSidType, PACL pAcl)
{
bool returnValue = false;
DWORD sidSize = SECURITY_MAX_SID_SIZE;
PSID sid = LocalAlloc(LMEM_FIXED, sidSize);
if(sid != NULL)
{
if(CreateWellKnownSid(WellKnownSidType, NULL, sid, &sidSize))
{
if (AddAccessAllowedAce(pAcl, ACL_REVISION, 0xFFFF, sid))
{
returnValue = true;
}
}
LocalFree(sid);
}
return returnValue;
}
void Create()
{
DWORD cbAcl = 256;
PACL pAcl = (ACL*)LocalAlloc(LPTR, cbAcl);
if (pAcl == NULL)
return;
if (InitializeAcl(pAcl, cbAcl, ACL_REVISION))
{
if (AddWellKnowSid(WinAnonymousSid, pAcl) &&
AddWellKnowSid(WinWorldSid, pAcl))
{
PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
{
if (SetSecurityDescriptorDacl(pSD, TRUE, pAcl, FALSE))
{
SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), pSD, FALSE};
HANDLE hPipe = CreateNamedPipe(_T("\\\\.\\pipe\\namedpipe"),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, 512, 512, 0, &sa);
if (hPipe != INVALID_HANDLE_VALUE)
{
CloseHandle(hPipe);
}
}
}
}
}
LocalFree(pAcl);
}
Solution 6 hi Eric, Thanks for the code. I tried it but it didn't work for me - a high integrity process could not talk to a medium integrity process if standard user. The DACL code I pasted earlier seems to solve that part for me...it's the low integrity SACL that I'm having issues with. Here's the code I try to use to set the SACL after creating the pipe handle....I cant figure out why SetSecurityInfo is returning ACCESS_DENIED. 1. Use AdjustTokenPrivileges() to grab SE_SECURITY_NAME 2. Specify the ACCESS_SYSTEM_SECURITY flag when calling CreateNamedPipe() 3. Call the below function as follows: SetObjectToLowIntegrity(pipe,SE_KERNEL_OBJECT); bool SetObjectToLowIntegrity(HANDLE hObject,SE_OBJECT_TYPE type) { LPCWSTR LOW_INTEGRITY_SDDL_SACL_W = L"S:(ML;;NW;;;LW)"; bool bRet = false; DWORD dwErr = ERROR_SUCCESS; PACL pSacl = NULL; BOOL fSaclPresent = FALSE; BOOL fSaclDefaulted = FALSE; PSECURITY_DESCRIPTOR pSD = NULL; if (ConvertStringSecurityDescriptorToSecurityDescriptorW(LOW_INTEGRITY_SDDL_SACL_W,SDDL_REVISION_1,&pSD,NULL)) { if (GetSecurityDescriptorSacl(pSD,&fSaclPresent,&pSacl,&fSaclDefaulted)) { dwErr = SetSecurityInfo(hObject,type,LABEL_SECURITY_INFORMATION,NULL,NULL,NULL,pSacl); // --------> FAILS WITH ACCESS_DENIED bRet = (ERROR_SUCCESS == dwErr); } LocalFree ( pSD ); } return bRet; } Solution 7 hi Eric, Thanks for your suggestions. I FINALLY got it working. Man this stuff is as clear as mud. It seems that there is no way to modify the permissions of a named pipe after it has been created. I tried and it always either simply doesn't work or returns ACCESS_DENIED. So, I had to specify both the DACL and SACL up front when creating the pipe. The DACL code was pasted above in an earlier thread and the SACL can be created as follows: 1. Create a new using InitializeAcl() 2. Initialize a new SID specifying 'SECURITY_MANDATORY_LOW_RID' 3. Call AddMandatoryAce() to add the SID to the ACL 4. Call SetSecurityDescriptorSacl() to add the ACL to the security descriptor Cheers, Ignac Solution 8 Congratulations!! That's a pretty good puzzle you figured out. Solution 9 Do you have full sample code you can post. I am trying to accomplish this same thing and even with the code you have pasted here so far, I am having no luck. Thanks Solution 10 Hello You can check out the full named pipe sample in Microsoft All-In-One Code Framework: http://1code.codeplex.com See the C# version: CSNamedPipeServer, CSNamedPipeClient C++ version: CppNamedPipeServer, CppNamedPipeClient VB.NET version: VBNamedPipeServer, VBNamedPipeClient These samples have been fully tested in various Windows environments (e.g. high integrity level env, x64, pre-Vista env, etc) If you have any feedback of the samples, please feel free to email to codefxf@microsoft.com Solution 11 After 2 days of working on it, I've confirmed that Ignac approach does work correctly. As he suggested, it seems to be impossible to set the SACL on the pipe after it has been created. I found this out when I tried to set PipeAccessRule and PipeAuditRule on a Pipe using the NamedPipeServerStream class in System.IO.Pipes. I'm assuming that behind the scenes the NamedPipeServerStream constructor creates the pipe first and
then tries setting the SACL/DACL. As a result, the constructor fails telling you that you don't have sufficient privileges to perform the action (or something similar) I developed Ignac's solution using C#, but then once I'd successfully created the pipe with the correct security attributes, I passed the pipe handle into the NamedPipeServerStream constructor. This seems to be working ok and now I can use NamedPipeServerStream
to work with a pipe that can be accessed by Everyone and by low integrity processes as well. If someone has questions on this issue I'd be happy to help guide you. David K | |