Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

335 lines
10 KiB

#include "priv.h"
#pragma hdrstop
//---------------------------------------------------------------------------
// GetUserToken - Gets the current process's user token and returns
// it. It can later be free'd with LocalFree.
//
// REARCHITECT (reinerf) - stolen from shell32\securent.c, we should consolidate
// the code somewhere and export it
//---------------------------------------------------------------------------
PTOKEN_USER GetUserToken(HANDLE hUser)
{
PTOKEN_USER pUser;
DWORD dwSize = 64;
HANDLE hToClose = NULL;
if (hUser == NULL)
{
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hUser);
hToClose = hUser;
}
pUser = (PTOKEN_USER)LocalAlloc(LPTR, dwSize);
if (pUser)
{
DWORD dwNewSize;
BOOL fOk = GetTokenInformation(hUser, TokenUser, pUser, dwSize, &dwNewSize);
if (!fOk && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
{
LocalFree((HLOCAL)pUser);
pUser = (PTOKEN_USER)LocalAlloc(LPTR, dwNewSize);
if (pUser)
{
fOk = GetTokenInformation(hUser, TokenUser, pUser, dwNewSize, &dwNewSize);
}
}
if (!fOk)
{
LocalFree((HLOCAL)pUser);
pUser = NULL;
}
}
if (hToClose)
{
CloseHandle(hToClose);
}
return pUser;
}
//
// checks to see if the SHELL_USER_SID is all zeros (flag which means we should really use the users current sid)
//
__inline BOOL IsCurrentUserShellSID(PSHELL_USER_SID psusID)
{
SID_IDENTIFIER_AUTHORITY sidNULL = {0};
if ((psusID->dwUserGroupID == 0) &&
(psusID->dwUserID == 0) &&
memcmp(&psusID->sidAuthority, &sidNULL, sizeof(SID_IDENTIFIER_AUTHORITY)) == 0)
{
return TRUE;
}
return FALSE;
}
//
// Sets the specified ACE in the ACL to have dwAccessMask permissions.
//
__inline BOOL MakeACEInheritable(PACL pAcl, int iIndex, DWORD dwAccessMask)
{
ACE_HEADER* pAceHeader;
if (GetAce(pAcl, iIndex, (LPVOID*)&pAceHeader))
{
pAceHeader->AceFlags |= dwAccessMask;
return TRUE;
}
return FALSE;
}
//
// Helper function to generate a SECURITY_DESCRIPTOR with the specified rights
//
// OUT: psd - A pointer to a uninitialized SECURITY_DESCRIPTOR struct to be inited and filled in
// in by this function
//
// IN: PSHELL_USER_PERMISSION - An array of PSHELL_USER_PERMISSION pointers that specify what access to grant
// cUserPerm - The count of PSHELL_USER_PERMISSION pointers in the array above
//
//
STDAPI_(SECURITY_DESCRIPTOR*) GetShellSecurityDescriptor(PSHELL_USER_PERMISSION* apUserPerm, int cUserPerm)
{
BOOL fSuccess = TRUE; // assume success
SECURITY_DESCRIPTOR* pSD = NULL;
PSID* apSids = NULL;
int cAces = cUserPerm; // one ACE for each entry to start with
int iAceIndex = 0; // helps us keep count of how many ACE's we have added (count as we go)
PTOKEN_USER pUserToken = NULL;
DWORD cbSidLength = 0;
DWORD cbAcl;
PACL pAcl;
int i;
ASSERT(!IsBadReadPtr(apUserPerm, sizeof(PSHELL_USER_PERMISSION) * cUserPerm));
// healthy parameter checking
if (!apUserPerm || cUserPerm <= 0)
{
return NULL;
}
// first find out how many additional ACE's we are going to need
// because of inheritance
for (i = 0; i < cUserPerm; i++)
{
if (apUserPerm[i]->fInherit)
{
cAces++;
}
// also check to see if any of these are using susCurrentUser, in which case
// we want to get the users token now so we have it already
if ((pUserToken == NULL) && IsCurrentUserShellSID(&apUserPerm[i]->susID))
{
pUserToken = GetUserToken(NULL);
if (!pUserToken)
{
DWORD dwLastError = GetLastError();
TraceMsg(TF_WARNING, "Failed to get the users token. Error = %d", dwLastError);
fSuccess = FALSE;
goto cleanup;
}
}
}
// alloc the array to hold all the SID's
apSids = (PSID*)LocalAlloc(LPTR, cUserPerm * sizeof(PSID));
if (!apSids)
{
DWORD dwLastError = GetLastError();
TraceMsg(TF_WARNING, "Failed allocate memory for %i SID's. Error = %d", cUserPerm, dwLastError);
fSuccess = FALSE;
goto cleanup;
}
// initialize the SID's
for (i = 0; i < cUserPerm; i++)
{
DWORD cbSid;
// check for the special case of susCurrentUser
if (IsCurrentUserShellSID(&apUserPerm[i]->susID))
{
ASSERT(pUserToken);
apSids[i] = pUserToken->User.Sid;
}
else
{
SID_IDENTIFIER_AUTHORITY sidAuthority = apUserPerm[i]->susID.sidAuthority;
if (!AllocateAndInitializeSid(&sidAuthority,
(BYTE)(apUserPerm[i]->susID.dwUserID ? 2 : 1), // if dwUserID is nonzero, then there are two SubAuthorities
apUserPerm[i]->susID.dwUserGroupID,
apUserPerm[i]->susID.dwUserID,
0, 0, 0, 0, 0, 0, &apSids[i]))
{
DWORD dwLastError = GetLastError();
TraceMsg(TF_WARNING, "AllocateAndInitializeSid: Failed to initialze SID. Error = %d", cUserPerm, dwLastError);
fSuccess = FALSE;
goto cleanup;
}
}
// add up all the SID lengths for an easy ACL size computation later...
cbSid = GetLengthSid(apSids[i]);
cbSidLength += cbSid;
if (apUserPerm[i]->fInherit)
{
// if we have an inherit ACE as well, we need to add in the size of the SID again
cbSidLength += cbSid;
}
}
// calculate the size of the ACL we will be building (note: used sizeof(ACCESS_ALLOWED_ACE) b/c all ACE's are the same
// size (excepting wacko object ACE's which we dont deal with).
//
// this makes the size computation easy, since the size of the ACL will be the size of all the ACE's + the size of the SID's.
cbAcl = SIZEOF(ACL) + (cAces * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD))) + cbSidLength;
// HACKHACK (reinerf)
//
// we allocate enough space for the SECURITY_DESCRIPTOR and the ACL together and pass them both back to the
// caller to free. we need to to this since the SECURITY_DESCRIPTOR contains a pointer to the ACL
pSD = (SECURITY_DESCRIPTOR*)LocalAlloc(LPTR, SIZEOF(SECURITY_DESCRIPTOR) + cbAcl);
if (!pSD)
{
DWORD dwLastError = GetLastError();
TraceMsg(TF_WARNING, "Failed to allocate space for the SECURITY_DESCRIPTOR and the ACL. Error = %d", dwLastError);
fSuccess = FALSE;
goto cleanup;
}
// set the address of the ACL to right after the SECURITY_DESCRIPTOR in the
// block of memory we just allocated
pAcl = (PACL)(pSD + 1);
if (!InitializeAcl(pAcl, cbAcl, ACL_REVISION))
{
DWORD dwLastError = GetLastError();
TraceMsg(TF_WARNING, "InitializeAcl: Failed to init the ACL. Error = %d", dwLastError);
fSuccess = FALSE;
goto cleanup;
}
for (i = 0; i < cUserPerm; i++)
{
BOOL bRet;
// add the ACE's to the ACL
if (apUserPerm[i]->dwAccessType == ACCESS_ALLOWED_ACE_TYPE)
{
bRet = AddAccessAllowedAce(pAcl, ACL_REVISION, apUserPerm[i]->dwAccessMask, apSids[i]);
}
else
{
bRet = AddAccessDeniedAce(pAcl, ACL_REVISION, apUserPerm[i]->dwAccessMask, apSids[i]);
}
if (!bRet)
{
DWORD dwLastError = GetLastError();
TraceMsg(TF_WARNING, "AddAccessAllowed/DeniedAce: Failed to add SID. Error = %d", dwLastError);
fSuccess = FALSE;
goto cleanup;
}
// sucessfully added an ace
iAceIndex++;
ASSERT(iAceIndex <= cAces);
// if its an inherit ACL, also add another ACE for the inheritance part
if (apUserPerm[i]->fInherit)
{
// add the ACE's to the ACL
if (apUserPerm[i]->dwAccessType == ACCESS_ALLOWED_ACE_TYPE)
{
bRet = AddAccessAllowedAce(pAcl, ACL_REVISION, apUserPerm[i]->dwInheritAccessMask, apSids[i]);
}
else
{
bRet = AddAccessDeniedAce(pAcl, ACL_REVISION, apUserPerm[i]->dwInheritAccessMask, apSids[i]);
}
if (!bRet)
{
DWORD dwLastError = GetLastError();
TraceMsg(TF_WARNING, "AddAccessAllowed/DeniedAce: Failed to add SID. Error = %d", dwLastError);
fSuccess = FALSE;
goto cleanup;
}
if (!MakeACEInheritable(pAcl, iAceIndex, apUserPerm[i]->dwInheritMask))
{
DWORD dwLastError = GetLastError();
TraceMsg(TF_WARNING, "MakeACEInheritable: Failed to add SID. Error = %d", dwLastError);
fSuccess = FALSE;
goto cleanup;
}
// sucessfully added another ace
iAceIndex++;
ASSERT(iAceIndex <= cAces);
}
}
if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
{
DWORD dwLastError = GetLastError();
TraceMsg(TF_WARNING, "InitializeSecurityDescriptor: Failed to init the descriptor. Error = %d", dwLastError);
fSuccess = FALSE;
goto cleanup;
}
if (!SetSecurityDescriptorDacl(pSD, TRUE, pAcl, FALSE))
{
DWORD dwLastError = GetLastError();
TraceMsg(TF_WARNING, "SetSecurityDescriptorDacl: Failed to set the DACL. Error = %d", dwLastError);
fSuccess = FALSE;
goto cleanup;
}
cleanup:
if (apSids)
{
for (i = 0; i < cUserPerm; i++)
{
if (apSids[i])
{
// if this is one of the ones we allocated (eg not the users sid), free it
if (!pUserToken || (apSids[i] != pUserToken->User.Sid))
{
FreeSid(apSids[i]);
}
}
}
LocalFree(apSids);
}
if (pUserToken)
LocalFree(pUserToken);
if (!fSuccess && pSD)
{
LocalFree(pSD);
pSD = NULL;
}
return pSD;
}