mirror of https://github.com/tongzx/nt5src
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.
4392 lines
102 KiB
4392 lines
102 KiB
/*++
|
|
|
|
Copyright (C) 1997-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
WINNTSEC.CPP
|
|
|
|
Abstract:
|
|
|
|
Generic wrapper classes for NT security objects.
|
|
|
|
Documention on class members is in WINNTSEC.CPP. Inline members
|
|
are commented in this file.
|
|
|
|
History:
|
|
|
|
raymcc 08-Jul-97 Created.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#include <stdio.h>
|
|
#include <io.h>
|
|
#include <errno.h>
|
|
#include <winntsec.h>
|
|
#include <tchar.h>
|
|
|
|
#include <genutils.h>
|
|
#include "arena.h"
|
|
#include "reg.h"
|
|
#include "wbemutil.h"
|
|
#include "arrtempl.h"
|
|
#include <cominit.h>
|
|
extern "C"
|
|
{
|
|
#include <lmaccess.h>
|
|
#include <lmapibuf.h>
|
|
#include <lmerr.h>
|
|
};
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSid::GetSize
|
|
//
|
|
// Returns the size of the SID in bytes.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
DWORD CNtSid::GetSize()
|
|
{
|
|
if (m_pSid == 0 || !IsValidSid(m_pSid))
|
|
return 0;
|
|
|
|
return GetLengthSid(m_pSid);
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSid Copy Constructor
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSid::CNtSid(CNtSid &Src)
|
|
{
|
|
m_pSid = 0;
|
|
m_dwStatus = 0;
|
|
m_pMachine = 0;
|
|
*this = Src;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSid Copy Constructor
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSid::CNtSid(SidType st)
|
|
{
|
|
m_pSid = 0;
|
|
m_dwStatus = InternalError;
|
|
m_pMachine = 0;
|
|
if(st == CURRENT_USER ||st == CURRENT_THREAD)
|
|
{
|
|
HANDLE hToken;
|
|
if(st == CURRENT_USER)
|
|
{
|
|
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
|
|
return;
|
|
}
|
|
|
|
// Get the user sid
|
|
// ================
|
|
|
|
TOKEN_USER tu;
|
|
DWORD dwLen = 0;
|
|
GetTokenInformation(hToken, TokenUser, &tu, sizeof(tu), &dwLen);
|
|
|
|
if(dwLen == 0)
|
|
{
|
|
CloseHandle(hToken);
|
|
return;
|
|
}
|
|
|
|
BYTE* pTemp = new BYTE[dwLen];
|
|
if (!pTemp)
|
|
{
|
|
CloseHandle(hToken);
|
|
return;
|
|
}
|
|
|
|
DWORD dwRealLen = dwLen;
|
|
if(!GetTokenInformation(hToken, TokenUser, pTemp, dwRealLen, &dwLen))
|
|
{
|
|
CloseHandle(hToken);
|
|
delete [] pTemp;
|
|
return;
|
|
}
|
|
|
|
CloseHandle(hToken);
|
|
|
|
// Make a copy of the SID
|
|
// ======================
|
|
|
|
PSID pSid = ((TOKEN_USER*)pTemp)->User.Sid;
|
|
DWORD dwSidLen = GetLengthSid(pSid);
|
|
m_pSid = new BYTE[dwSidLen];
|
|
if (m_pSid)
|
|
CopySid(dwSidLen, m_pSid, pSid);
|
|
delete [] pTemp;
|
|
m_dwStatus = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSid::CopyTo
|
|
//
|
|
// An unchecked copy of the internal SID to the destination pointer.
|
|
//
|
|
// Parameters:
|
|
// <pDestination> points to the buffer to which to copy the SID. The
|
|
// buffer must be large enough to hold the SID.
|
|
//
|
|
// Return value:
|
|
// TRUE on success, FALSE on failure.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
BOOL CNtSid::CopyTo(PSID pDestination)
|
|
{
|
|
if (m_pSid == 0 || m_dwStatus != NoError)
|
|
return FALSE;
|
|
|
|
DWORD dwLen = GetLengthSid(m_pSid);
|
|
memcpy(pDestination, m_pSid, dwLen);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSid assignment operator
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSid & CNtSid::operator =(CNtSid &Src)
|
|
{
|
|
if (m_pMachine != 0)
|
|
{
|
|
delete [] m_pMachine;
|
|
m_pMachine = 0;
|
|
}
|
|
|
|
if (m_pSid != 0)
|
|
{
|
|
delete [] m_pSid;
|
|
m_pSid = 0;
|
|
}
|
|
|
|
if (Src.m_pSid == 0)
|
|
{
|
|
m_pSid = 0;
|
|
m_dwStatus = NullSid;
|
|
return *this;
|
|
}
|
|
|
|
if (!IsValidSid(Src.m_pSid))
|
|
{
|
|
m_pSid = 0;
|
|
m_dwStatus = InvalidSid;
|
|
return *this;
|
|
}
|
|
|
|
|
|
// If here, the source has a real SID.
|
|
// ===================================
|
|
|
|
DWORD dwLen = GetLengthSid(Src.m_pSid);
|
|
|
|
m_pSid = (PSID) new BYTE [dwLen];
|
|
if (!m_pSid)
|
|
{
|
|
m_dwStatus = InternalError;
|
|
return *this;
|
|
}
|
|
|
|
ZeroMemory(m_pSid, dwLen);
|
|
|
|
if (!CopySid(dwLen, m_pSid, Src.m_pSid))
|
|
{
|
|
delete [] m_pSid;
|
|
m_pSid = NULL;
|
|
m_dwStatus = InternalError;
|
|
return *this;
|
|
}
|
|
|
|
if (Src.m_pMachine)
|
|
{
|
|
m_pMachine = new wchar_t[wcslen(Src.m_pMachine) + 1];
|
|
if (!m_pMachine)
|
|
{
|
|
delete [] m_pSid;
|
|
m_pSid = NULL;
|
|
m_dwStatus = InternalError;
|
|
return *this;
|
|
}
|
|
|
|
wcscpy(m_pMachine, Src.m_pMachine);
|
|
}
|
|
|
|
m_dwStatus = NoError;
|
|
return *this;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSid comparison operator
|
|
//
|
|
//***************************************************************************
|
|
int CNtSid::operator ==(CNtSid &Comparand)
|
|
{
|
|
if (m_pSid == 0 && Comparand.m_pSid == 0)
|
|
return 1;
|
|
if (m_pSid == 0 || Comparand.m_pSid == 0)
|
|
return 0;
|
|
|
|
return EqualSid(m_pSid, Comparand.m_pSid);
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSid::CNtSid
|
|
//
|
|
// Constructor which builds a SID directly from a user or group name.
|
|
// If the machine is available, then its name can be used to help
|
|
// distinguish the same name in different SAM databases (domains, etc).
|
|
//
|
|
// Parameters:
|
|
//
|
|
// <pUser> The desired user or group.
|
|
//
|
|
// <pMachine> Points to a machine name with or without backslashes,
|
|
// or else is NULL, in which case the current machine, domain,
|
|
// and trusted domains are searched for a match.
|
|
//
|
|
// After construction, call GetStatus() to determine if the constructor
|
|
// succeeded. NoError is expected.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSid::CNtSid(
|
|
LPWSTR pUser,
|
|
LPWSTR pMachine
|
|
)
|
|
{
|
|
DWORD dwRequired = 0;
|
|
DWORD dwDomRequired = 0;
|
|
LPWSTR pszDomain = NULL;
|
|
m_pSid = 0;
|
|
m_pMachine = 0;
|
|
|
|
if (pMachine)
|
|
{
|
|
m_pMachine = new wchar_t[wcslen(pMachine) + 1];
|
|
if (!m_pMachine)
|
|
{
|
|
m_dwStatus = Failed;
|
|
return;
|
|
}
|
|
|
|
wcscpy(m_pMachine, pMachine);
|
|
}
|
|
|
|
BOOL bRes = LookupAccountNameW(
|
|
m_pMachine,
|
|
pUser,
|
|
m_pSid,
|
|
&dwRequired,
|
|
pszDomain,
|
|
&dwDomRequired,
|
|
&m_snu
|
|
);
|
|
|
|
DWORD dwLastErr = GetLastError();
|
|
|
|
if (dwLastErr != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
m_pSid = 0;
|
|
if (dwLastErr == ERROR_ACCESS_DENIED)
|
|
m_dwStatus = AccessDenied;
|
|
else
|
|
m_dwStatus = InvalidSid;
|
|
return;
|
|
}
|
|
|
|
m_pSid = (PSID) new BYTE [dwRequired];
|
|
if (!m_pSid)
|
|
{
|
|
m_dwStatus = Failed;
|
|
return;
|
|
}
|
|
|
|
ZeroMemory(m_pSid, dwRequired);
|
|
pszDomain = new wchar_t[dwDomRequired + 1];
|
|
if (!pszDomain)
|
|
{
|
|
delete [] m_pSid;
|
|
m_pSid = 0;
|
|
m_dwStatus = Failed;
|
|
return;
|
|
}
|
|
|
|
bRes = LookupAccountNameW(
|
|
pMachine,
|
|
pUser,
|
|
m_pSid,
|
|
&dwRequired,
|
|
pszDomain,
|
|
&dwDomRequired,
|
|
&m_snu
|
|
);
|
|
|
|
if (!bRes || !IsValidSid(m_pSid))
|
|
{
|
|
delete [] m_pSid;
|
|
delete [] pszDomain;
|
|
m_pSid = 0;
|
|
m_dwStatus = InvalidSid;
|
|
return;
|
|
}
|
|
|
|
delete [] pszDomain; // We never really needed this
|
|
m_dwStatus = NoError;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSid::CNtSid
|
|
//
|
|
// Constructs a CNtSid object directly from an NT SID. The SID is copied,
|
|
// so the caller retains ownership.
|
|
//
|
|
// Parameters:
|
|
// <pSrc> The source SID upon which to base the object.
|
|
//
|
|
// Call GetStatus() after construction to ensure the object was
|
|
// constructed correctly. NoError is expected.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSid::CNtSid(PSID pSrc)
|
|
{
|
|
m_pMachine = 0;
|
|
m_pSid = 0;
|
|
m_dwStatus = NoError;
|
|
|
|
if (!IsValidSid(pSrc))
|
|
{
|
|
m_dwStatus = InvalidSid;
|
|
return;
|
|
}
|
|
|
|
DWORD dwLen = GetLengthSid(pSrc);
|
|
|
|
m_pSid = (PSID) new BYTE [dwLen];
|
|
// Check for mem. alloc. failure
|
|
// NT RAID#: 158600 [marioh]
|
|
if ( m_pSid == NULL )
|
|
{
|
|
m_dwStatus = Failed;
|
|
return;
|
|
}
|
|
|
|
ZeroMemory(m_pSid, dwLen);
|
|
|
|
if (!CopySid(dwLen, m_pSid, pSrc))
|
|
{
|
|
delete [] m_pSid;
|
|
m_dwStatus = InternalError;
|
|
return;
|
|
}
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSid::GetInfo
|
|
//
|
|
// Returns information about the SID.
|
|
//
|
|
// Parameters:
|
|
// <pRetAccount> Receives a UNICODE string containing the account
|
|
// name (user or group). The caller must use operator
|
|
// delete to free the memory. This can be NULL if
|
|
// this information is not required.
|
|
// <pRetDomain> Returns a UNICODE string containing the domain
|
|
// name in which the account resides. The caller must
|
|
// use operator delete to free the memory. This can be
|
|
// NULL if this information is not required.
|
|
// <pdwUse> Points to a DWORD to receive information about the name.
|
|
// Possible return values are defined under SID_NAME_USE
|
|
// in NT SDK documentation. Examples are
|
|
// SidTypeUser, SidTypeGroup, etc. See CNtSid::Dump()
|
|
// for an example.
|
|
//
|
|
// Return values:
|
|
// NoError, InvalidSid, Failed
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
int CNtSid::GetInfo(
|
|
LPWSTR *pRetAccount, // Account, use operator delete
|
|
LPWSTR *pRetDomain, // Domain, use operator delete
|
|
DWORD *pdwUse // See SID_NAME_USE for values
|
|
)
|
|
{
|
|
if (pRetAccount)
|
|
*pRetAccount = 0;
|
|
if (pRetDomain)
|
|
*pRetDomain = 0;
|
|
if (pdwUse)
|
|
*pdwUse = 0;
|
|
|
|
if (!m_pSid || !IsValidSid(m_pSid))
|
|
return InvalidSid;
|
|
|
|
DWORD dwNameLen = 0;
|
|
DWORD dwDomainLen = 0;
|
|
LPWSTR pUser = 0;
|
|
LPWSTR pDomain = 0;
|
|
SID_NAME_USE Use;
|
|
|
|
|
|
// Do the first lookup to get the buffer sizes required.
|
|
// =====================================================
|
|
|
|
BOOL bRes = LookupAccountSidW(
|
|
m_pMachine,
|
|
m_pSid,
|
|
pUser,
|
|
&dwNameLen,
|
|
pDomain,
|
|
&dwDomainLen,
|
|
&Use
|
|
);
|
|
|
|
DWORD dwLastErr = GetLastError();
|
|
|
|
if (dwLastErr != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
return Failed;
|
|
}
|
|
|
|
// Allocate the required buffers and look them up again.
|
|
// =====================================================
|
|
|
|
pUser = new wchar_t[dwNameLen + 1];
|
|
if (!pUser)
|
|
return Failed;
|
|
|
|
pDomain = new wchar_t[dwDomainLen + 1];
|
|
if (!pDomain)
|
|
{
|
|
delete pUser;
|
|
return Failed;
|
|
}
|
|
|
|
bRes = LookupAccountSidW(
|
|
m_pMachine,
|
|
m_pSid,
|
|
pUser,
|
|
&dwNameLen,
|
|
pDomain,
|
|
&dwDomainLen,
|
|
&Use
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
delete [] pUser;
|
|
delete [] pDomain;
|
|
return Failed;
|
|
}
|
|
|
|
if (pRetAccount)
|
|
*pRetAccount = pUser;
|
|
else
|
|
delete [] pUser;
|
|
if (pRetDomain)
|
|
*pRetDomain = pDomain;
|
|
else
|
|
delete [] pDomain;
|
|
if (pdwUse)
|
|
*pdwUse = Use;
|
|
|
|
return NoError;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSid::Dump
|
|
//
|
|
// Dumps the SID to the console outuput for debugging.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
void CNtSid::Dump()
|
|
{
|
|
LPWSTR pUser, pDomain;
|
|
DWORD dwUse;
|
|
|
|
printf("---SID DUMP---\n");
|
|
|
|
if (m_pSid == 0)
|
|
{
|
|
printf("<NULL>\n");
|
|
return;
|
|
}
|
|
|
|
if (!IsValidSid(m_pSid))
|
|
{
|
|
printf("<Invalid Sid>\n");
|
|
return;
|
|
}
|
|
|
|
int nRes = GetInfo(&pUser, &pDomain, &dwUse);
|
|
|
|
if (nRes != NoError)
|
|
return;
|
|
|
|
// Print out SID in SID-style notation.
|
|
// ====================================
|
|
|
|
// Print out human-readable info.
|
|
// ===============================
|
|
|
|
printf("User = %S Domain = %S Type = ", pUser, pDomain);
|
|
|
|
delete [] pUser;
|
|
delete [] pDomain;
|
|
|
|
switch (dwUse)
|
|
{
|
|
case SidTypeUser:
|
|
printf("SidTypeUser\n");
|
|
break;
|
|
|
|
case SidTypeGroup:
|
|
printf("SidTypeGroup\n");
|
|
break;
|
|
|
|
case SidTypeDomain:
|
|
printf("SidTypeDomain\n");
|
|
break;
|
|
|
|
case SidTypeAlias:
|
|
printf("SidTypeAlias\n");
|
|
break;
|
|
|
|
case SidTypeWellKnownGroup:
|
|
printf("SidTypeWellKnownGroup\n");
|
|
break;
|
|
|
|
case SidTypeDeletedAccount:
|
|
printf("SidTypeDeletedAccount\n");
|
|
break;
|
|
|
|
case SidTypeUnknown:
|
|
printf("SidTypeUnknown\n");
|
|
break;
|
|
|
|
case SidTypeInvalid:
|
|
default:
|
|
printf("SidTypeInvalid\n");
|
|
}
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSid destructor
|
|
//
|
|
//***************************************************************************
|
|
|
|
CNtSid::~CNtSid()
|
|
{
|
|
if (m_pSid)
|
|
delete [] m_pSid;
|
|
if (m_pMachine)
|
|
delete [] m_pMachine;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSid::GetTextSid
|
|
//
|
|
// Converts the sid to text form. The caller should passin a 130 character
|
|
// buffer.
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL CNtSid::GetTextSid(LPTSTR pszSidText, LPDWORD dwBufferLen)
|
|
{
|
|
PSID_IDENTIFIER_AUTHORITY psia;
|
|
DWORD dwSubAuthorities = 0;
|
|
DWORD dwSidRev=SID_REVISION;
|
|
DWORD dwCounter = 0;
|
|
DWORD dwSidSize = 0;
|
|
|
|
// test if Sid is valid
|
|
|
|
if(m_pSid == 0 || !IsValidSid(m_pSid))
|
|
return FALSE;
|
|
|
|
// obtain SidIdentifierAuthority
|
|
|
|
psia=GetSidIdentifierAuthority(m_pSid);
|
|
|
|
// obtain sidsubauthority count
|
|
|
|
PUCHAR p = GetSidSubAuthorityCount(m_pSid);
|
|
dwSubAuthorities = *p;
|
|
|
|
// compute buffer length
|
|
// S-SID_REVISION- + identifierauthority- + subauthorities- + NULL
|
|
|
|
dwSidSize=(15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);
|
|
|
|
// check provided buffer length. If not large enough, indicate proper size.
|
|
|
|
if (*dwBufferLen < dwSidSize)
|
|
{
|
|
*dwBufferLen = dwSidSize;
|
|
return FALSE;
|
|
}
|
|
|
|
// prepare S-SID_REVISION-
|
|
|
|
dwSidSize=wsprintf(pszSidText, TEXT("S-%lu-"), dwSidRev );
|
|
|
|
// prepare SidIdentifierAuthority
|
|
|
|
if ( (psia->Value[0] != 0) || (psia->Value[1] != 0) )
|
|
{
|
|
dwSidSize+=wsprintf(pszSidText + lstrlen(pszSidText),
|
|
TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
|
|
(USHORT)psia->Value[0],
|
|
(USHORT)psia->Value[1],
|
|
(USHORT)psia->Value[2],
|
|
(USHORT)psia->Value[3],
|
|
(USHORT)psia->Value[4],
|
|
(USHORT)psia->Value[5]);
|
|
}
|
|
else
|
|
{
|
|
dwSidSize+=wsprintf(pszSidText + lstrlen(pszSidText),
|
|
TEXT("%lu"),
|
|
(ULONG)(psia->Value[5] ) +
|
|
(ULONG)(psia->Value[4] << 8) +
|
|
(ULONG)(psia->Value[3] << 16) +
|
|
(ULONG)(psia->Value[2] << 24) );
|
|
}
|
|
|
|
// loop through SidSubAuthorities
|
|
|
|
for (dwCounter=0 ; dwCounter < dwSubAuthorities ; dwCounter++)
|
|
{
|
|
dwSidSize+=wsprintf(pszSidText + dwSidSize, TEXT("-%lu"),
|
|
*GetSidSubAuthority(m_pSid, dwCounter) );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::CNtAce
|
|
//
|
|
// Constructor which directly builds the ACE based on a user, access mask
|
|
// and flags without a need to build an explicit SID.
|
|
//
|
|
//
|
|
// Parameters:
|
|
// <AccessMask> A WINNT ACCESS_MASK which specifies the permissions
|
|
// the user should have to the object being secured.
|
|
// See ACCESS_MASK in NT SDK documentation.
|
|
// <dwAceType> One of the following:
|
|
// ACCESS_ALLOWED_ACE_TYPE
|
|
// ACCESS_DENIED_ACE_TYPE
|
|
// ACCESS_AUDIT_ACE_TYPE
|
|
// See ACE_HEADER in NT SDK documentation.
|
|
// <dwAceFlags> Of of the ACE propation flags. See ACE_HEADER
|
|
// in NT SDK documentation for legal values.
|
|
// <sid> CNtSid specifying the user or group for which the ACE is being
|
|
// created.
|
|
//
|
|
// After construction, call GetStatus() to verify that the ACE
|
|
// is valid. NoError is expected.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAce::CNtAce(
|
|
ACCESS_MASK AccessMask,
|
|
DWORD dwAceType,
|
|
DWORD dwAceFlags,
|
|
CNtSid & Sid
|
|
)
|
|
{
|
|
m_pAce = 0;
|
|
m_dwStatus = 0;
|
|
|
|
// If the SID is invalid, the ACE will be as well.
|
|
// ===============================================
|
|
|
|
if (Sid.GetStatus() != CNtSid::NoError)
|
|
{
|
|
m_dwStatus = InvalidAce;
|
|
return;
|
|
}
|
|
|
|
// Compute the size of the ACE.
|
|
// ============================
|
|
|
|
DWORD dwSidLength = Sid.GetSize();
|
|
|
|
DWORD dwTotal = dwSidLength + sizeof(GENERIC_ACE) - 4;
|
|
|
|
m_pAce = (PGENERIC_ACE) new BYTE[dwTotal];
|
|
|
|
if (m_pAce)
|
|
{
|
|
ZeroMemory(m_pAce, dwTotal);
|
|
|
|
// Build up the ACE info.
|
|
// ======================
|
|
|
|
m_pAce->Header.AceType = BYTE(dwAceType);
|
|
m_pAce->Header.AceFlags = BYTE(dwAceFlags);
|
|
m_pAce->Header.AceSize = WORD(dwTotal);
|
|
m_pAce->Mask = AccessMask;
|
|
|
|
BOOL bRes = Sid.CopyTo(PSID(&m_pAce->SidStart));
|
|
|
|
if (!bRes)
|
|
{
|
|
delete m_pAce;
|
|
m_pAce = 0;
|
|
m_dwStatus = InvalidAce;
|
|
return;
|
|
}
|
|
|
|
m_dwStatus = NoError;
|
|
}
|
|
else
|
|
m_dwStatus = InternalError;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::CNtAce
|
|
//
|
|
// Constructor which directly builds the ACE based on a user, access mask
|
|
// and flags without a need to build an explicit SID.
|
|
//
|
|
//
|
|
// Parameters:
|
|
// <AccessMask> A WINNT ACCESS_MASK which specifies the permissions
|
|
// the user should have to the object being secured.
|
|
// See ACCESS_MASK in NT SDK documentation.
|
|
// <dwAceType> One of the following:
|
|
// ACCESS_ALLOWED_ACE_TYPE
|
|
// ACCESS_DENIED_ACE_TYPE
|
|
// ACCESS_AUDIT_ACE_TYPE
|
|
// See ACE_HEADER in NT SDK documentation.
|
|
// <dwAceFlags> Of of the ACE propation flags. See ACE_HEADER
|
|
// in NT SDK documentation for legal values.
|
|
// <pUser> The user or group for which the ACE is being
|
|
// created.
|
|
// <pMachine> If NULL, the current machine, domain, and trusted
|
|
// domains are searched for a match. If not NULL,
|
|
// can point to a UNICODE machine name (with or without
|
|
// leading backslashes) which contains the account.
|
|
//
|
|
// After construction, call GetStatus() to verify that the ACE
|
|
// is valid. NoError is expected.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAce::CNtAce(
|
|
ACCESS_MASK AccessMask,
|
|
DWORD dwAceType,
|
|
DWORD dwAceFlags,
|
|
LPWSTR pUser,
|
|
LPWSTR pMachine
|
|
)
|
|
{
|
|
m_pAce = 0;
|
|
m_dwStatus = 0;
|
|
|
|
// Create the SID of the user.
|
|
// ===========================
|
|
|
|
CNtSid Sid(pUser, pMachine);
|
|
|
|
// If the SID is invalid, the ACE will be as well.
|
|
// ===============================================
|
|
|
|
if (Sid.GetStatus() != CNtSid::NoError)
|
|
{
|
|
m_dwStatus = InvalidAce;
|
|
return;
|
|
}
|
|
|
|
// Compute the size of the ACE.
|
|
// ============================
|
|
|
|
DWORD dwSidLength = Sid.GetSize();
|
|
|
|
DWORD dwTotal = dwSidLength + sizeof(GENERIC_ACE) - 4;
|
|
|
|
m_pAce = (PGENERIC_ACE) new BYTE[dwTotal];
|
|
if ( m_pAce == NULL )
|
|
{
|
|
m_dwStatus = InternalError;
|
|
return;
|
|
}
|
|
ZeroMemory(m_pAce, dwTotal);
|
|
|
|
// Build up the ACE info.
|
|
// ======================
|
|
|
|
m_pAce->Header.AceType = BYTE(dwAceType);
|
|
m_pAce->Header.AceFlags = BYTE(dwAceFlags);
|
|
m_pAce->Header.AceSize = WORD(dwTotal);
|
|
m_pAce->Mask = AccessMask;
|
|
|
|
BOOL bRes = Sid.CopyTo(PSID(&m_pAce->SidStart));
|
|
|
|
if (!bRes)
|
|
{
|
|
delete m_pAce;
|
|
m_pAce = 0;
|
|
m_dwStatus = InvalidAce;
|
|
return;
|
|
}
|
|
|
|
m_dwStatus = NoError;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::GetAccessMask
|
|
//
|
|
// Returns the ACCESS_MASK of the ACe.
|
|
//
|
|
//***************************************************************************
|
|
ACCESS_MASK CNtAce::GetAccessMask()
|
|
{
|
|
if (m_pAce == 0)
|
|
return 0;
|
|
return m_pAce->Mask;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::GetSerializedSize
|
|
//
|
|
// Returns the number of bytes needed to store this
|
|
//
|
|
//***************************************************************************
|
|
|
|
DWORD CNtAce::GetSerializedSize()
|
|
{
|
|
if (m_pAce == 0)
|
|
return 0;
|
|
return m_pAce->Header.AceSize;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::DumpAccessMask
|
|
//
|
|
// A helper function for CNtAce::Dump(). Illustrates the values
|
|
// that the ACCESS_MASK for the ACE can take on.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
void CNtAce::DumpAccessMask()
|
|
{
|
|
if (m_pAce == 0)
|
|
return;
|
|
|
|
ACCESS_MASK AccessMask = m_pAce->Mask;
|
|
|
|
printf("Access Mask = 0x%X\n", AccessMask);
|
|
printf(" User Portion = 0x%X\n", AccessMask & 0xFFFF);
|
|
|
|
if (AccessMask & DELETE)
|
|
printf(" DELETE\n");
|
|
if (AccessMask & READ_CONTROL)
|
|
printf(" READ_CONTROL\n");
|
|
if (AccessMask & WRITE_DAC)
|
|
printf(" WRITE_DAC\n");
|
|
if (AccessMask & WRITE_OWNER)
|
|
printf(" WRITE_OWNER\n");
|
|
if (AccessMask & SYNCHRONIZE)
|
|
printf(" SYNCHRONIZE\n");
|
|
if (AccessMask & ACCESS_SYSTEM_SECURITY)
|
|
printf(" ACCESS_SYSTEM_SECURITY\n");
|
|
if (AccessMask & MAXIMUM_ALLOWED)
|
|
printf(" MAXIMUM_ALLOWED\n");
|
|
if (AccessMask & GENERIC_ALL)
|
|
printf(" GENERIC_ALL\n");
|
|
if (AccessMask & GENERIC_EXECUTE)
|
|
printf(" GENERIC_EXECUTE\n");
|
|
if (AccessMask & GENERIC_READ)
|
|
printf(" GENERIC_READ\n");
|
|
if (AccessMask & GENERIC_WRITE)
|
|
printf(" GENERIC_WRITE\n");
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::GetSid
|
|
//
|
|
// Returns a copy of the CNtSid object which makes up the ACE.
|
|
//
|
|
// Return value:
|
|
// A newly allocated CNtSid which represents the user or group
|
|
// referenced in the ACE. The caller must use operator delete to free
|
|
// the memory.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSid* CNtAce::GetSid()
|
|
{
|
|
if (m_pAce == 0)
|
|
return 0;
|
|
|
|
PSID pSid = 0;
|
|
|
|
pSid = &m_pAce->SidStart;
|
|
|
|
if (!IsValidSid(pSid))
|
|
return 0;
|
|
|
|
return new CNtSid(pSid);
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::GetSid
|
|
//
|
|
// Gets the SID in an alternate manner, by assigning to an existing
|
|
// object instead of returning a dynamically allocated one.
|
|
//
|
|
// Parameters:
|
|
// <Dest> A reference to a CNtSid to receive the SID.
|
|
//
|
|
// Return value:
|
|
// TRUE on successful assignment, FALSE on failure.
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL CNtAce::GetSid(CNtSid &Dest)
|
|
{
|
|
CNtSid *pSid = GetSid();
|
|
if (pSid == 0)
|
|
return FALSE;
|
|
|
|
Dest = *pSid;
|
|
delete pSid;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::Dump
|
|
//
|
|
// Dumps the current ACE to the console for debugging purposes.
|
|
// Illustrates the structure of the ACE and the values the different
|
|
// fields can take on.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
void CNtAce::Dump(int iAceNum)
|
|
{
|
|
if(iAceNum != -1)
|
|
printf("\n---ACE DUMP FOR ACE #%d---\n", iAceNum);
|
|
else
|
|
printf("\n---ACE DUMP---\n");
|
|
|
|
printf("Ace Type = ");
|
|
|
|
if (m_pAce == 0)
|
|
{
|
|
printf("NULL ACE\n");
|
|
return;
|
|
}
|
|
|
|
switch (m_pAce->Header.AceType)
|
|
{
|
|
case ACCESS_ALLOWED_ACE_TYPE:
|
|
printf("ACCESS_ALLOWED_ACE_TYPE\n");
|
|
break;
|
|
|
|
case ACCESS_DENIED_ACE_TYPE:
|
|
printf("ACCESS_DENIED_ACE_TYPE\n");
|
|
break;
|
|
|
|
case SYSTEM_AUDIT_ACE_TYPE:
|
|
printf("SYSTEM_AUDIT_ACE_TYPE\n");
|
|
break;
|
|
|
|
default:
|
|
printf("INVALID ACE\n");
|
|
break;
|
|
}
|
|
|
|
// Dump ACE flags.
|
|
// ===============
|
|
|
|
printf("ACE FLAGS = ");
|
|
|
|
if (m_pAce->Header.AceFlags & INHERITED_ACE)
|
|
printf("INHERITED_ACE ");
|
|
if (m_pAce->Header.AceFlags & CONTAINER_INHERIT_ACE)
|
|
printf("CONTAINER_INHERIT_ACE ");
|
|
if (m_pAce->Header.AceFlags & INHERIT_ONLY_ACE)
|
|
printf("INHERIT_ONLY_ACE ");
|
|
if (m_pAce->Header.AceFlags & NO_PROPAGATE_INHERIT_ACE)
|
|
printf("NO_PROPAGATE_INHERIT_ACE ");
|
|
if (m_pAce->Header.AceFlags & OBJECT_INHERIT_ACE)
|
|
printf("OBJECT_INHERIT_ACE ");
|
|
if (m_pAce->Header.AceFlags & FAILED_ACCESS_ACE_FLAG)
|
|
printf(" FAILED_ACCESS_ACE_FLAG");
|
|
if (m_pAce->Header.AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG)
|
|
printf(" SUCCESSFUL_ACCESS_ACE_FLAG");
|
|
printf("\n");
|
|
|
|
// Dump the SID.
|
|
// =============
|
|
|
|
CNtSid *pSid = GetSid();
|
|
|
|
if (pSid)
|
|
{
|
|
pSid->Dump();
|
|
delete pSid;
|
|
}
|
|
|
|
DumpAccessMask();
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::CNtAce
|
|
//
|
|
// Alternate constructor which uses a normal NT ACE as a basis for
|
|
// object construction.
|
|
//
|
|
// Parameters:
|
|
// <pAceSrc> A read-only pointer to the source ACE upon which to
|
|
// base object construction.
|
|
//
|
|
// After construction, GetStatus() can be used to determine if the
|
|
// object constructed properly. NoError is expected.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAce::CNtAce(PGENERIC_ACE pAceSrc)
|
|
{
|
|
m_dwStatus = NoError;
|
|
|
|
if (pAceSrc == 0)
|
|
{
|
|
m_dwStatus = NullAce;
|
|
m_pAce = 0;
|
|
}
|
|
|
|
m_pAce = (PGENERIC_ACE) new BYTE[pAceSrc->Header.AceSize];
|
|
if ( m_pAce == NULL )
|
|
{
|
|
m_dwStatus = InternalError;
|
|
return;
|
|
}
|
|
ZeroMemory(m_pAce, pAceSrc->Header.AceSize);
|
|
memcpy(m_pAce, pAceSrc, pAceSrc->Header.AceSize);
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce copy constructor.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAce::CNtAce(CNtAce &Src)
|
|
{
|
|
m_dwStatus = NoError;
|
|
m_pAce = 0;
|
|
*this = Src;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce assignment operator.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAce &CNtAce::operator =(CNtAce &Src)
|
|
{
|
|
if (m_pAce != 0)
|
|
delete m_pAce;
|
|
|
|
if (Src.m_pAce == 0)
|
|
{
|
|
m_pAce = 0;
|
|
m_dwStatus = NullAce;
|
|
return *this;
|
|
}
|
|
|
|
m_pAce = (PGENERIC_ACE) new BYTE[Src.m_pAce->Header.AceSize];
|
|
if(m_pAce == NULL)
|
|
{
|
|
m_dwStatus = InternalError;
|
|
}
|
|
else
|
|
{
|
|
ZeroMemory(m_pAce, Src.m_pAce->Header.AceSize);
|
|
memcpy(m_pAce, Src.m_pAce, Src.m_pAce->Header.AceSize);
|
|
m_dwStatus = Src.m_dwStatus;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce destructor
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAce::~CNtAce()
|
|
{
|
|
if (m_pAce)
|
|
delete m_pAce;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::GetType
|
|
//
|
|
// Gets the Ace Type as defined under the NT SDK documentation for
|
|
// ACE_HEADER.
|
|
//
|
|
// Return value:
|
|
// Returns ACCESS_ALLOWED_ACE_TYPE, ACCESS_DENIED_ACE_TYPE, or
|
|
// SYSTEM_AUDIT_ACE_TYPE. Returns -1 on error, such as a null ACE.
|
|
//
|
|
// Returning -1 (or an analog) is required as an error code because
|
|
// ACCESS_ALLOWED_ACE_TYPE is defined to be zero.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
int CNtAce::GetType()
|
|
{
|
|
if (m_pAce == 0 || m_dwStatus != NoError)
|
|
return -1;
|
|
return m_pAce->Header.AceType;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::GetFlags
|
|
//
|
|
// Gets the Ace Flag as defined under the NT SDK documentation for
|
|
// ACE_HEADER.
|
|
//
|
|
// Return value:
|
|
// Returning -1 if error, other wise the flags.
|
|
//
|
|
//***************************************************************************
|
|
|
|
int CNtAce::GetFlags()
|
|
{
|
|
if (m_pAce == 0 || m_dwStatus != NoError)
|
|
return -1;
|
|
return m_pAce->Header.AceFlags;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::GetFullUserName
|
|
//
|
|
// Gets the domain\user name.
|
|
//
|
|
//***************************************************************************
|
|
|
|
HRESULT CNtAce::GetFullUserName(WCHAR * pBuff, DWORD dwSize)
|
|
{
|
|
CNtSid *pSid = GetSid();
|
|
if(pSid == NULL)
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
CDeleteMe<CNtSid> d0(pSid);
|
|
DWORD dwJunk;
|
|
LPWSTR pRetAccount = NULL, pRetDomain = NULL;
|
|
pSid->GetInfo(&pRetAccount, &pRetDomain,&dwJunk);
|
|
CDeleteMe<WCHAR> d1(pRetAccount);
|
|
CDeleteMe<WCHAR> d2(pRetDomain);
|
|
WCHAR wTemp[256];
|
|
wTemp[0] = 0;
|
|
if(pRetDomain && wcslen(pRetDomain) > 0)
|
|
{
|
|
wcscpy(wTemp, pRetDomain);
|
|
wcscat(wTemp, L"|");
|
|
}
|
|
wcscat(wTemp, pRetAccount);
|
|
wcsncpy(pBuff, wTemp, dwSize-1);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CNtAce::GetFullUserName2(WCHAR ** pBuff)
|
|
{
|
|
CNtSid *pSid = GetSid();
|
|
if(pSid == NULL)
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
CDeleteMe<CNtSid> d0(pSid);
|
|
DWORD dwJunk;
|
|
LPWSTR pRetAccount = NULL, pRetDomain = NULL;
|
|
if(0 != pSid->GetInfo(&pRetAccount, &pRetDomain,&dwJunk))
|
|
return WBEM_E_FAILED;
|
|
|
|
CDeleteMe<WCHAR> d1(pRetAccount);
|
|
CDeleteMe<WCHAR> d2(pRetDomain);
|
|
|
|
int iLen = 3;
|
|
if(pRetAccount)
|
|
iLen += wcslen(pRetAccount);
|
|
if(pRetDomain)
|
|
iLen += wcslen(pRetDomain);
|
|
(*pBuff) = new WCHAR[iLen];
|
|
if((*pBuff) == NULL)
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
|
|
(*pBuff)[0] = 0;
|
|
if(pRetDomain && wcslen(pRetDomain) > 0)
|
|
wcscpy(*pBuff, pRetDomain);
|
|
else
|
|
wcscpy(*pBuff, L".");
|
|
wcscat(*pBuff, L"|");
|
|
wcscat(*pBuff, pRetAccount);
|
|
return S_OK;
|
|
|
|
}
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::Serialize
|
|
//
|
|
// Serializes the ace.
|
|
//
|
|
//***************************************************************************
|
|
|
|
bool CNtAce::Serialize(BYTE * pData)
|
|
{
|
|
if(m_pAce == NULL)
|
|
return false;
|
|
DWORD dwSize = m_pAce->Header.AceSize;
|
|
memcpy((void *)pData, (void *)m_pAce, dwSize);
|
|
return true;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAce::Deserialize
|
|
//
|
|
// Deserializes the ace. Normally this isnt called since the
|
|
// CNtAce(PGENERIC_ACE pAceSrc) constructor is fine. However, this is
|
|
// used for the case where the db was created on win9x and we are now
|
|
// running on nt. In that case, the format is the same as outlined in
|
|
// C9XAce::Serialize
|
|
//
|
|
//***************************************************************************
|
|
|
|
bool CNtAce::Deserialize(BYTE * pData)
|
|
{
|
|
BYTE * pNext;
|
|
pNext = pData + 2*(wcslen((LPWSTR)pData) + 1);
|
|
DWORD * pdwData = (DWORD *)pNext;
|
|
DWORD dwFlags, dwType, dwAccess;
|
|
dwFlags = *pdwData;
|
|
pdwData++;
|
|
dwType = *pdwData;
|
|
pdwData++;
|
|
dwAccess = *pdwData;
|
|
pdwData++;
|
|
CNtAce temp(dwAccess, dwType, dwFlags, (LPWSTR)pData);
|
|
*this = temp;
|
|
return true;
|
|
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl::CNtAcl
|
|
//
|
|
// Constructs an empty ACL with a user-specified size.
|
|
|
|
//
|
|
// Parameters:
|
|
// <dwInitialSize> Defaults to 128. Recommended values are 128 or
|
|
// higher in powers of two.
|
|
//
|
|
// After construction, GetStatus() should be called to verify
|
|
// the ACL initialized properly. Expected value is NoError.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAcl::CNtAcl(DWORD dwInitialSize)
|
|
{
|
|
m_pAcl = (PACL) new BYTE[dwInitialSize];
|
|
if ( m_pAcl == NULL )
|
|
{
|
|
m_dwStatus = InternalError;
|
|
return;
|
|
}
|
|
ZeroMemory(m_pAcl, dwInitialSize);
|
|
BOOL bRes = InitializeAcl(m_pAcl, dwInitialSize, ACL_REVISION);
|
|
|
|
if (!bRes)
|
|
{
|
|
delete m_pAcl;
|
|
m_pAcl = 0;
|
|
m_dwStatus = NullAcl;
|
|
return;
|
|
}
|
|
|
|
m_dwStatus = NoError;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl copy constructor.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAcl::CNtAcl(CNtAcl &Src)
|
|
{
|
|
m_pAcl = 0;
|
|
m_dwStatus = NoError;
|
|
|
|
*this = Src;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl assignment operator
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAcl &CNtAcl::operator = (CNtAcl &Src)
|
|
{
|
|
if (m_pAcl != 0)
|
|
delete m_pAcl;
|
|
|
|
// Default to a NULL ACL.
|
|
// ======================
|
|
|
|
m_pAcl = 0;
|
|
m_dwStatus = NullAcl;
|
|
|
|
if (Src.m_pAcl == 0)
|
|
return *this;
|
|
|
|
// Now copy the source ACL.
|
|
// ========================
|
|
|
|
DWORD dwSize = Src.m_pAcl->AclSize;
|
|
|
|
m_pAcl = (PACL) new BYTE[dwSize];
|
|
if(m_pAcl == NULL)
|
|
{
|
|
m_dwStatus = InternalError;
|
|
return *this;
|
|
}
|
|
|
|
ZeroMemory(m_pAcl, dwSize);
|
|
|
|
memcpy(m_pAcl, Src.m_pAcl, dwSize);
|
|
|
|
// Verify it.
|
|
// ==========
|
|
|
|
if (!IsValidAcl(m_pAcl))
|
|
{
|
|
delete m_pAcl;
|
|
m_pAcl = 0;
|
|
m_dwStatus = InvalidAcl;
|
|
return *this;
|
|
}
|
|
|
|
m_dwStatus = Src.m_dwStatus;
|
|
return *this;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl::GetAce
|
|
//
|
|
// Returns an ACE at the specified index. To enumerate ACEs, the caller
|
|
// should determine the number of ACEs using GetNumAces() and then call
|
|
// this function with each index starting from 0 to number of ACEs - 1.
|
|
//
|
|
// Parameters:
|
|
// <nIndex> The index of the desired ACE.
|
|
//
|
|
// Return value:
|
|
// A newly allocated CNtAce object which must be deallocated using
|
|
// operator delete. This is only a copy. Modifications to the returned
|
|
// CNtAce do not affect the ACL from which it came.
|
|
//
|
|
// Returns NULL on error.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAce *CNtAcl::GetAce(int nIndex)
|
|
{
|
|
if (m_pAcl == 0)
|
|
return 0;
|
|
|
|
LPVOID pAce = 0;
|
|
|
|
BOOL bRes = ::GetAce(m_pAcl, (DWORD) nIndex, &pAce);
|
|
|
|
if (!bRes)
|
|
return 0;
|
|
|
|
return new CNtAce(PGENERIC_ACE(pAce));
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl::GetAce
|
|
//
|
|
// Alternate method to get ACEs to avoid dynamic allocation & cleanup,
|
|
// since an auto object can be used as the parameter.
|
|
//
|
|
// Parameters:
|
|
// <Dest> A reference to a CNtAce to receive the ACE value.
|
|
//
|
|
// Return value:
|
|
// TRUE if assigned, FALSE if not.
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL CNtAcl::GetAce(int nIndex, CNtAce &Dest)
|
|
{
|
|
CNtAce *pNew = GetAce(nIndex);
|
|
if (pNew == 0)
|
|
return FALSE;
|
|
|
|
Dest = *pNew;
|
|
delete pNew;
|
|
return TRUE;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl::DeleteAce
|
|
//
|
|
// Removes the specified ACE from the ACL.
|
|
//
|
|
// Parameters:
|
|
// <nIndex> The 0-based index of the ACE which should be removed.
|
|
//
|
|
// Return value:
|
|
// TRUE if the ACE was deleted, FALSE if not.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
BOOL CNtAcl::DeleteAce(int nIndex)
|
|
{
|
|
if (m_pAcl == 0)
|
|
return FALSE;
|
|
|
|
BOOL bRes = ::DeleteAce(m_pAcl, DWORD(nIndex));
|
|
|
|
return bRes;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl::GetSize()
|
|
//
|
|
// Return value:
|
|
// Returns the size in bytes of the ACL
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
DWORD CNtAcl::GetSize()
|
|
{
|
|
if (m_pAcl == 0 || !IsValidAcl(m_pAcl))
|
|
return 0;
|
|
|
|
return DWORD(m_pAcl->AclSize);
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl::GetAclSizeInfo
|
|
//
|
|
// Gets information about used/unused space in the ACL. This function
|
|
// is primarily for internal use.
|
|
//
|
|
// Parameters:
|
|
// <pdwBytesInUse> Points to a DWORD to receive the number of
|
|
// bytes in use in the ACL. Can be NULL.
|
|
// <pdwBytesFree> Points to a DWORD to receive the number of
|
|
// bytes free in the ACL. Can be NULL.
|
|
//
|
|
// Return value:
|
|
// Returns TRUE if the information was retrieved, FALSE if not.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
BOOL CNtAcl::GetAclSizeInfo(
|
|
PDWORD pdwBytesInUse,
|
|
PDWORD pdwBytesFree
|
|
)
|
|
{
|
|
if (m_pAcl == 0)
|
|
return 0;
|
|
|
|
if (!IsValidAcl(m_pAcl))
|
|
return 0;
|
|
|
|
if (pdwBytesInUse)
|
|
*pdwBytesInUse = 0;
|
|
if (pdwBytesFree)
|
|
*pdwBytesFree = 0;
|
|
|
|
ACL_SIZE_INFORMATION inf;
|
|
|
|
BOOL bRes = GetAclInformation(
|
|
m_pAcl,
|
|
&inf,
|
|
sizeof(ACL_SIZE_INFORMATION),
|
|
AclSizeInformation
|
|
);
|
|
|
|
if (!bRes)
|
|
return FALSE;
|
|
|
|
if (pdwBytesInUse)
|
|
*pdwBytesInUse = inf.AclBytesInUse;
|
|
if (pdwBytesFree)
|
|
*pdwBytesFree = inf.AclBytesFree;
|
|
|
|
return bRes;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl::AddAce
|
|
//
|
|
// Adds an ACE to the ACL.
|
|
// Ordering semantics for denial ACEs are handled automatically.
|
|
//
|
|
// Parameters:
|
|
// <pAce> A read-only pointer to the CNtAce to be added.
|
|
//
|
|
// Return value:
|
|
// TRUE on success, FALSE on failure.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
BOOL CNtAcl::AddAce(CNtAce *pAce)
|
|
{
|
|
// Verify we have an ACL and a valid ACE.
|
|
// ======================================
|
|
|
|
if (m_pAcl == 0 || m_dwStatus != NoError)
|
|
return FALSE;
|
|
|
|
if (pAce->GetStatus() != CNtAce::NoError)
|
|
return FALSE;
|
|
|
|
// Inherited aces go after non inherited aces
|
|
|
|
bool bInherited = (pAce->GetFlags() & INHERITED_ACE) != 0;
|
|
int iFirstInherited = 0;
|
|
|
|
// inherited aces must go after non inherited. Find out
|
|
// the position of the first inherited ace
|
|
|
|
int iCnt;
|
|
for(iCnt = 0; iCnt < m_pAcl->AceCount; iCnt++)
|
|
{
|
|
CNtAce *pAce2 = GetAce(iCnt);
|
|
CDeleteMe<CNtAce> dm(pAce2);
|
|
if (pAce2)
|
|
if((pAce2->GetFlags() & INHERITED_ACE) != 0)
|
|
break;
|
|
}
|
|
iFirstInherited = iCnt;
|
|
|
|
|
|
// Since we want to add access denial ACEs to the front of the ACL,
|
|
// we have to determine the type of ACE.
|
|
// ================================================================
|
|
|
|
DWORD dwIndex;
|
|
|
|
if (pAce->GetType() == ACCESS_DENIED_ACE_TYPE)
|
|
dwIndex = (bInherited) ? iFirstInherited : 0;
|
|
else
|
|
dwIndex = (bInherited) ? MAXULONG : iFirstInherited;
|
|
|
|
// Verify that there is enough room in the ACL.
|
|
// ============================================
|
|
|
|
DWORD dwRequiredFree = pAce->GetSize();
|
|
|
|
DWORD dwFree = 0;
|
|
DWORD dwUsed = 0;
|
|
GetAclSizeInfo(&dwUsed, &dwFree);
|
|
|
|
// If we don't have enough room, resize the ACL.
|
|
// =============================================
|
|
|
|
if (dwFree < dwRequiredFree)
|
|
{
|
|
BOOL bRes = Resize(dwUsed + dwRequiredFree);
|
|
|
|
if (!bRes)
|
|
return FALSE;
|
|
}
|
|
|
|
// Now actually add the ACE.
|
|
// =========================
|
|
|
|
BOOL bRes = ::AddAce(
|
|
m_pAcl,
|
|
ACL_REVISION,
|
|
dwIndex, // Either beginning or end.
|
|
pAce->GetPtr(), // Get ptr to ACE.
|
|
pAce->GetSize() // One ACE only.
|
|
);
|
|
|
|
return bRes;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl::Resize()
|
|
//
|
|
// Expands the size of the ACL to hold more info or reduces the size
|
|
// of the ACL for maximum efficiency after ACL editing is completed.
|
|
//
|
|
// Normally, the user should not attempt to resize the ACL to a larger
|
|
// size, as this is automatically handled by AddAce. However, shrinking
|
|
// the ACL to its minimum size is recommended.
|
|
//
|
|
// Parameters:
|
|
// <dwNewSize> The required new size of the ACL in bytes. If set to
|
|
// the class constant MinimumSize (1), then the ACL
|
|
// is reduced to its minimum size.
|
|
//
|
|
// Return value:
|
|
// TRUE on success, FALSE on failure.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
BOOL CNtAcl::Resize(DWORD dwNewSize)
|
|
{
|
|
if (m_pAcl == 0 || m_dwStatus != NoError)
|
|
return FALSE;
|
|
|
|
if (!IsValidAcl(m_pAcl))
|
|
return FALSE;
|
|
|
|
// If the ACL cannot be reduced to the requested size,
|
|
// return FALSE.
|
|
// ===================================================
|
|
|
|
DWORD dwInUse, dwFree;
|
|
|
|
if (!GetAclSizeInfo(&dwInUse, &dwFree))
|
|
return FALSE;
|
|
|
|
if (dwNewSize == MinimumSize) // If user is requesting a 'minimize'
|
|
dwNewSize = dwInUse;
|
|
|
|
if (dwNewSize < dwInUse)
|
|
return FALSE;
|
|
|
|
// Allocate a new ACL.
|
|
// ===================
|
|
|
|
CNtAcl *pNewAcl = new CNtAcl(dwNewSize);
|
|
|
|
if (!pNewAcl || pNewAcl->GetStatus() != NoError)
|
|
{
|
|
delete pNewAcl;
|
|
return FALSE;
|
|
}
|
|
|
|
// Loop through ACEs and transfer them.
|
|
// ====================================
|
|
|
|
for (int i = 0; i < GetNumAces(); i++)
|
|
{
|
|
CNtAce *pAce = GetAce(i);
|
|
|
|
if (pAce == NULL)
|
|
{
|
|
delete pNewAcl;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL bRes = pNewAcl->AddAce(pAce);
|
|
|
|
if (!bRes)
|
|
{
|
|
DWORD dwLast = GetLastError();
|
|
delete pAce;
|
|
delete pNewAcl;
|
|
return FALSE;
|
|
}
|
|
|
|
delete pAce;
|
|
}
|
|
|
|
if (!IsValid())
|
|
{
|
|
delete pNewAcl;
|
|
return FALSE;
|
|
}
|
|
|
|
// Now transfer the ACL.
|
|
// =====================
|
|
|
|
*this = *pNewAcl;
|
|
delete pNewAcl;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl::CNtAcl
|
|
//
|
|
// Alternate constructor which builds the object based on a plain
|
|
// NT ACL.
|
|
//
|
|
// Parameters:
|
|
// <pAcl> Pointer to a read-only ACL.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
CNtAcl::CNtAcl(PACL pAcl)
|
|
{
|
|
m_pAcl = 0;
|
|
m_dwStatus = NoError;
|
|
|
|
if (pAcl == 0)
|
|
{
|
|
m_dwStatus = NullAcl;
|
|
return;
|
|
}
|
|
|
|
if (!IsValidAcl(pAcl))
|
|
{
|
|
m_dwStatus = InvalidAcl;
|
|
return;
|
|
}
|
|
|
|
m_pAcl = (PACL) new BYTE[pAcl->AclSize];
|
|
if(m_pAcl == NULL)
|
|
{
|
|
m_dwStatus = InternalError;
|
|
return;
|
|
}
|
|
ZeroMemory(m_pAcl, pAcl->AclSize);
|
|
memcpy(m_pAcl, pAcl, pAcl->AclSize);
|
|
}
|
|
|
|
/*
|
|
--------------------------------------------------------------------------
|
|
|
|
|
| Checks to see if the Acl contains an ACE with the specified SID.
|
|
| The characteristics of the ACE is irrelevant. Only SID comparison applies.
|
|
|
|
|
--------------------------------------------------------------------------
|
|
*/
|
|
BOOL CNtAcl::ContainsSid ( CNtSid& sid, BYTE& flags )
|
|
{
|
|
BOOL bContainsSid = FALSE ;
|
|
|
|
int iNumAces = GetNumAces ( ) ;
|
|
if ( iNumAces < 0 )
|
|
{
|
|
return FALSE ;
|
|
}
|
|
|
|
for ( int i = 0 ; i < iNumAces; i++ )
|
|
{
|
|
CNtAce* pAce = GetAce ( i ) ;
|
|
CNtSid* pSid = pAce->GetSid ( ) ;
|
|
|
|
CDeleteMe<CNtAce> AceDelete ( pAce ) ;
|
|
CDeleteMe<CNtSid> SidDelete ( pSid ) ;
|
|
|
|
if ( pAce && pSid )
|
|
{
|
|
if ( EqualSid ( sid.GetPtr ( ), pSid->GetPtr ( ) ) == TRUE )
|
|
{
|
|
flags = ( BYTE ) pAce->GetFlags ( ) ;
|
|
bContainsSid = TRUE ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
return bContainsSid ;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl::GetNumAces
|
|
//
|
|
// Return value:
|
|
// Returns the number of ACEs available in the ACL. Zero is a legal return
|
|
// value. Returns -1 on error
|
|
//
|
|
// Aces can be retrieved using GetAce using index values from 0...n-1 where
|
|
// n is the value returned from this function.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
int CNtAcl::GetNumAces()
|
|
{
|
|
if (m_pAcl == 0)
|
|
return -1;
|
|
|
|
ACL_SIZE_INFORMATION inf;
|
|
|
|
BOOL bRes = GetAclInformation(
|
|
m_pAcl,
|
|
&inf,
|
|
sizeof(ACL_SIZE_INFORMATION),
|
|
AclSizeInformation
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return (int) inf.AceCount;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl destructor
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAcl::~CNtAcl()
|
|
{
|
|
if (m_pAcl)
|
|
delete m_pAcl;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtAcl::Dump
|
|
//
|
|
// Dumps the ACL to the console for debugging purposes. Illustrates
|
|
// how to traverse the ACL and extract the ACEs.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
void CNtAcl::Dump()
|
|
{
|
|
printf("---ACL DUMP---\n");
|
|
|
|
if (m_pAcl == 0)
|
|
{
|
|
switch (m_dwStatus)
|
|
{
|
|
case NullAcl:
|
|
printf("NullAcl\n");
|
|
break;
|
|
|
|
case InvalidAcl:
|
|
printf("InvalidAcl\n");
|
|
break;
|
|
|
|
default:
|
|
printf("<internal error; unknown status>\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
DWORD InUse, Free;
|
|
GetAclSizeInfo(&InUse, &Free);
|
|
printf("%d bytes in use, %d bytes free\n",
|
|
InUse, Free
|
|
);
|
|
|
|
printf("Number of ACEs = %d\n", GetNumAces());
|
|
|
|
for (int i = 0; i < GetNumAces(); i++)
|
|
{
|
|
CNtAce *pAce = GetAce(i);
|
|
if (pAce)
|
|
{
|
|
pAce->Dump(i+1);
|
|
delete pAce;
|
|
}
|
|
}
|
|
|
|
printf("---END ACL DUMP---\n");
|
|
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::GetDacl
|
|
//
|
|
// Returns the DACL of the security descriptor.
|
|
//
|
|
// Return value:
|
|
// A newly allocated CNtAcl which contains the DACL. This object
|
|
// is a copy of the DACL and modifications made to it do not affect
|
|
// the security descriptor. The caller must use operator delete
|
|
// to deallocate the CNtAcl.
|
|
//
|
|
// Returns NULL on error or if no DACL is available.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAcl *CNtSecurityDescriptor::GetDacl()
|
|
{
|
|
BOOL bDaclPresent = FALSE;
|
|
BOOL bDefaulted;
|
|
|
|
PACL pDacl;
|
|
BOOL bRes = GetSecurityDescriptorDacl(
|
|
m_pSD,
|
|
&bDaclPresent,
|
|
&pDacl,
|
|
&bDefaulted
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (!bDaclPresent) // No DACL present
|
|
return 0;
|
|
|
|
CNtAcl *pNewDacl = new CNtAcl(pDacl);
|
|
|
|
return pNewDacl;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::GetDacl
|
|
//
|
|
// An alternate method to returns the DACL of the security descriptor.
|
|
// This version uses an existing object instead of returning a
|
|
// dynamically allocated object.
|
|
//
|
|
// Parameters:
|
|
// <DestAcl> A object which will receive the DACL.
|
|
//
|
|
// Return value:
|
|
// TRUE on success, FALSE on failure
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL CNtSecurityDescriptor::GetDacl(CNtAcl &DestAcl)
|
|
{
|
|
CNtAcl *pNew = GetDacl();
|
|
if (pNew == 0)
|
|
return FALSE;
|
|
|
|
DestAcl = *pNew;
|
|
delete pNew;
|
|
return TRUE;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// SNtAbsoluteSD
|
|
//
|
|
// SD Helpers
|
|
//
|
|
//***************************************************************************
|
|
|
|
SNtAbsoluteSD::SNtAbsoluteSD()
|
|
{
|
|
m_pSD = 0;
|
|
m_pDacl = 0;
|
|
m_pSacl = 0;
|
|
m_pOwner = 0;
|
|
m_pPrimaryGroup = 0;
|
|
}
|
|
|
|
SNtAbsoluteSD::~SNtAbsoluteSD()
|
|
{
|
|
if (m_pSD)
|
|
delete m_pSD;
|
|
if (m_pDacl)
|
|
delete m_pDacl;
|
|
if (m_pSacl)
|
|
delete m_pSacl;
|
|
if (m_pOwner)
|
|
delete m_pOwner;
|
|
if (m_pPrimaryGroup)
|
|
delete m_pPrimaryGroup;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::GetAbsoluteCopy
|
|
//
|
|
// Returns a copy of the current object's internal SD in absolute format.
|
|
// Returns NULL on error. The memory must be freed with LocalFree().
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
SNtAbsoluteSD* CNtSecurityDescriptor::GetAbsoluteCopy()
|
|
{
|
|
if (m_dwStatus != NoError || m_pSD == 0 || !IsValid())
|
|
return 0;
|
|
|
|
// Prepare for conversion.
|
|
// =======================
|
|
|
|
DWORD dwSDSize = 0, dwDaclSize = 0, dwSaclSize = 0,
|
|
dwOwnerSize = 0, dwPrimaryGroupSize = 0;
|
|
|
|
SNtAbsoluteSD *pNewSD = new SNtAbsoluteSD;
|
|
if (!pNewSD)
|
|
return NULL;
|
|
|
|
BOOL bRes = MakeAbsoluteSD(
|
|
m_pSD,
|
|
pNewSD->m_pSD,
|
|
&dwSDSize,
|
|
pNewSD->m_pDacl,
|
|
&dwDaclSize,
|
|
pNewSD->m_pSacl,
|
|
&dwSaclSize,
|
|
pNewSD->m_pOwner,
|
|
&dwOwnerSize,
|
|
pNewSD->m_pPrimaryGroup,
|
|
&dwPrimaryGroupSize
|
|
);
|
|
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
delete pNewSD;
|
|
return 0;
|
|
}
|
|
|
|
// Allocate the required buffers and convert.
|
|
// ==========================================
|
|
|
|
pNewSD->m_pSD = (PSECURITY_DESCRIPTOR) new BYTE[dwSDSize];
|
|
if(pNewSD->m_pSD == NULL)
|
|
{
|
|
delete pNewSD;
|
|
return NULL;
|
|
}
|
|
ZeroMemory(pNewSD->m_pSD, dwSDSize);
|
|
|
|
pNewSD->m_pDacl = (PACL) new BYTE[dwDaclSize];
|
|
if(pNewSD->m_pDacl == NULL)
|
|
{
|
|
delete pNewSD;
|
|
return NULL;
|
|
}
|
|
ZeroMemory(pNewSD->m_pDacl, dwDaclSize);
|
|
|
|
pNewSD->m_pSacl = (PACL) new BYTE[dwSaclSize];
|
|
if(pNewSD->m_pSacl == NULL)
|
|
{
|
|
delete pNewSD;
|
|
return NULL;
|
|
}
|
|
ZeroMemory(pNewSD->m_pSacl, dwSaclSize);
|
|
|
|
pNewSD->m_pOwner = (PSID) new BYTE[dwOwnerSize];
|
|
if(pNewSD->m_pOwner == NULL)
|
|
{
|
|
delete pNewSD;
|
|
return NULL;
|
|
}
|
|
ZeroMemory(pNewSD->m_pOwner, dwOwnerSize);
|
|
|
|
pNewSD->m_pPrimaryGroup = (PSID) new BYTE[dwPrimaryGroupSize];
|
|
if(pNewSD->m_pPrimaryGroup == NULL)
|
|
{
|
|
delete pNewSD;
|
|
return NULL;
|
|
}
|
|
ZeroMemory(pNewSD->m_pPrimaryGroup, dwPrimaryGroupSize);
|
|
|
|
bRes = MakeAbsoluteSD(
|
|
m_pSD,
|
|
pNewSD->m_pSD,
|
|
&dwSDSize,
|
|
pNewSD->m_pDacl,
|
|
&dwDaclSize,
|
|
pNewSD->m_pSacl,
|
|
&dwSaclSize,
|
|
pNewSD->m_pOwner,
|
|
&dwOwnerSize,
|
|
pNewSD->m_pPrimaryGroup,
|
|
&dwPrimaryGroupSize
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
delete pNewSD;
|
|
return 0;
|
|
}
|
|
|
|
return pNewSD;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::SetFromAbsoluteCopy
|
|
//
|
|
// Replaces the current SD from an absolute copy.
|
|
//
|
|
// Parameters:
|
|
// <pSrcSD> A read-only pointer to the absolute SD used as a source.
|
|
//
|
|
// Return value:
|
|
// TRUE on success, FALSE on failure.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
BOOL CNtSecurityDescriptor::SetFromAbsoluteCopy(
|
|
SNtAbsoluteSD *pSrcSD
|
|
)
|
|
{
|
|
if (pSrcSD == 0 || !IsValidSecurityDescriptor(pSrcSD->m_pSD))
|
|
return FALSE;
|
|
|
|
|
|
// Ensure that SD is self-relative
|
|
// ===============================
|
|
|
|
SECURITY_DESCRIPTOR_CONTROL ctrl;
|
|
DWORD dwRev;
|
|
|
|
BOOL bRes = GetSecurityDescriptorControl(
|
|
pSrcSD->m_pSD,
|
|
&ctrl,
|
|
&dwRev
|
|
);
|
|
|
|
if (!bRes)
|
|
return FALSE;
|
|
|
|
if (ctrl & SE_SELF_RELATIVE) // Source is not absolute!!
|
|
return FALSE;
|
|
|
|
// If here, we are committed to change.
|
|
// ====================================
|
|
|
|
if (m_pSD)
|
|
{
|
|
delete m_pSD;
|
|
}
|
|
m_pSD = 0;
|
|
m_dwStatus = NullSD;
|
|
|
|
|
|
DWORD dwRequired = 0;
|
|
|
|
bRes = MakeSelfRelativeSD(
|
|
pSrcSD->m_pSD,
|
|
m_pSD,
|
|
&dwRequired
|
|
);
|
|
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
m_dwStatus = InvalidSD;
|
|
return FALSE;
|
|
}
|
|
|
|
m_pSD = new BYTE[dwRequired];
|
|
if (!m_pSD)
|
|
{
|
|
m_dwStatus = InvalidSD;
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory(m_pSD, dwRequired);
|
|
|
|
bRes = MakeSelfRelativeSD(
|
|
pSrcSD->m_pSD,
|
|
m_pSD,
|
|
&dwRequired
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
m_dwStatus = InvalidSD;
|
|
delete m_pSD;
|
|
m_pSD = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
m_dwStatus = NoError;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::SetDacl
|
|
//
|
|
// Sets the DACL of the Security descriptor.
|
|
//
|
|
// Parameters:
|
|
// <pSrc> A read-only pointer to the new DACL to replace the current one.
|
|
//
|
|
// Return value:
|
|
// TRUE on success, FALSE on failure.
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL CNtSecurityDescriptor::SetDacl(CNtAcl *pSrc)
|
|
{
|
|
if (m_dwStatus != NoError || m_pSD == 0)
|
|
return FALSE;
|
|
|
|
|
|
// Since we cannot alter a self-relative SD, we have to make
|
|
// an absolute one, alter it, and then set the current
|
|
// SD based on the absolute one (we keep the self-relative form
|
|
// internally in the m_pSD variable.
|
|
// ============================================================
|
|
|
|
SNtAbsoluteSD *pTmp = GetAbsoluteCopy();
|
|
|
|
if (pTmp == 0)
|
|
return FALSE;
|
|
|
|
BOOL bRes = ::SetSecurityDescriptorDacl(
|
|
pTmp->m_pSD,
|
|
TRUE,
|
|
pSrc->GetPtr(),
|
|
FALSE
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
delete pTmp;
|
|
return FALSE;
|
|
}
|
|
|
|
bRes = SetFromAbsoluteCopy(pTmp);
|
|
delete pTmp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::Dump
|
|
//
|
|
// Dumps the contents of the security descriptor to the console
|
|
// for debugging purposes.
|
|
//
|
|
//***************************************************************************
|
|
// ?
|
|
|
|
void CNtSecurityDescriptor::Dump()
|
|
{
|
|
SECURITY_DESCRIPTOR_CONTROL Control;
|
|
DWORD dwRev;
|
|
BOOL bRes;
|
|
|
|
printf("--- SECURITY DESCRIPTOR DUMP ---\n");
|
|
|
|
bRes = GetSecurityDescriptorControl(m_pSD, &Control, &dwRev);
|
|
|
|
if (!bRes)
|
|
{
|
|
printf("SD Dump: Failed to get control info\n");
|
|
return;
|
|
}
|
|
|
|
printf("Revision : 0x%X\n", dwRev);
|
|
|
|
printf("Control Info :\n");
|
|
|
|
if (Control & SE_SELF_RELATIVE)
|
|
printf(" SE_SELF_RELATIVE\n");
|
|
|
|
if (Control & SE_OWNER_DEFAULTED)
|
|
printf(" SE_OWNER_DEFAULTED\n");
|
|
|
|
if (Control & SE_GROUP_DEFAULTED)
|
|
printf(" SE_GROUP_DEFAULTED\n");
|
|
|
|
if (Control & SE_DACL_PRESENT)
|
|
printf(" SE_DACL_PRESENT\n");
|
|
|
|
if (Control & SE_DACL_DEFAULTED)
|
|
printf(" SE_DACL_DEFAULTED\n");
|
|
|
|
if (Control & SE_SACL_PRESENT)
|
|
printf(" SE_SACL_PRESENT\n");
|
|
|
|
if (Control & SE_SACL_DEFAULTED)
|
|
printf(" SE_SACL_DEFAULTED\n");
|
|
|
|
if (Control & SE_DACL_PROTECTED)
|
|
printf(" SE_DACL_PROTECTED\n");
|
|
|
|
// Get owner.
|
|
// =========
|
|
|
|
CNtSid *pSid = GetOwner();
|
|
|
|
if (pSid)
|
|
{
|
|
printf("Owner : ");
|
|
pSid->Dump();
|
|
delete pSid;
|
|
}
|
|
|
|
CNtAcl *pDacl = GetDacl();
|
|
|
|
if (pDacl == 0)
|
|
{
|
|
printf("Unable to locate DACL\n");
|
|
return;
|
|
}
|
|
|
|
printf("DACL retrieved\n");
|
|
|
|
pDacl->Dump();
|
|
|
|
delete pDacl;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor constructor
|
|
//
|
|
// A default constructor creates a no-access security descriptor.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSecurityDescriptor::CNtSecurityDescriptor()
|
|
{
|
|
m_pSD = 0;
|
|
m_dwStatus = NoError;
|
|
|
|
PSECURITY_DESCRIPTOR pTmp = new BYTE[SECURITY_DESCRIPTOR_MIN_LENGTH];
|
|
if (!pTmp)
|
|
{
|
|
delete pTmp;
|
|
m_dwStatus = InvalidSD;
|
|
return;
|
|
}
|
|
|
|
ZeroMemory(pTmp, SECURITY_DESCRIPTOR_MIN_LENGTH);
|
|
|
|
if (!InitializeSecurityDescriptor(pTmp, SECURITY_DESCRIPTOR_REVISION))
|
|
{
|
|
delete pTmp;
|
|
m_dwStatus = InvalidSD;
|
|
return;
|
|
}
|
|
|
|
DWORD dwRequired = 0;
|
|
|
|
BOOL bRes = MakeSelfRelativeSD(
|
|
pTmp,
|
|
m_pSD,
|
|
&dwRequired
|
|
);
|
|
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
m_dwStatus = InvalidSD;
|
|
delete pTmp;
|
|
return;
|
|
}
|
|
|
|
m_pSD = new BYTE[dwRequired];
|
|
if (!m_pSD)
|
|
{
|
|
m_dwStatus = InvalidSD;
|
|
delete pTmp;
|
|
return;
|
|
}
|
|
|
|
ZeroMemory(m_pSD, dwRequired);
|
|
|
|
bRes = MakeSelfRelativeSD(
|
|
pTmp,
|
|
m_pSD,
|
|
&dwRequired
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
m_dwStatus = InvalidSD;
|
|
delete m_pSD;
|
|
m_pSD = 0;
|
|
delete pTmp;
|
|
return;
|
|
}
|
|
|
|
delete pTmp;
|
|
m_dwStatus = NoError;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::GetSize
|
|
//
|
|
// Returns the size in bytes of the internal SD.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
DWORD CNtSecurityDescriptor::GetSize()
|
|
{
|
|
if (m_pSD == 0 || m_dwStatus != NoError)
|
|
return 0;
|
|
|
|
return GetSecurityDescriptorLength(m_pSD);
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor copy constructor
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSecurityDescriptor::CNtSecurityDescriptor(CNtSecurityDescriptor &Src)
|
|
{
|
|
m_pSD = 0;
|
|
m_dwStatus = NoError;
|
|
*this = Src;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor assignment operator
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSecurityDescriptor & CNtSecurityDescriptor::operator=(
|
|
CNtSecurityDescriptor &Src
|
|
)
|
|
{
|
|
if (m_pSD)
|
|
delete m_pSD;
|
|
|
|
m_dwStatus = Src.m_dwStatus;
|
|
m_pSD = 0;
|
|
|
|
if (Src.m_pSD == 0)
|
|
return *this;
|
|
|
|
//SIZE_T dwSize = 2*GetSecurityDescriptorLength(Src.m_pSD);
|
|
SIZE_T dwSize = GetSecurityDescriptorLength(Src.m_pSD);
|
|
m_pSD = (PSECURITY_DESCRIPTOR) new BYTE[dwSize];
|
|
if(m_pSD == NULL)
|
|
{
|
|
m_dwStatus = Failed;
|
|
}
|
|
else
|
|
{
|
|
ZeroMemory(m_pSD, dwSize);
|
|
CopyMemory(m_pSD, Src.m_pSD, dwSize);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor destructor.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSecurityDescriptor::~CNtSecurityDescriptor()
|
|
{
|
|
if (m_pSD)
|
|
delete m_pSD;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::GetSacl
|
|
//
|
|
// Returns the SACL of the security descriptor.
|
|
//
|
|
// Return value:
|
|
// A newly allocated CNtAcl which contains the SACL. This object
|
|
// is a copy of the SACL and modifications made to it do not affect
|
|
// the security descriptor. The caller must use operator delete
|
|
// to deallocate the CNtAcl.
|
|
//
|
|
// Returns NULL on error or if no SACL is available.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtAcl *CNtSecurityDescriptor::GetSacl()
|
|
{
|
|
BOOL bSaclPresent = FALSE;
|
|
BOOL bDefaulted;
|
|
|
|
PACL pSacl;
|
|
BOOL bRes = GetSecurityDescriptorSacl(
|
|
m_pSD,
|
|
&bSaclPresent,
|
|
&pSacl,
|
|
&bDefaulted
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (!bSaclPresent) // No Sacl present
|
|
return 0;
|
|
|
|
CNtAcl *pNewSacl = new CNtAcl(pSacl);
|
|
|
|
return pNewSacl;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::SetSacl
|
|
//
|
|
// Sets the SACL of the Security descriptor.
|
|
//
|
|
// Parameters:
|
|
// <pSrc> A read-only pointer to the new DACL to replace the current one.
|
|
//
|
|
// Return value:
|
|
// TRUE on success, FALSE on failure.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
BOOL CNtSecurityDescriptor::SetSacl(CNtAcl *pSrc)
|
|
{
|
|
if (m_dwStatus != NoError || m_pSD == 0)
|
|
return FALSE;
|
|
|
|
// Since we cannot alter a self-relative SD, we have to make
|
|
// an absolute one, alter it, and then set the current
|
|
// SD based on the absolute one (we keep the self-relative form
|
|
// internally in the m_pSD variable.
|
|
// ============================================================
|
|
|
|
SNtAbsoluteSD *pTmp = GetAbsoluteCopy();
|
|
|
|
if (pTmp == 0)
|
|
return FALSE;
|
|
|
|
BOOL bRes = ::SetSecurityDescriptorSacl(
|
|
pTmp->m_pSD,
|
|
TRUE,
|
|
pSrc->GetPtr(),
|
|
FALSE
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
delete pTmp;
|
|
return FALSE;
|
|
}
|
|
|
|
bRes = SetFromAbsoluteCopy(pTmp);
|
|
delete pTmp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::GetGroup
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSid *CNtSecurityDescriptor::GetGroup()
|
|
{
|
|
if (m_pSD == 0 || m_dwStatus != NoError)
|
|
return 0;
|
|
|
|
PSID pSid = 0;
|
|
BOOL bDefaulted;
|
|
|
|
BOOL bRes = GetSecurityDescriptorGroup(m_pSD, &pSid, &bDefaulted);
|
|
|
|
// TMP: Check to make sure the group is not NULL!!!!
|
|
if ( pSid == NULL )
|
|
{
|
|
// DebugBreak();
|
|
ERRORTRACE((LOG_WBEMCORE, "ERROR: Security descriptor has no group\n"));
|
|
}
|
|
|
|
|
|
if (!bRes || !IsValidSid(pSid))
|
|
return 0;
|
|
|
|
return new CNtSid(pSid);
|
|
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::SetGroup
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
BOOL CNtSecurityDescriptor::SetGroup(CNtSid *pSid)
|
|
{
|
|
if (m_dwStatus != NoError || m_pSD == 0)
|
|
return FALSE;
|
|
|
|
// TMP: Check to make sure the group is not NULL!!!!
|
|
if ( pSid->GetPtr() == NULL )
|
|
{
|
|
// DebugBreak();
|
|
ERRORTRACE((LOG_WBEMCORE, "ERROR: Security descriptor is trying to bland out the group!\n"));
|
|
}
|
|
|
|
|
|
|
|
// Since we cannot alter a self-relative SD, we have to make
|
|
// an absolute one, alter it, and then set the current
|
|
// SD based on the absolute one (we keep the self-relative form
|
|
// internally in the m_pSD variable.
|
|
// ============================================================
|
|
|
|
SNtAbsoluteSD *pTmp = GetAbsoluteCopy();
|
|
|
|
if (pTmp == 0)
|
|
return FALSE;
|
|
|
|
BOOL bRes = ::SetSecurityDescriptorGroup(
|
|
pTmp->m_pSD,
|
|
pSid->GetPtr(),
|
|
FALSE
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
delete pTmp;
|
|
return FALSE;
|
|
}
|
|
|
|
bRes = SetFromAbsoluteCopy(pTmp);
|
|
delete pTmp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::HasOwner
|
|
//
|
|
// Determines if a security descriptor has an owner.
|
|
//
|
|
// Return values:
|
|
// SDNotOwned, SDOwned, Failed
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
int CNtSecurityDescriptor::HasOwner()
|
|
{
|
|
if (m_pSD == 0 || m_dwStatus != NoError)
|
|
return Failed;
|
|
|
|
PSID pSid = 0;
|
|
|
|
BOOL bRes = GetSecurityDescriptorOwner(m_pSD, &pSid, 0);
|
|
|
|
if (!bRes || !IsValidSid(pSid))
|
|
return Failed;
|
|
|
|
if (pSid == 0)
|
|
return SDNotOwned;
|
|
|
|
return SDOwned;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::GetOwner
|
|
//
|
|
// Returns the SID of the owner of the Security Descriptor or NULL
|
|
// if an error occurred or there is no owner. Use HasOwner() to
|
|
// determine this.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSid *CNtSecurityDescriptor::GetOwner()
|
|
{
|
|
if (m_pSD == 0 || m_dwStatus != NoError)
|
|
return 0;
|
|
|
|
PSID pSid = 0;
|
|
BOOL bDefaulted;
|
|
|
|
BOOL bRes = GetSecurityDescriptorOwner(m_pSD, &pSid, &bDefaulted);
|
|
|
|
// TMP: Check to make sure the owner is not NULL!!!!
|
|
if ( pSid == NULL )
|
|
{
|
|
// DebugBreak();
|
|
ERRORTRACE((LOG_WBEMCORE, "ERROR: Security descriptor has no owner\n"));
|
|
}
|
|
|
|
|
|
|
|
if (!bRes || !IsValidSid(pSid))
|
|
return 0;
|
|
|
|
return new CNtSid(pSid);
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::SetOwner
|
|
//
|
|
// Sets the owner of a security descriptor.
|
|
//
|
|
// Parameters:
|
|
// <pSid> The SID of the new owner.
|
|
//
|
|
// Return Value:
|
|
// TRUE if owner was changed, FALSE if not.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
BOOL CNtSecurityDescriptor::SetOwner(CNtSid *pSid)
|
|
{
|
|
if (m_pSD == 0 || m_dwStatus != NoError)
|
|
return FALSE;
|
|
|
|
if (!pSid->IsValid())
|
|
return FALSE;
|
|
|
|
// TMP: Check to make sure the owner is not NULL!!!!
|
|
if ( pSid->GetPtr() == NULL )
|
|
{
|
|
ERRORTRACE((LOG_WBEMCORE, "ERROR: Security descriptor is trying to zap the owner!\n"));
|
|
// DebugBreak();
|
|
}
|
|
|
|
|
|
|
|
// We must convert to absolute format to make the change.
|
|
// =======================================================
|
|
|
|
SNtAbsoluteSD *pTmp = GetAbsoluteCopy();
|
|
|
|
if (pTmp == 0)
|
|
return FALSE;
|
|
|
|
BOOL bRes = SetSecurityDescriptorOwner(pTmp->m_pSD, pSid->GetPtr(), FALSE);
|
|
|
|
if (!bRes)
|
|
{
|
|
delete pTmp;
|
|
return FALSE;
|
|
}
|
|
|
|
// If here, we have managed the change, so we have to
|
|
// convert *this back from the temporary absolute SD.
|
|
// ===================================================
|
|
|
|
bRes = SetFromAbsoluteCopy(pTmp);
|
|
delete pTmp;
|
|
|
|
return bRes;
|
|
}
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurityDescriptor::CNtSecurityDescriptor
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CNtSecurityDescriptor::CNtSecurityDescriptor(
|
|
PSECURITY_DESCRIPTOR pSD,
|
|
BOOL bAcquire
|
|
)
|
|
{
|
|
m_pSD = 0;
|
|
m_dwStatus = NullSD;
|
|
|
|
// Ensure that SD is not NULL.
|
|
// ===========================
|
|
|
|
if (pSD == 0)
|
|
{
|
|
if (bAcquire)
|
|
delete pSD;
|
|
return;
|
|
}
|
|
|
|
if (!IsValidSecurityDescriptor(pSD))
|
|
{
|
|
m_dwStatus = InvalidSD;
|
|
if (bAcquire)
|
|
delete pSD;
|
|
return;
|
|
}
|
|
|
|
// Ensure that SD is self-relative
|
|
// ===============================
|
|
|
|
SECURITY_DESCRIPTOR_CONTROL ctrl;
|
|
DWORD dwRev;
|
|
|
|
BOOL bRes = GetSecurityDescriptorControl(
|
|
pSD,
|
|
&ctrl,
|
|
&dwRev
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
m_dwStatus = InvalidSD;
|
|
if (bAcquire)
|
|
delete pSD;
|
|
return;
|
|
}
|
|
|
|
if ((ctrl & SE_SELF_RELATIVE) == 0)
|
|
{
|
|
// If here, we have to conver the SD to self-relative form.
|
|
// ========================================================
|
|
|
|
DWORD dwRequired = 0;
|
|
|
|
bRes = MakeSelfRelativeSD(
|
|
pSD,
|
|
m_pSD,
|
|
&dwRequired
|
|
);
|
|
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
m_dwStatus = InvalidSD;
|
|
if (bAcquire)
|
|
delete pSD;
|
|
return;
|
|
}
|
|
|
|
m_pSD = new BYTE[dwRequired];
|
|
if (!m_pSD)
|
|
{
|
|
m_dwStatus = InvalidSD;
|
|
if (bAcquire)
|
|
delete pSD;
|
|
return;
|
|
}
|
|
|
|
ZeroMemory(m_pSD, dwRequired);
|
|
|
|
bRes = MakeSelfRelativeSD(
|
|
pSD,
|
|
m_pSD,
|
|
&dwRequired
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
m_dwStatus = InvalidSD;
|
|
if (bAcquire)
|
|
delete pSD;
|
|
return;
|
|
}
|
|
|
|
m_dwStatus = NoError;
|
|
return;
|
|
}
|
|
|
|
|
|
// If here, the SD was already self-relative.
|
|
// ==========================================
|
|
|
|
if (bAcquire)
|
|
m_pSD = pSD;
|
|
else
|
|
{
|
|
DWORD dwRes = GetSecurityDescriptorLength(pSD);
|
|
m_pSD = new BYTE[dwRes];
|
|
if (!m_pSD)
|
|
{
|
|
m_dwStatus = InvalidSD;
|
|
return;
|
|
}
|
|
|
|
ZeroMemory(m_pSD, dwRes);
|
|
memcpy(m_pSD, pSD, dwRes);
|
|
}
|
|
|
|
m_dwStatus = NoError;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurity::DumpPrivileges
|
|
//
|
|
// Dumps current process token privileges to the console.
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL CNtSecurity::DumpPrivileges()
|
|
{
|
|
HANDLE hToken = 0;
|
|
TOKEN_INFORMATION_CLASS tki;
|
|
BOOL bRes;
|
|
LPVOID pTokenInfo = 0;
|
|
DWORD dwRequiredBytes;
|
|
BOOL bRetVal = FALSE;
|
|
TOKEN_PRIVILEGES *pPriv = 0;
|
|
TCHAR *pName = 0;
|
|
DWORD dwIndex;
|
|
DWORD dwLastError;
|
|
|
|
_tprintf(__TEXT("--- Current Token Privilege Dump ---\n"));
|
|
|
|
// Starting point: open the process token.
|
|
// =======================================
|
|
|
|
bRes = OpenProcessToken(
|
|
GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&hToken
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
_tprintf(__TEXT("Unable to open process token\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
// Query for privileges.
|
|
// =====================
|
|
|
|
tki = TokenPrivileges;
|
|
|
|
bRes = GetTokenInformation(
|
|
hToken,
|
|
tki,
|
|
pTokenInfo,
|
|
0,
|
|
&dwRequiredBytes
|
|
);
|
|
|
|
dwLastError = GetLastError();
|
|
|
|
if (dwLastError != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
printf("Unable to get buffer size for token information\n");
|
|
goto Exit;
|
|
}
|
|
|
|
pTokenInfo = new BYTE[dwRequiredBytes];
|
|
if (!pTokenInfo)
|
|
goto Exit;
|
|
ZeroMemory(pTokenInfo, dwRequiredBytes);
|
|
|
|
bRes = GetTokenInformation(
|
|
hToken,
|
|
tki,
|
|
pTokenInfo,
|
|
dwRequiredBytes,
|
|
&dwRequiredBytes
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
printf("Unable to query token\n");
|
|
goto Exit;
|
|
}
|
|
|
|
// Loop through the privileges.
|
|
// ============================
|
|
|
|
pPriv = (TOKEN_PRIVILEGES *) pTokenInfo;
|
|
|
|
for (dwIndex = 0; dwIndex < pPriv->PrivilegeCount; dwIndex++)
|
|
{
|
|
pName = 0;
|
|
dwRequiredBytes = 0;
|
|
|
|
// Find the buffer size required for the name.
|
|
// ===========================================
|
|
|
|
bRes = LookupPrivilegeName(
|
|
0, // System name
|
|
&pPriv->Privileges[dwIndex].Luid,
|
|
pName,
|
|
&dwRequiredBytes
|
|
);
|
|
|
|
dwLastError = GetLastError();
|
|
|
|
if (dwLastError != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
printf("Failed to find privilege name\n");
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate enough space to hold the privilege name.
|
|
// =================================================
|
|
|
|
pName = (TCHAR *) new BYTE[dwRequiredBytes];
|
|
if(pName == NULL)
|
|
goto Exit;
|
|
|
|
ZeroMemory(pName, dwRequiredBytes);
|
|
|
|
bRes = LookupPrivilegeName(
|
|
0, // System name
|
|
&pPriv->Privileges[dwIndex].Luid,
|
|
pName,
|
|
&dwRequiredBytes
|
|
);
|
|
|
|
printf("%s ", pName);
|
|
delete pName;
|
|
|
|
// Determine the privilege 'status'.
|
|
// =================================
|
|
|
|
if (pPriv->Privileges[dwIndex].Attributes & SE_PRIVILEGE_ENABLED)
|
|
printf("<ENABLED> ");
|
|
if (pPriv->Privileges[dwIndex].Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT)
|
|
printf("<ENABLED BY DEFAULT> ");
|
|
if (pPriv->Privileges[dwIndex].Attributes & SE_PRIVILEGE_USED_FOR_ACCESS)
|
|
printf("<USED FOR ACCESS> ");
|
|
|
|
printf("\n");
|
|
|
|
pName = 0;
|
|
}
|
|
|
|
printf("--- End Privilege Dump ---\n");
|
|
|
|
bRetVal = TRUE;
|
|
|
|
Exit:
|
|
if (pTokenInfo)
|
|
delete pTokenInfo;
|
|
if (hToken)
|
|
CloseHandle(hToken);
|
|
return bRetVal;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurity::SetPrivilege
|
|
//
|
|
// Ensures a given privilege is enabled.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// <pszPrivilegeName> One of the SE_ constants defined in WINNT.H for
|
|
// privilege names. Example: SE_SECURITY_NAME
|
|
// <bEnable> If TRUE, the privilege will be enabled. If FALSE,
|
|
// the privilege will be disabled.
|
|
//
|
|
// Return value:
|
|
// TRUE if the privilege was enabled, FALSE if not.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
BOOL CNtSecurity::SetPrivilege(
|
|
TCHAR *pszPrivilegeName, // An SE_ value.
|
|
BOOL bEnable // TRUE=enable, FALSE=disable
|
|
)
|
|
{
|
|
HANDLE hToken = 0;
|
|
TOKEN_PRIVILEGES tkp;
|
|
LUID priv;
|
|
BOOL bRes;
|
|
BOOL bRetVal = FALSE;
|
|
|
|
bRes = OpenProcessToken(
|
|
GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&hToken
|
|
);
|
|
|
|
if (!bRes)
|
|
goto Exit;
|
|
|
|
// Locate the privilege LUID based on the requested name.
|
|
// ======================================================
|
|
|
|
bRes = LookupPrivilegeValue(
|
|
0, // system name, 0=local
|
|
pszPrivilegeName,
|
|
&priv
|
|
);
|
|
|
|
if (!bRes)
|
|
goto Exit;
|
|
|
|
// We now have the LUID. Next, we build up the privilege
|
|
// setting based on the user-specified <bEnable>.
|
|
// ======================================================
|
|
|
|
tkp.PrivilegeCount = 1;
|
|
tkp.Privileges[0].Luid = priv;
|
|
|
|
if (bEnable)
|
|
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
else
|
|
tkp.Privileges[0].Attributes = 0;
|
|
|
|
// Do it.
|
|
// ======
|
|
|
|
bRes = AdjustTokenPrivileges(
|
|
hToken,
|
|
FALSE,
|
|
&tkp,
|
|
sizeof(TOKEN_PRIVILEGES),
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (!bRes)
|
|
goto Exit;
|
|
|
|
bRetVal = TRUE;
|
|
|
|
Exit:
|
|
if (hToken)
|
|
CloseHandle(hToken);
|
|
return bRetVal;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurity::GetFileSD
|
|
//
|
|
// Gets the complete security descriptor for file or directory on NT systems.
|
|
//
|
|
// Parameters:
|
|
// <pszFile> The path to the file or directory.
|
|
//
|
|
// <SecInfo> The information which will be manipulated. See
|
|
// SECURITY_INFORMATION in NT SDK documentation.
|
|
//
|
|
// <pReturnedSD> Receives a pointer to the CNtSecurityDecriptor object
|
|
// which represents security on the file. The caller
|
|
// becomes onwer of the object, which must be deallocated
|
|
// with operator delete.
|
|
//
|
|
// The returned object which is a copy of the
|
|
// underlying security descriptor. Changes to the returned
|
|
// object are not propagated to the file. SetFileSD must
|
|
// be used to do this.
|
|
//
|
|
// This will be set to point to NULL on error.
|
|
//
|
|
// Return value:
|
|
// NoError, NotFound, AccessDenied, Failed
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
int CNtSecurity::GetFileSD(
|
|
IN TCHAR *pszFile,
|
|
IN SECURITY_INFORMATION SecInfo,
|
|
OUT CNtSecurityDescriptor **pReturnedSD
|
|
)
|
|
{
|
|
// First, verify that the file/dir exists.
|
|
// =======================================
|
|
|
|
#ifdef _UNICODE
|
|
int nRes = _waccess(pszFile, 0);
|
|
#else
|
|
int nRes = _access(pszFile, 0);
|
|
#endif
|
|
|
|
if (nRes != 0)
|
|
{
|
|
if (errno == ENOENT)
|
|
return NotFound;
|
|
if (errno == EACCES)
|
|
return AccessDenied;
|
|
if (nRes == -1) // Other errors
|
|
return Failed;
|
|
}
|
|
|
|
// If here, we think we can play with it.
|
|
// ======================================
|
|
|
|
PSECURITY_DESCRIPTOR pSD = 0;
|
|
DWORD dwRequiredBytes;
|
|
BOOL bRes;
|
|
DWORD dwLastError;
|
|
|
|
*pReturnedSD = 0;
|
|
|
|
// Call once first to get the required buffer sizes.
|
|
// =================================================
|
|
|
|
bRes = GetFileSecurity(
|
|
pszFile,
|
|
SecInfo,
|
|
pSD,
|
|
0,
|
|
&dwRequiredBytes
|
|
);
|
|
|
|
dwLastError = GetLastError();
|
|
|
|
if (dwLastError != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
// Analyze the error
|
|
|
|
return Failed;
|
|
}
|
|
|
|
// Now call again with a buffer large enough to hold the SD.
|
|
// =========================================================
|
|
|
|
pSD = (PSECURITY_DESCRIPTOR) new BYTE[dwRequiredBytes];
|
|
if(pSD == NULL)
|
|
return Failed;
|
|
|
|
ZeroMemory(pSD, dwRequiredBytes);
|
|
|
|
bRes = GetFileSecurity(
|
|
pszFile,
|
|
SecInfo,
|
|
pSD,
|
|
dwRequiredBytes,
|
|
&dwRequiredBytes
|
|
);
|
|
|
|
if (!bRes)
|
|
{
|
|
delete pSD;
|
|
return Failed;
|
|
}
|
|
|
|
// If here, we have a security descriptor.
|
|
// =======================================
|
|
|
|
CNtSecurityDescriptor *pNewSD = new CNtSecurityDescriptor(pSD, TRUE);
|
|
if(pNewSD == NULL)
|
|
{
|
|
delete pSD;
|
|
return Failed;
|
|
}
|
|
|
|
*pReturnedSD = pNewSD;
|
|
|
|
return NoError;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurity::GetRegSD
|
|
//
|
|
// Retrieves the security descriptor for a registry key.
|
|
//
|
|
// Parameters:
|
|
// <hRoot> The root key (HKEY_LOCAL_MACHINE, etc.)
|
|
// <pszSubKey> The subkey under the root key.
|
|
// <SecInfo> The information which will be manipulated. See
|
|
// SECURITY_INFORMATION in NT SDK documentation.
|
|
// <pSD> Receives the pointer to the security descriptor if
|
|
// no error occurs. Caller must use operator delete.
|
|
//
|
|
// Return value:
|
|
// NoError, NotFound, AccessDenied, Failed
|
|
//
|
|
//***************************************************************************
|
|
int CNtSecurity::GetRegSD(
|
|
IN HKEY hRoot,
|
|
IN TCHAR *pszSubKey,
|
|
IN SECURITY_INFORMATION SecInfo,
|
|
OUT CNtSecurityDescriptor **pSD
|
|
)
|
|
{
|
|
HKEY hKey;
|
|
*pSD = 0;
|
|
|
|
ACCESS_MASK amAccess = KEY_ALL_ACCESS;
|
|
if (SecInfo & SACL_SECURITY_INFORMATION)
|
|
amAccess |= ACCESS_SYSTEM_SECURITY;
|
|
|
|
LONG lRes = RegOpenKeyEx(hRoot, pszSubKey, 0, amAccess, &hKey);
|
|
|
|
if (lRes == ERROR_ACCESS_DENIED)
|
|
return AccessDenied;
|
|
|
|
if (lRes != ERROR_SUCCESS)
|
|
return Failed;
|
|
|
|
// If here, the key is open. Now we try to get the security descriptor.
|
|
// =====================================================================
|
|
|
|
PSECURITY_DESCRIPTOR pTmpSD = 0;
|
|
DWORD dwRequired = 0;
|
|
|
|
// Determine the buffer size required.
|
|
// ===================================
|
|
|
|
lRes = RegGetKeySecurity(hKey, SecInfo, pTmpSD, &dwRequired);
|
|
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
RegCloseKey(hKey);
|
|
return Failed;
|
|
}
|
|
|
|
// Allocate room for the SD and get it.
|
|
// ====================================
|
|
pTmpSD = new BYTE[dwRequired];
|
|
if (!pTmpSD)
|
|
{
|
|
RegCloseKey(hKey);
|
|
return Failed;
|
|
}
|
|
|
|
ZeroMemory(pTmpSD, dwRequired);
|
|
|
|
lRes = RegGetKeySecurity(hKey, SecInfo, pTmpSD, &dwRequired);
|
|
|
|
if (lRes != 0 || !IsValidSecurityDescriptor(pTmpSD))
|
|
{
|
|
delete pTmpSD;
|
|
RegCloseKey(hKey);
|
|
return Failed;
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
CNtSecurityDescriptor *pNewSD = new CNtSecurityDescriptor(pTmpSD, TRUE);
|
|
if(pNewSD == NULL)
|
|
{
|
|
delete pTmpSD;
|
|
return Failed;
|
|
}
|
|
|
|
*pSD = pNewSD;
|
|
|
|
return NoError;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurity::SetRegSD
|
|
//
|
|
// Sets the security descriptor for a registry key.
|
|
//
|
|
// Parameters:
|
|
// <hRoot> The root key (HKEY_LOCAL_MACHINE, etc.)
|
|
// <pszSubKey> The subkey under the root key.
|
|
// <SecInfo> The information which will be manipulated. See
|
|
// SECURITY_INFORMATION in NT SDK documentation.
|
|
// <pSD> The read-only pointer to the new security descriptor.
|
|
//
|
|
// Return value:
|
|
// NoError, NotFound, AccessDenied, Failed
|
|
//
|
|
//***************************************************************************
|
|
int CNtSecurity::SetRegSD(
|
|
IN HKEY hRoot,
|
|
IN TCHAR *pszSubKey,
|
|
IN SECURITY_INFORMATION SecInfo,
|
|
IN CNtSecurityDescriptor *pSD
|
|
)
|
|
{
|
|
HKEY hKey;
|
|
|
|
if (!pSD->IsValid())
|
|
return Failed;
|
|
|
|
ACCESS_MASK amAccess = KEY_ALL_ACCESS;
|
|
if (SecInfo & SACL_SECURITY_INFORMATION)
|
|
amAccess |= ACCESS_SYSTEM_SECURITY;
|
|
|
|
LONG lRes = RegOpenKeyEx(hRoot, pszSubKey, 0, amAccess, &hKey);
|
|
|
|
if (lRes == ERROR_ACCESS_DENIED)
|
|
return AccessDenied;
|
|
|
|
if (lRes != ERROR_SUCCESS)
|
|
return Failed;
|
|
|
|
// If here, the key is open. Now we try to get the security descriptor.
|
|
// =====================================================================
|
|
|
|
PSECURITY_DESCRIPTOR pTmpSD = 0;
|
|
DWORD dwRequired = 0;
|
|
|
|
// Determine the buffer size required.
|
|
// ===================================
|
|
|
|
lRes = RegSetKeySecurity(hKey, SecInfo, pSD->GetPtr());
|
|
|
|
if (lRes != 0)
|
|
{
|
|
RegCloseKey(hKey);
|
|
return Failed;
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
return NoError;
|
|
}
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurity::SetFileSD
|
|
//
|
|
// Sets the security descriptor for a file or directory.
|
|
//
|
|
// Parameters:
|
|
// <pszFile> The file/dir for which to set security.
|
|
// <SecInfo> The information which will be manipulated. See
|
|
// SECURITY_INFORMATION in NT SDK documentation.
|
|
// <pSD> Pointer to a valid CNtSecurityDescriptor
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
BOOL CNtSecurity::SetFileSD(
|
|
IN TCHAR *pszFile,
|
|
IN SECURITY_INFORMATION SecInfo,
|
|
IN CNtSecurityDescriptor *pSD
|
|
)
|
|
{
|
|
// First, verify that the file/dir exists.
|
|
// =======================================
|
|
#ifdef _UNICODE
|
|
int nRes = _waccess(pszFile, 0);
|
|
#else
|
|
int nRes = _access(pszFile, 0);
|
|
#endif
|
|
|
|
if (nRes != 0)
|
|
return FALSE;
|
|
|
|
// Verify the SD is good.
|
|
// ======================
|
|
|
|
if (pSD->GetStatus() != NoError)
|
|
return FALSE;
|
|
|
|
BOOL bRes = ::SetFileSecurity(
|
|
pszFile,
|
|
SecInfo,
|
|
pSD->GetPtr()
|
|
);
|
|
|
|
return bRes;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurity::GetDCName
|
|
//
|
|
// Determines the domain controller for a given domain name.
|
|
//
|
|
// Parameters:
|
|
// <pszDomain> The domain name for which to find the controller.
|
|
// <pszDC> Receives a pointer to the DC name. Deallocate with
|
|
// operator delete.
|
|
// <pszServer> Optional remote helper server on which to execute
|
|
// the query. Defaults to NULL, which typically
|
|
// succeeds.
|
|
//
|
|
// Return value:
|
|
// NoError, NotFound, InvalidName
|
|
//
|
|
//***************************************************************************
|
|
/*
|
|
int CNtSecurity::GetDCName(
|
|
IN LPWSTR pszDomain,
|
|
OUT LPWSTR *pszDC,
|
|
IN LPWSTR pszServer
|
|
)
|
|
{
|
|
LPBYTE pBuf;
|
|
NET_API_STATUS Status;
|
|
|
|
Status = NetGetDCName(pszServer, pszDomain, &pBuf);
|
|
|
|
if (Status == NERR_DCNotFound)
|
|
return NotFound;
|
|
|
|
if (Status == ERROR_INVALID_NAME)
|
|
return InvalidName;
|
|
|
|
LPWSTR pRetStr = new wchar_t[wcslen(LPWSTR(pBuf)) + 1];
|
|
wcscpy(pRetStr, LPWSTR(pBuf));
|
|
NetApiBufferFree(pBuf);
|
|
|
|
*pszDC = pRetStr;
|
|
return NoError;
|
|
}
|
|
*/
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurity::IsUserInGroup2
|
|
//
|
|
// Determines if the use belongs to a particular NTLM group by checking the
|
|
// group list in the access token. This may be a better way than the
|
|
// current implementation.
|
|
//
|
|
// Parameters:
|
|
// <hToken> The user's access token.
|
|
// <Sid> Object containing the sid of the group being tested.
|
|
//
|
|
// Return value:
|
|
// TRUE if the user belongs to the group.
|
|
//
|
|
//***************************************************************************
|
|
/*
|
|
BOOL CNtSecurity::IsUserInGroup2(
|
|
HANDLE hAccessToken,
|
|
CNtSid & Sid)
|
|
{
|
|
if(!IsNT() || hAccessToken == NULL)
|
|
return FALSE; // No point in further testing
|
|
|
|
DWORD dwErr;
|
|
|
|
// Obtain and the groups from token. Start off by determining how much
|
|
// memory is required.
|
|
|
|
TOKEN_GROUPS Groups;
|
|
DWORD dwLen = 0;
|
|
GetTokenInformation(hAccessToken, TokenGroups, &Groups, sizeof(Groups), &dwLen);
|
|
if(dwLen == 0)
|
|
return FALSE;
|
|
|
|
// Load up the group list
|
|
|
|
int BUFFER_SIZE = dwLen;
|
|
BYTE * byteBuffer = new BYTE[BUFFER_SIZE];
|
|
if(byteBuffer == NULL)
|
|
return FALSE;
|
|
DWORD dwSizeRequired = 0;
|
|
BOOL bResult = GetTokenInformation( hAccessToken,
|
|
TokenGroups,
|
|
(void *) byteBuffer,
|
|
BUFFER_SIZE,
|
|
&dwSizeRequired );
|
|
if ( !bResult ) {
|
|
delete [] byteBuffer;
|
|
dwErr = GetLastError();
|
|
return ( FALSE );
|
|
}
|
|
|
|
// Loop through the group list looking for a match
|
|
|
|
BOOL bFound = FALSE;
|
|
PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS) byteBuffer;
|
|
for ( unsigned i = 0; i < pGroups->GroupCount; i++ )
|
|
{
|
|
CNtSid test(pGroups->Groups[i].Sid);
|
|
if(test == Sid)
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
delete [] byteBuffer;
|
|
return bFound;
|
|
}*/
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurity::IsUserInGroup
|
|
//
|
|
// Determines if the use belongs to a particular NTLM group.
|
|
//
|
|
// Parameters:
|
|
// <hToken> The user's access token.
|
|
// <Sid> Object containing the sid of the group being tested.
|
|
//
|
|
// Return value:
|
|
// TRUE if the user belongs to the group.
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL CNtSecurity::IsUserInGroup(
|
|
HANDLE hAccessToken,
|
|
CNtSid & Sid)
|
|
{
|
|
if(!IsNT() || hAccessToken == NULL)
|
|
return FALSE; // No point in further testing
|
|
|
|
// create a security descriptor with a single entry which
|
|
// is the group in question.
|
|
|
|
CNtAce ace(1,ACCESS_ALLOWED_ACE_TYPE,0,Sid);
|
|
if(ace.GetStatus() != 0)
|
|
return FALSE;
|
|
|
|
CNtAcl acl;
|
|
acl.AddAce(&ace);
|
|
CNtSecurityDescriptor sd;
|
|
sd.SetDacl(&acl);
|
|
sd.SetGroup(&Sid); // Access check doesnt really care what you put, so long as you
|
|
// put something for the owner
|
|
sd.SetOwner(&Sid);
|
|
|
|
GENERIC_MAPPING map;
|
|
map.GenericRead = 1;
|
|
map.GenericWrite = 0;
|
|
map.GenericExecute = 0;
|
|
map.GenericAll = 0;
|
|
PRIVILEGE_SET ps[10];
|
|
DWORD dwSize = 10 * sizeof(PRIVILEGE_SET);
|
|
|
|
|
|
DWORD dwGranted;
|
|
BOOL bResult;
|
|
|
|
BOOL bOK = ::AccessCheck(sd.GetPtr(), hAccessToken, 1, &map, ps, &dwSize, &dwGranted, &bResult);
|
|
DWORD dwErr = GetLastError();
|
|
if(bOK && bResult)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurity::DoesGroupExist
|
|
//
|
|
// Determines if a group exists.
|
|
//
|
|
// Return value:
|
|
// TRUE if the group exists
|
|
//
|
|
//***************************************************************************
|
|
|
|
bool CNtSecurity::DoesLocalGroupExist(
|
|
LPWSTR pwszGroup,
|
|
LPWSTR pwszMachine)
|
|
{
|
|
bool bRet = false;
|
|
HINSTANCE hAPI = LoadLibraryEx(__TEXT("netapi32"), NULL, 0);
|
|
if(hAPI)
|
|
{
|
|
NET_API_STATUS (NET_API_FUNCTION *pfnGetInfo)(LPWSTR , LPWSTR ,DWORD , LPBYTE *);
|
|
(FARPROC&)pfnGetInfo = GetProcAddress(hAPI, "NetLocalGroupGetInfo");
|
|
long lRes;
|
|
if(pfnGetInfo)
|
|
{
|
|
LOCALGROUP_INFO_1 * info;
|
|
|
|
lRes = pfnGetInfo(pwszMachine, pwszGroup, 1, (LPBYTE *)&info);
|
|
if(lRes == NERR_Success)
|
|
{
|
|
NET_API_STATUS (NET_API_FUNCTION *pfnBufferFree)(LPVOID);
|
|
(FARPROC&)pfnBufferFree = GetProcAddress(hAPI, "NetApiBufferFree");
|
|
if(pfnBufferFree)
|
|
pfnBufferFree(info);
|
|
|
|
bRet = true;
|
|
}
|
|
}
|
|
FreeLibrary(hAPI);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNtSecurity::AddLocalGroup
|
|
//
|
|
// Determines if a group exists.
|
|
//
|
|
// Return value:
|
|
// TRUE if the group exists
|
|
//
|
|
//***************************************************************************
|
|
|
|
bool CNtSecurity::AddLocalGroup(LPWSTR pwszGroupName, LPWSTR pwszGroupDescription)
|
|
{
|
|
bool bRet = false;
|
|
HINSTANCE hAPI = LoadLibraryEx(__TEXT("netapi32"), NULL, 0);
|
|
if(hAPI)
|
|
{
|
|
LOCALGROUP_INFO_1 info;
|
|
info.lgrpi1_name = pwszGroupName;
|
|
info.lgrpi1_comment = pwszGroupDescription;
|
|
NET_API_STATUS (*pfnLocalAdd)(LPWSTR ,DWORD , LPBYTE ,LPDWORD);
|
|
|
|
(FARPROC&)pfnLocalAdd = GetProcAddress(hAPI, "NetLocalGroupAdd");
|
|
if(pfnLocalAdd)
|
|
bRet = (pfnLocalAdd(NULL, 1, (LPBYTE)&info, NULL) == NERR_Success);
|
|
FreeLibrary(hAPI);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
//***************************************************************************
|
|
void ChangeSecurity(CNtSecurityDescriptor *pSD)
|
|
{
|
|
CNtAcl Acl;
|
|
|
|
|
|
ACCESS_MASK Mask = FULL_CONTROL;
|
|
|
|
|
|
CNtSid Sid(L"Everyone", 0);
|
|
CNtAce Ace(
|
|
Mask,
|
|
ACCESS_ALLOWED_ACE_TYPE,
|
|
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
|
|
Sid);
|
|
|
|
if (Ace.GetStatus() != CNtAce::NoError)
|
|
{
|
|
printf("Bad ACE\n");
|
|
return;
|
|
}
|
|
|
|
CNtAce Ace2(Ace);
|
|
CNtAce Ace3;
|
|
Ace3 = Ace2;
|
|
|
|
Acl.AddAce(&Ace3);
|
|
|
|
CNtAcl Acl2(Acl);
|
|
CNtAcl Acl3;
|
|
Acl3 = Acl2;
|
|
|
|
pSD->SetDacl(&Acl);
|
|
|
|
CNtSecurityDescriptor SD2(*pSD);
|
|
CNtSecurityDescriptor SD3;
|
|
SD3.SetDacl(&Acl3);
|
|
|
|
SD3 = SD2;
|
|
|
|
*pSD = SD3;
|
|
|
|
CNtSid *pOwner = pSD->GetOwner();
|
|
|
|
if (pOwner)
|
|
pSD->SetOwner(pOwner);
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
//***************************************************************************
|
|
|
|
|
|
|
|
void SidTest(char *pUser, char *pMachine)
|
|
{
|
|
wchar_t User[128], Mach[128];
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, pUser, -1, User, 128);
|
|
MultiByteToWideChar(CP_ACP, 0, pMachine, -1, Mach, 128);
|
|
|
|
printf("------SID TEST----------\n");
|
|
|
|
LPWSTR pMach2 = 0;
|
|
if (pMachine)
|
|
pMach2 = Mach;
|
|
|
|
CNtSid TseSid(User, pMach2);
|
|
|
|
printf("TseSid status = %d\n", TseSid.GetStatus());
|
|
|
|
TseSid.Dump();
|
|
}
|
|
|
|
|
|
void TestRegSec()
|
|
{
|
|
|
|
CNtSecurityDescriptor *pSD = 0;
|
|
|
|
int nRes = CNtSecurity::GetRegSD(HKEY_LOCAL_MACHINE,WBEM_REG_WBEM,
|
|
DACL_SECURITY_INFORMATION, &pSD);
|
|
|
|
printf("----------------BEGIN SECURITY KEY DUMP-------------\n");
|
|
pSD->Dump();
|
|
printf("----------------END SECURITY KEY DUMP-------------\n");
|
|
|
|
if (pSD->IsValid())
|
|
nRes = CNtSecurity::SetRegSD(HKEY_LOCAL_MACHINE, WBEM_REG_WBEM,
|
|
DACL_SECURITY_INFORMATION, pSD);
|
|
}
|
|
|
|
/*
|
|
void main(int argc, char **argv)
|
|
{
|
|
BOOL bRes;
|
|
|
|
printf("Test\n");
|
|
|
|
if (argc < 2)
|
|
return;
|
|
|
|
bRes = CNtSecurity::SetPrivilege(SE_SECURITY_NAME, TRUE);
|
|
|
|
CNtSecurity::DumpPrivileges();
|
|
|
|
CNtSecurityDescriptor *pSD = 0;
|
|
|
|
int nRes = CNtSecurity::GetFileSD(argv[1], DACL_SECURITY_INFORMATION, &pSD);
|
|
|
|
if (nRes == CNtSecurity::NotFound)
|
|
{
|
|
printf("No such file/dir\n");
|
|
return;
|
|
}
|
|
|
|
if (nRes != 0)
|
|
{
|
|
printf("Cannot get security descriptor. Last=%d\n", GetLastError());
|
|
}
|
|
|
|
pSD->Dump();
|
|
|
|
|
|
delete pSD;
|
|
}
|
|
*/
|
|
//***************************************************************************
|
|
//
|
|
// FIsRunningAsService
|
|
//
|
|
// Purpose:
|
|
// Determines if the current process is running as a service.
|
|
//
|
|
// Returns:
|
|
// FALSE if running interactively
|
|
// TRUE if running as a service.
|
|
//
|
|
//***************************************************************************
|
|
|
|
/*BOOL FIsRunningAsService(VOID)
|
|
{
|
|
HWINSTA hws = GetProcessWindowStation();
|
|
if(hws == NULL)
|
|
return TRUE;
|
|
|
|
DWORD LengthNeeded;
|
|
|
|
BOOL bService = FALSE;
|
|
USEROBJECTFLAGS fl;
|
|
if(GetUserObjectInformation(hws, UOI_FLAGS, &fl, sizeof(USEROBJECTFLAGS), &LengthNeeded))
|
|
if(fl.dwFlags & WSF_VISIBLE)
|
|
bService = FALSE;
|
|
else
|
|
bService = TRUE;
|
|
CloseWindowStation(hws);
|
|
return bService;
|
|
}*/
|
|
|
|
|
|
C9XAce::C9XAce(
|
|
ACCESS_MASK Mask,
|
|
DWORD AceType,
|
|
DWORD dwAceFlags,
|
|
LPWSTR pUser
|
|
)
|
|
{
|
|
m_wszFullName = NULL;
|
|
if(pUser)
|
|
m_wszFullName = Macro_CloneLPWSTR(pUser);
|
|
m_dwAccess = Mask;
|
|
m_iFlags = dwAceFlags;
|
|
m_iType = AceType;
|
|
}
|
|
|
|
C9XAce::~C9XAce()
|
|
{
|
|
if(m_wszFullName)
|
|
delete [] m_wszFullName;
|
|
}
|
|
|
|
HRESULT C9XAce::GetFullUserName(WCHAR * pBuff, DWORD dwSize)
|
|
{
|
|
if(pBuff && m_wszFullName)
|
|
{
|
|
wcsncpy(pBuff, m_wszFullName, dwSize-1);
|
|
pBuff[dwSize-1] = 0;
|
|
return S_OK;
|
|
}
|
|
return WBEM_E_FAILED;
|
|
}
|
|
|
|
HRESULT C9XAce::GetFullUserName2(WCHAR ** pBuff)
|
|
{
|
|
if(wcslen(m_wszFullName) < 1)
|
|
return WBEM_E_FAILED;
|
|
|
|
int iLen = wcslen(m_wszFullName)+4;
|
|
*pBuff = new WCHAR[iLen];
|
|
if(*pBuff == NULL)
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
|
|
// there are two possible formats, the first is "UserName", and the
|
|
// second is "domain\username".
|
|
|
|
WCHAR * pSlash;
|
|
for(pSlash = m_wszFullName; *pSlash && *pSlash != L'\\'; pSlash++); // intentional
|
|
|
|
if(*pSlash && pSlash > m_wszFullName)
|
|
{
|
|
// got a domain\user, convert to domain|user
|
|
|
|
wcscpy(*pBuff, m_wszFullName);
|
|
for(pSlash = *pBuff; *pSlash; pSlash++)
|
|
if(*pSlash == L'\\')
|
|
{
|
|
*pSlash = L'|';
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// got a "user", convert to ".|user"
|
|
|
|
wcscpy(*pBuff, L".|");
|
|
wcscat(*pBuff, m_wszFullName);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// C9XAce::GetSerializedSize
|
|
//
|
|
// Returns the number of bytes needed to store this
|
|
//
|
|
//***************************************************************************
|
|
|
|
DWORD C9XAce::GetSerializedSize()
|
|
{
|
|
if (m_wszFullName == 0 || wcslen(m_wszFullName) == 0)
|
|
return 0;
|
|
return 2 * (wcslen(m_wszFullName) + 1) + 12;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// C9XAce::Serialize
|
|
//
|
|
// Serializes the ace. The serialized version will consist of
|
|
// <DOMAIN\USERNAME LPWSTR><FLAGS><TYPE><MASK>
|
|
//
|
|
// Note that the fields are dwords except for the name.
|
|
//
|
|
//***************************************************************************
|
|
|
|
bool C9XAce::Serialize(BYTE * pData)
|
|
{
|
|
wcscpy((LPWSTR)pData, m_wszFullName);
|
|
pData += 2*(wcslen(m_wszFullName) + 1);
|
|
DWORD * pdwData = (DWORD *)pData;
|
|
*pdwData = m_iFlags;
|
|
pdwData++;
|
|
*pdwData = m_iType;
|
|
pdwData++;
|
|
*pdwData = m_dwAccess;
|
|
pdwData++;
|
|
return true;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// C9XAce::Deserialize
|
|
//
|
|
// Deserializes the ace. See the comments for Serialize for comments.
|
|
//
|
|
//***************************************************************************
|
|
|
|
bool C9XAce::Deserialize(BYTE * pData)
|
|
{
|
|
m_wszFullName = new WCHAR[wcslen((LPWSTR)pData) + 1];
|
|
if (!m_wszFullName)
|
|
return false;
|
|
|
|
wcscpy(m_wszFullName, (LPWSTR)pData);
|
|
pData += 2*(wcslen(m_wszFullName) + 1);
|
|
|
|
DWORD * pdwData = (DWORD *)pData;
|
|
|
|
m_iFlags = *pdwData;
|
|
pdwData++;
|
|
m_iType = *pdwData;
|
|
pdwData++;
|
|
m_dwAccess = *pdwData;
|
|
pdwData++;
|
|
return true;
|
|
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// BOOL SetObjectAccess2
|
|
//
|
|
// DESCRIPTION:
|
|
//
|
|
// Adds read/open and set access for the everyone group to an object.
|
|
//
|
|
// PARAMETERS:
|
|
//
|
|
// hObj Object to set access on.
|
|
//
|
|
// RETURN VALUE:
|
|
//
|
|
// Returns TRUE if OK.
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL SetObjectAccess2(IN HANDLE hObj)
|
|
{
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
DWORD dwLastErr = 0;
|
|
BOOL bRet = FALSE;
|
|
|
|
// no point if we arnt on nt
|
|
|
|
if(!IsNT())
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// figure out how much space to allocate
|
|
|
|
DWORD dwSizeNeeded;
|
|
bRet = GetKernelObjectSecurity(
|
|
hObj, // handle of object to query
|
|
DACL_SECURITY_INFORMATION, // requested information
|
|
pSD, // address of security descriptor
|
|
0, // size of buffer for security descriptor
|
|
&dwSizeNeeded); // address of required size of buffer
|
|
|
|
if(bRet == TRUE || (ERROR_INSUFFICIENT_BUFFER != GetLastError()))
|
|
return FALSE;
|
|
|
|
pSD = new BYTE[dwSizeNeeded];
|
|
if(pSD == NULL)
|
|
return FALSE;
|
|
|
|
// Get the data
|
|
|
|
bRet = GetKernelObjectSecurity(
|
|
hObj, // handle of object to query
|
|
DACL_SECURITY_INFORMATION, // requested information
|
|
pSD, // address of security descriptor
|
|
dwSizeNeeded, // size of buffer for security descriptor
|
|
&dwSizeNeeded ); // address of required size of buffer
|
|
if(bRet == FALSE)
|
|
{
|
|
delete pSD;
|
|
return FALSE;
|
|
}
|
|
|
|
// move it into object for
|
|
|
|
CNtSecurityDescriptor sd(pSD,TRUE); // Acquires ownership of the memory
|
|
if(sd.GetStatus() != 0)
|
|
return FALSE;
|
|
CNtAcl acl;
|
|
if(!sd.GetDacl(acl))
|
|
return FALSE;
|
|
|
|
// Create an everyone ace
|
|
|
|
PSID pRawSid;
|
|
SID_IDENTIFIER_AUTHORITY id2 = SECURITY_WORLD_SID_AUTHORITY;;
|
|
|
|
if(AllocateAndInitializeSid( &id2, 1,
|
|
0,0,0,0,0,0,0,0,&pRawSid))
|
|
{
|
|
CNtSid SidUsers(pRawSid);
|
|
FreeSid(pRawSid);
|
|
CNtAce * pace = new CNtAce(EVENT_MODIFY_STATE | SYNCHRONIZE, ACCESS_ALLOWED_ACE_TYPE, 0
|
|
, SidUsers);
|
|
if(pace == NULL)
|
|
return FALSE;
|
|
if( pace->GetStatus() == 0)
|
|
acl.AddAce(pace);
|
|
delete pace;
|
|
|
|
}
|
|
|
|
if(acl.GetStatus() != 0)
|
|
return FALSE;
|
|
sd.SetDacl(&acl);
|
|
bRet = SetKernelObjectSecurity(hObj, DACL_SECURITY_INFORMATION, sd.GetPtr());
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// IsAdmin
|
|
//
|
|
// returns TRUE if we are a member of the admin group or running as
|
|
// NETWORK_SERVICE or running as LOCAL_SERVICE
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL IsAdmin(HANDLE hAccess)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
PSID pRawSid;
|
|
SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY;
|
|
|
|
if(AllocateAndInitializeSid( &id, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
|
|
0,0,0,0,0,0,&pRawSid))
|
|
{
|
|
CNtSid Sid(pRawSid);
|
|
bRet = CNtSecurity::IsUserInGroup(hAccess, Sid);
|
|
|
|
// We're done with this
|
|
FreeSid( pRawSid );
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Lets check for NETWORK_SERVICE and LOCAL_SERVICE since
|
|
// they also want full admin rights
|
|
// COMMENTED OUT DUE TO: 504554
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//if ( !bRet )
|
|
//{
|
|
// bRet = IsNetworkService ( hAccess );
|
|
// if ( !bRet )
|
|
// {
|
|
// bRet = IsLocalService ( hAccess );
|
|
// }
|
|
//}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// IsNetworkService
|
|
//
|
|
// returns TRUE if we are running as NETWORK_SERVICE
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL IsNetworkService ( HANDLE hAccess )
|
|
{
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Construct the NETWORK_SERVICE SID
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY;
|
|
PSID pSidSystem;
|
|
BOOL bRes = FALSE;
|
|
|
|
if ( AllocateAndInitializeSid(&id, 1, SECURITY_NETWORK_SERVICE_RID, 0, 0,0,0,0,0,0,&pSidSystem) )
|
|
{
|
|
if ( !CheckTokenMembership ( hAccess, pSidSystem, &bRes ) )
|
|
{
|
|
bRes = FALSE;
|
|
}
|
|
FreeSid ( pSidSystem );
|
|
}
|
|
|
|
return bRes;
|
|
}
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// IsLocalService
|
|
//
|
|
// returns TRUE if we are running as LOCAL_SERVICE
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL IsLocalService ( HANDLE hAccess )
|
|
{
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Construct the NETWORK_SERVICE SID
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY;
|
|
PSID pSidSystem;
|
|
BOOL bRes = FALSE;
|
|
|
|
if ( AllocateAndInitializeSid(&id, 1, SECURITY_LOCAL_SERVICE_RID, 0, 0,0,0,0,0,0,&pSidSystem) )
|
|
{
|
|
if ( !CheckTokenMembership ( hAccess, pSidSystem, &bRes ) )
|
|
{
|
|
bRes = FALSE;
|
|
}
|
|
FreeSid ( pSidSystem );
|
|
}
|
|
|
|
return bRes;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// IsInAdminGroup
|
|
//
|
|
// returns TRUE if we are a member of the admin group.
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL IsInAdminGroup()
|
|
{
|
|
HANDLE hAccessToken = INVALID_HANDLE_VALUE;
|
|
if(S_OK != GetAccessToken(hAccessToken))
|
|
return TRUE; // Not having a token indicates an internal thread
|
|
|
|
CCloseHandle cm(hAccessToken);
|
|
|
|
DWORD dwMask = 0;
|
|
|
|
if(IsAdmin(hAccessToken))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// HRESULT GetAccessToken
|
|
//
|
|
// Gets the access token and sets it the the reference argument.
|
|
//
|
|
//***************************************************************************
|
|
|
|
HRESULT GetAccessToken(HANDLE &hAccessToken)
|
|
{
|
|
// Ensures auto release of the mutex if we crash
|
|
CAutoSecurityMutex autosm;
|
|
|
|
bool bIsImpersonating = WbemIsImpersonating();
|
|
|
|
HRESULT hRes = S_OK;
|
|
if(bIsImpersonating == false)
|
|
hRes = WbemCoImpersonateClient();
|
|
if(hRes == S_OK)
|
|
{
|
|
BOOL bOK = OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &hAccessToken);
|
|
if(bOK == FALSE)
|
|
{
|
|
hRes = WBEM_E_INVALID_CONTEXT;
|
|
}
|
|
else
|
|
hRes = S_OK;
|
|
}
|
|
|
|
if(bIsImpersonating == false)
|
|
WbemCoRevertToSelf();
|
|
|
|
// The security mutex will auto release
|
|
|
|
return hRes;
|
|
}
|