Reputation: 21956
My app has 2 processes, one requires elevation, another doesn’t, but otherwise they’re running under the same user account on the same desktop.
I need to create a file (not on disk, other type of file) in elevated process which reads from the file, but have my non-elevated process write access to the file.
With nullptr
SECURITY_ATTRIBUTES, non-elevated process can’t open the file, CreateFile fails with access denied code. That’s expected and the SetSecurityDescriptorDacl
workaround similar to this answer works OK.
However, I don’t like that workaround. I don’t want to give write access to that file to Everyone. I only want to give access to the current user. This is kinda sensitive, the elevated reader process will run for hours and I don’t want Everyone to be able to write to that file.
How do I get/construct SECURITY_ATTRIBUTES that would be the default security for non-elevated process running on the same desktop?
Upvotes: 2
Views: 2338
Reputation: 33794
if we run elevated process (by admin user) - it have not elevated linked session. (and non elevated process have elevated linked session) you need:
TokenLinkedToken
TokenDefaultDacl
code for get default dacl for not elevated session:
ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? 0 : GetLastError();
}
ULONG GetNotElevatedDefaultDacl(PTOKEN_DEFAULT_DACL* DefaultDacl)
{
HANDLE hToken;
ULONG err = BOOL_TO_ERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken));
if (!err)
{
ULONG cb;
union {
TOKEN_LINKED_TOKEN tlt;
TOKEN_ELEVATION_TYPE tet;
};
err = BOOL_TO_ERROR(GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &cb));
if (!err)
{
if (tet == TokenElevationTypeFull)
{
err = BOOL_TO_ERROR(GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &cb));
}
else
{
err = ERROR_ELEVATION_REQUIRED;
}
}
CloseHandle(hToken);
if (!err)
{
union {
PTOKEN_DEFAULT_DACL p;
PVOID buf;
};
cb = 0x100;
do
{
if (buf = LocalAlloc(0, cb))
{
if (err = BOOL_TO_ERROR(GetTokenInformation(
tlt.LinkedToken, TokenDefaultDacl, buf, cb, &cb)))
{
LocalFree(buf);
}
else
{
*DefaultDacl = p;
}
}
else
{
err = GetLastError();
break;
}
} while (err == ERROR_INSUFFICIENT_BUFFER);
CloseHandle(tlt.LinkedToken);
}
}
return err;
}
and using it (this for any object which take SECURITY_ATTRIBUTES
on create)
PTOKEN_DEFAULT_DACL DefaultDacl;
ULONG err = GetNotElevatedDefaultDacl(&DefaultDacl);
SECURITY_DESCRIPTOR sd;
SECURITY_ATTRIBUTES sa = { sizeof(sa), &sd, FALSE };
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
if (!err)
{
SetSecurityDescriptorDacl(&sd, TRUE, DefaultDacl->DefaultDacl, FALSE);
}
HANDLE hObject = CreateMailslot(
L"\\\\?\\Global\\MailSlot\\12345678", 0, MAILSLOT_WAIT_FOREVER, &sa);
if (!err)
{
LocalFree(DefaultDacl);
}
if (hObject)
{
// CheckObjectSD(hObject);
CloseHandle(hObject);
}
if create object(mailslot in this case) from elevated process with default dacl - security DACL will be look like:
T FL AcessMsK Sid
A 00 001F01FF S-1-5-32-544 'Administrators'
A 00 001F01FF S-1-5-18 'SYSTEM'
A 00 001200A9 S-1-5-5-0-x 'LogonSessionId_0_x'
so all access for SYSTEM and Administrators and read+execute access for current Logon Session. as result not elevated process from same logon session have only read access.
if use explicit DACL from not elevated session - result:
T FL AcessMsK Sid
A 00 001F01FF S-1-5-21-a-b-c-d 'SomeUser'
A 00 001F01FF S-1-5-18 'SYSTEM'
A 00 001200A9 S-1-5-5-0-x 'LogonSessionId_0_x'
so all access for SYSTEM and SomeUser and read+execute access for current Logon Session.
note because elevated process have SomeUser as TokenUser he have all access for this object
for check object security descriptor we can use for example next code:
void CheckObjectSD(HANDLE hObject)
{
union {
PSECURITY_DESCRIPTOR psd;
PVOID buf;
};
ULONG cb = 0, rcb = 0x30;
volatile static UCHAR guz;
buf = alloca(guz);
PVOID stack = alloca(guz);
ULONG err;
do
{
if (cb < rcb)
{
cb = (ULONG)((ULONG_PTR)stack - (ULONG_PTR)(buf = alloca(rcb - cb)));
}
if (!(err = BOOL_TO_ERROR(GetKernelObjectSecurity(hObject,
DACL_SECURITY_INFORMATION|LABEL_SECURITY_INFORMATION|OWNER_SECURITY_INFORMATION, psd, cb, &rcb))))
{
PWSTR psz;
if (ConvertSecurityDescriptorToStringSecurityDescriptorW(psd, SDDL_REVISION,
DACL_SECURITY_INFORMATION|LABEL_SECURITY_INFORMATION|OWNER_SECURITY_INFORMATION, &psz, 0))
{
DbgPrint("%S\n", psz);
LocalFree(psz);
}
}
} while (err == ERROR_INSUFFICIENT_BUFFER);
}
if we have not admin user account, elevation was via another user account. elevated process in this case have no more linked session (if we try query linked token we got error - A specified logon session does not exist. It may already have been terminated. ). possible solution here(and in generic case too) in next:
for users process default DACL usually grant GENERIC_ALL
for System and UserSid and GENERIC_READ | GENERIC_EXECUTE
for logon session SID. we can query process token, get it default DACL, found LogonSession SID in DACL and change it access mask to GENERIC_ALL
. this can be done by next code:
ULONG GetDaclForLogonSession(HANDLE hToken, PTOKEN_DEFAULT_DACL* DefaultDacl)
{
ULONG err;
ULONG cb = 0x100;
union {
PTOKEN_DEFAULT_DACL p;
PVOID buf;
};
do
{
if (buf = LocalAlloc(0, cb))
{
if (!(err = BOOL_TO_ERROR(GetTokenInformation(hToken, TokenDefaultDacl, buf, cb, &cb))))
{
err = ERROR_NOT_FOUND;
if (PACL Dacl = p->DefaultDacl)
{
if (USHORT AceCount = Dacl->AceCount)
{
union {
PVOID pv;
PBYTE pb;
PACE_HEADER pah;
PACCESS_ALLOWED_ACE paaa;
};
pv = Dacl + 1;
static const SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY;
do
{
switch (pah->AceType)
{
case ACCESS_ALLOWED_ACE_TYPE:
PSID Sid = &paaa->SidStart;
if (*GetSidSubAuthorityCount(Sid) == SECURITY_LOGON_IDS_RID_COUNT &&
*GetSidSubAuthority(Sid, 0) == SECURITY_LOGON_IDS_RID &&
!memcmp(GetSidIdentifierAuthority(Sid), &NtAuth, sizeof(NtAuth)))
{
paaa->Mask = GENERIC_ALL;
*DefaultDacl = p;
return 0;
}
break;
}
pb += pah->AceSize;
} while (--AceCount);
}
}
}
LocalFree(buf);
}
else
{
return GetLastError();
}
} while (err == ERROR_INSUFFICIENT_BUFFER);
return err;
}
ULONG GetDaclForLogonSession(PTOKEN_DEFAULT_DACL* DefaultDacl)
{
HANDLE hToken;
ULONG err = BOOL_TO_ERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken));
if (!err)
{
err = GetDaclForLogonSession(hToken, DefaultDacl);
CloseHandle(hToken);
}
return err;
}
as result we got DACL
we grant all access to current logon session. usage the same - simply replace call from GetNotElevatedDefaultDacl
to GetDaclForLogonSession
Upvotes: 4