Reputation: 2384
I have a service running as LocalSystem which creates a Processes in the logged on users' session. Then the service creates a named pipe to which the client connects to read and write. According to https://msdn.microsoft.com/en-us/library/aa365600%28v=vs.85%29.aspx the client can only read from the pipe (It's No Admin, not the Creator, neither LocalSystem ).
I created a security descriptor to grant the user read & write access. But this didn't work. So i tried giving read & write-access to the Everyone-Group. But this also does not work. The error code my client returns is always ACCESS_DENIED (5).
I would be glad to know what i am doing wrong.
EDIT: if i don't create a custom security descriptor and just open the pipe with GENERIC_READ
it works (but only reading).
EDIT2 I want to learn how to do this right. And i still want to be only the logged on user able to write. Not everyone (this was just for testing).
Service-Code (commented-out my code for getting the user sid):
PSID EveryoneSID = nullptr;
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
if(!AllocateAndInitializeSid(&SIDAuthWorld, 1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&EveryoneSID))
{
throw(std::runtime_error("Failed to initialize group sid: " + std::to_string(GetLastError())));
}
//TOKEN_USER* tokeninfo = nullptr;
//DWORD tokeninfolen = 0;
//DWORD outlen = 0;
//// Query token info size
//if(!GetTokenInformation(UserToken,
// TokenUser,
// tokeninfo,
// tokeninfolen,
// &outlen))
//{
// throw(std::runtime_error("Failed to obtain user token size: " + std::to_string(GetLastError())));
//}
//// Allocate enough space to hold token user information
//tokeninfo = (TOKEN_USER*) LocalAlloc(LPTR, outlen);
//tokeninfolen = outlen;
//// Get SID from user token
//if(!GetTokenInformation(UserToken,
// TokenUser,
// tokeninfo,
// tokeninfolen,
// &outlen))
//{
// throw(std::runtime_error("Failed to obtain user token info: " + std::to_string(GetLastError())));
//}
//auto UserSID = tokeninfo->User.Sid;
//LocalFree(tokeninfo);
SECURITY_ATTRIBUTES sa = {0};
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = FALSE;
// Set up ACE
EXPLICIT_ACCESS ace = {0};
ace.grfAccessMode = SET_ACCESS;
ace.grfAccessPermissions = PIPE_ACCESS_DUPLEX; // GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE
ace.grfInheritance = NO_INHERITANCE;
ace.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ace.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ace.Trustee.ptstrName = (LPTSTR) EveryoneSID;
PACL acl = nullptr;
if(ERROR_SUCCESS != SetEntriesInAcl(1, &ace, nullptr, &acl))
throw(std::runtime_error("Failed to set acl entries: " + std::to_string(GetLastError())));
// Create security descriptor.
auto sd = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION))
throw(std::runtime_error("Failed to initialize security descriptor: " + std::to_string(GetLastError())));
if(!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE))
throw(std::runtime_error("Failed to set DACL: " + std::to_string(GetLastError())));
// Set security descriptor in security attributes
sa.lpSecurityDescriptor = sd;
// Create a named pipe to which the user-session application
// connects.
auto pipe = CreateNamedPipe(LOCAL_PIPE_NAME,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS | PIPE_WAIT,
1,
256,
256,
NULL,
&sa);
if(pipe == INVALID_HANDLE_VALUE)
throw std::runtime_error("Failed to create named pipe");
LocalFree(acl);
LocalFree(sd);
User-Process-Code:
auto pipe = CreateFile(LOCAL_PIPE_NAME,
GENERIC_READ | GENERIC_WRITE, // read access
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
if (pipe == INVALID_HANDLE_VALUE)
throw(std::runtime_error("Failed to open local pipe: " + std::to_string(GetLastError())));
Upvotes: 6
Views: 3312
Reputation: 36308
This is the first problem:
ace.grfAccessPermissions = PIPE_ACCESS_DUPLEX;
The PIPE_ACCESS_DUPLEX
constant is only meant to be used as an argument to CreateNamedPipe(), it is not a valid access permission. (By coincidence, it is equal to FILE_READ_DATA|FILE_WRITE_DATA
but those access rights do not by themselves allow you to connect to a pipe.)
According to Named Pipe Security and Access Rights, the following access rights are assigned to the server end of a duplex pipe, implying that they are also sufficient to open the client end:
ace.grfAccessPermissions = FILE_GENERIC_READ | FILE_GENERIC_WRITE | SYNCHRONIZE;
However, FILE_GENERIC_WRITE
is too broad to grant to the client; in particular, it allows the client to create a new instance of the server end of the pipe. This is unlikely to be desirable. Instead, for a duplex pipe, you should use
ace.grfAccessPermissions = FILE_GENERIC_READ | FILE_WRITE_DATA;
Of course, the access you request when opening the client end must be consistent:
auto pipe = CreateFile(LOCAL_PIPE_NAME,
GENERIC_READ | FILE_WRITE_DATA, // read-write access
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
Details
Experimentally, on Windows 7 SP1 x64, in order to connect to a pipe (even if you request no access in the call to CreateFile) you must have the READ_ATTRIBUTES
and the SYNCHRONIZE
rights. Note that the FILE_GENERIC_READ
constant incorporates both of these.
To read data from the pipe you must have (and request) FILE_READ_DATA
. (This is incorporated in FILE_GENERIC_READ
.)
To write data to the pipe you must have (and request) FILE_WRITE_DATA
.
Upvotes: 4
Reputation: 595319
There is a much simpler option - set the security descriptor;s dacl to NULL instead:
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
SECURITY_ATTRIBUTES sa = {0};
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = FALSE;
Upvotes: 0