538 lines
15 KiB
C++
538 lines
15 KiB
C++
|
#include <nt.h>
|
||
|
#include <ntrtl.h>
|
||
|
#include <nturtl.h>
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <rpc.h>
|
||
|
#include <ntdsapi.h>
|
||
|
#define SECURITY_WIN32
|
||
|
#include <security.h>
|
||
|
#include <aclapi.h>
|
||
|
#include <winldap.h>
|
||
|
#include <ntldap.h>
|
||
|
#include <dsgetdc.h>
|
||
|
#include <wbemcli.h>
|
||
|
|
||
|
#include "smartptr.h"
|
||
|
#include "rsoputil.h"
|
||
|
#include "rsopdbg.h"
|
||
|
#include "rsopsec.h"
|
||
|
|
||
|
|
||
|
extern "C" {
|
||
|
DWORD CheckAccessForPolicyGeneration( HANDLE hToken,
|
||
|
LPCWSTR szContainer,
|
||
|
LPWSTR szDomain,
|
||
|
BOOL bLogging,
|
||
|
BOOL* pbAccessGranted);
|
||
|
|
||
|
}
|
||
|
|
||
|
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
|
||
|
|
||
|
|
||
|
//*************************************************************
|
||
|
//
|
||
|
// CheckAccessForPolicyGeneration()
|
||
|
//
|
||
|
// Purpose: Finds out whether user has the right to generate rsop data
|
||
|
//
|
||
|
// Parameters: hToken - Token of the user who is using the tool
|
||
|
// szContainer - DS Container with which it needs to be validated
|
||
|
// bLogging - Logging or Planning mode
|
||
|
// pbAccessGranted - Access was granted or not
|
||
|
//
|
||
|
// Return: ERROR_SUCCESS
|
||
|
// Error Code otherwise
|
||
|
//
|
||
|
// The container passed in is actually parsed to figure out the first ou= or
|
||
|
// dc= supercontainer and then the rights are evaluated..
|
||
|
//
|
||
|
//*************************************************************
|
||
|
|
||
|
DWORD
|
||
|
CheckAccessForPolicyGeneration( HANDLE hToken,
|
||
|
LPCWSTR szContainer,
|
||
|
LPWSTR szDomain,
|
||
|
BOOL bLogging,
|
||
|
BOOL* pbAccessGranted)
|
||
|
{
|
||
|
DWORD dwError = ERROR_SUCCESS;
|
||
|
XHandle xhTokenDup;
|
||
|
BOOL bDomain = FALSE;
|
||
|
// BOOLEAN bSecurityWasEnabled;
|
||
|
WCHAR *pDomainString[1];
|
||
|
PDS_NAME_RESULT pNameResult = NULL;
|
||
|
|
||
|
*pbAccessGranted = 0;
|
||
|
|
||
|
//
|
||
|
// Parse the container first to get the OU= or DC=
|
||
|
// The "Actual SOM"
|
||
|
//
|
||
|
|
||
|
while (*szContainer) {
|
||
|
|
||
|
//
|
||
|
// See if the DN name starts with OU=
|
||
|
//
|
||
|
|
||
|
if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE,
|
||
|
szContainer, 3, TEXT("OU="), 3) == CSTR_EQUAL) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// See if the DN name starts with DC=
|
||
|
//
|
||
|
|
||
|
else if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE,
|
||
|
szContainer, 3, TEXT("DC="), 3) == CSTR_EQUAL) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Move to the next chunk of the DN name
|
||
|
//
|
||
|
|
||
|
while (*szContainer && (*szContainer != TEXT(','))) {
|
||
|
szContainer++;
|
||
|
}
|
||
|
|
||
|
if (*szContainer == TEXT(',')) {
|
||
|
szContainer++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!*szContainer) {
|
||
|
return ERROR_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
dbg.Msg( DEBUG_MESSAGE_VERBOSE, L"CheckAccessForPolicyGeneration: SOM for account is %s", szContainer );
|
||
|
|
||
|
|
||
|
//
|
||
|
// See if the DN name starts with DC=
|
||
|
//
|
||
|
|
||
|
if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE,
|
||
|
szContainer, 3, TEXT("DC="), 3) == CSTR_EQUAL) {
|
||
|
bDomain = TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// preparse the name to just get the string , dc=
|
||
|
//
|
||
|
|
||
|
XPtrLF<WCHAR> xwszDomain;
|
||
|
LPWSTR szDomLocal;
|
||
|
|
||
|
|
||
|
if (!szDomain) {
|
||
|
dwError = GetDomain(szContainer, &xwszDomain);
|
||
|
|
||
|
if (dwError != ERROR_SUCCESS) {
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"CheckAccessForPolicyGeneration: GetDomain failed with error %d", dwError );
|
||
|
return dwError;
|
||
|
}
|
||
|
|
||
|
szDomLocal = xwszDomain;
|
||
|
}
|
||
|
else {
|
||
|
szDomLocal = szDomain;
|
||
|
}
|
||
|
|
||
|
dbg.Msg( DEBUG_MESSAGE_VERBOSE, L"CheckAccessForPolicyGeneration: Som resides in domain %s", szDomLocal );
|
||
|
|
||
|
XPtrLF<WCHAR> xszDSObject = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*(wcslen(L"LDAP://")+
|
||
|
wcslen(szDomLocal)+
|
||
|
wcslen(szContainer)+5));
|
||
|
|
||
|
if (!xszDSObject) {
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"CheckAccessForPolicyGeneration: AllocMem failed with %d.", GetLastError() );
|
||
|
return GetLastError();
|
||
|
}
|
||
|
|
||
|
wcscpy(xszDSObject, L"LDAP://");
|
||
|
wcscat(xszDSObject, szDomLocal);
|
||
|
wcscat(xszDSObject, L"/");
|
||
|
wcscat(xszDSObject, szContainer);
|
||
|
|
||
|
dbg.Msg( DEBUG_MESSAGE_VERBOSE, L"CheckAccessForPolicyGeneration: getting SD off %s", xszDSObject );
|
||
|
|
||
|
|
||
|
if ( !DuplicateTokenEx( hToken,
|
||
|
TOKEN_IMPERSONATE | TOKEN_QUERY,
|
||
|
0,
|
||
|
SecurityImpersonation,
|
||
|
TokenImpersonation,
|
||
|
&xhTokenDup ) )
|
||
|
{
|
||
|
dwError = GetLastError();
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"CheckAccessForPolicyGeneration: DuplicateTokenEx failed, 0x%X", dwError );
|
||
|
return dwError;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Enable privilege to read SDs
|
||
|
//
|
||
|
|
||
|
/*
|
||
|
dwError = RtlAdjustPrivilege(SE_SECURITY_PRIVILEGE, TRUE, FALSE, &bSecurityWasEnabled);
|
||
|
|
||
|
if (!NT_SUCCESS(dwError)) {
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"CheckAccessForPolicyGeneration: DuplicateTokenEx failed, 0x%X", dwError );
|
||
|
return dwError;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
|
||
|
XPtrLF<SECURITY_DESCRIPTOR> xptrSD;
|
||
|
|
||
|
dwError = GetNamedSecurityInfo( (LPWSTR) xszDSObject,
|
||
|
SE_DS_OBJECT_ALL,
|
||
|
DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION ,
|
||
|
0,
|
||
|
0,
|
||
|
0,
|
||
|
0,
|
||
|
(void**) &xptrSD );
|
||
|
|
||
|
if ( !dwError )
|
||
|
{
|
||
|
|
||
|
//
|
||
|
// bf967aa5-0de6-11d0-a285-00aa003049e
|
||
|
//
|
||
|
GUID OUClass = {0xbf967aa5, 0x0de6, 0x11d0, 0xa2, 0x85, 0x00, 0xaa, 0x00, 0x30, 0x49, 0xe2};
|
||
|
|
||
|
//
|
||
|
// 19195a5b-6da0-11d0-afd3-00c04fd930c9
|
||
|
//
|
||
|
|
||
|
GUID DomainClass = {0x19195a5b, 0x6da0, 0x11d0, 0xaf, 0xd3, 0x00, 0xc0, 0x4f, 0xd9, 0x30, 0xc9};
|
||
|
|
||
|
|
||
|
//
|
||
|
// b7b1b3dd-ab09-4242-9e30-9980e5d322f7
|
||
|
//
|
||
|
GUID planningRight = {0xb7b1b3dd, 0xab09, 0x4242, 0x9e, 0x30, 0x99, 0x80, 0xe5, 0xd3, 0x22, 0xf7};
|
||
|
|
||
|
//
|
||
|
// b7b1b3de-ab09-4242-9e30-9980e5d322f7
|
||
|
//
|
||
|
GUID loggingRight = {0xb7b1b3de, 0xab09, 0x4242, 0x9e, 0x30, 0x99, 0x80, 0xe5, 0xd3, 0x22, 0xf7};
|
||
|
|
||
|
OBJECT_TYPE_LIST ObjType[2];
|
||
|
|
||
|
ObjType[0].Level = ACCESS_OBJECT_GUID;
|
||
|
ObjType[0].Sbz = 0;
|
||
|
|
||
|
if (bDomain) {
|
||
|
ObjType[0].ObjectType = &DomainClass;
|
||
|
}
|
||
|
else {
|
||
|
ObjType[0].ObjectType = &OUClass;
|
||
|
}
|
||
|
|
||
|
|
||
|
ObjType[1].Level = ACCESS_PROPERTY_SET_GUID;
|
||
|
ObjType[1].Sbz = 0;
|
||
|
|
||
|
if (bLogging) {
|
||
|
ObjType[1].ObjectType = &loggingRight;
|
||
|
}
|
||
|
else {
|
||
|
ObjType[1].ObjectType = &planningRight;
|
||
|
}
|
||
|
|
||
|
|
||
|
GENERIC_MAPPING GenericMapping = {
|
||
|
DS_GENERIC_READ,
|
||
|
DS_GENERIC_WRITE,
|
||
|
DS_GENERIC_EXECUTE,
|
||
|
DS_GENERIC_ALL
|
||
|
};
|
||
|
|
||
|
const DWORD PriviledgeSize = 2 * ( sizeof(PRIVILEGE_SET) + sizeof(LUID_AND_ATTRIBUTES) );
|
||
|
BYTE PrivilegeSetBuffer[PriviledgeSize];
|
||
|
DWORD cPrivilegeSet = PriviledgeSize;
|
||
|
PPRIVILEGE_SET pPrivilegeSet = (PPRIVILEGE_SET)PrivilegeSetBuffer;
|
||
|
DWORD dwGrantedAccess;
|
||
|
|
||
|
if ( !AccessCheckByType( xptrSD,
|
||
|
0,
|
||
|
xhTokenDup,
|
||
|
ACTRL_DS_CONTROL_ACCESS,
|
||
|
ObjType,
|
||
|
ARRAYSIZE(ObjType),
|
||
|
&GenericMapping,
|
||
|
pPrivilegeSet,
|
||
|
&cPrivilegeSet,
|
||
|
&dwGrantedAccess,
|
||
|
pbAccessGranted ) )
|
||
|
{
|
||
|
dwError = GetLastError();
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"CheckAccessForPolicyGeneration: AccessCheckByType failed, 0x%X", dwError );
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"CheckAccessForPolicyGeneration: GetNamedSecurityInfo failed, 0x%X", dwError );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
dwError = RtlAdjustPrivilege(SE_SECURITY_PRIVILEGE, bSecurityWasEnabled, FALSE, &bSecurityWasEnabled);
|
||
|
|
||
|
if (!NT_SUCCESS(dwError)) {
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"CheckAccessForPolicyGeneration: DuplicateTokenEx failed, 0x%X", dwError );
|
||
|
return dwError;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
return dwError;
|
||
|
}
|
||
|
|
||
|
//*************************************************************
|
||
|
//
|
||
|
// GetSOM()
|
||
|
//
|
||
|
// Purpose: Finds out the FQDN of a given user/computer
|
||
|
//
|
||
|
// Parameters: szAccount - User or computer Account name in an appropriate format
|
||
|
//
|
||
|
// Return: SOM, NULL otherwise. GetLastError() for details
|
||
|
// This just returns the DN of the user
|
||
|
//
|
||
|
//*************************************************************
|
||
|
|
||
|
|
||
|
LPWSTR
|
||
|
GetSOM( LPCWSTR szAccount )
|
||
|
{
|
||
|
DWORD dwSize = 0;
|
||
|
XPtrLF<WCHAR> xszXlatName;
|
||
|
XPtrLF<WCHAR> xszSOM;
|
||
|
|
||
|
TranslateName( szAccount,
|
||
|
NameUnknown,
|
||
|
NameFullyQualifiedDN,
|
||
|
xszXlatName,
|
||
|
&dwSize );
|
||
|
if (!dwSize)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
xszXlatName = (LPWSTR)LocalAlloc( LPTR, ( dwSize + 1 ) * sizeof( WCHAR ) );
|
||
|
if ( !xszXlatName )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if ( !TranslateName(szAccount,
|
||
|
NameUnknown,
|
||
|
NameFullyQualifiedDN,
|
||
|
xszXlatName,
|
||
|
&dwSize ) )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
xszSOM = xszXlatName.Acquire();
|
||
|
|
||
|
return xszSOM.Acquire();
|
||
|
}
|
||
|
|
||
|
|
||
|
//*************************************************************
|
||
|
//
|
||
|
// GetDomain()
|
||
|
//
|
||
|
// Purpose: Finds out the domain given a SOM.
|
||
|
//
|
||
|
// Parameters: szSOM - SOM
|
||
|
//
|
||
|
// Return: domain Dns if success, null otherwise
|
||
|
//
|
||
|
//*************************************************************
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
GetDomain( LPCWSTR szSOM, LPWSTR *pszDomain )
|
||
|
{
|
||
|
DWORD dwError = ERROR_SUCCESS;
|
||
|
WCHAR *pDomainString[1];
|
||
|
PDS_NAME_RESULT pNameResult = NULL;
|
||
|
|
||
|
//
|
||
|
// preparse the name to just get the string , dc=
|
||
|
//
|
||
|
|
||
|
pDomainString[0] = NULL;
|
||
|
|
||
|
LPWSTR pwszTemp = (LPWSTR)szSOM;
|
||
|
|
||
|
while ( *pwszTemp ) {
|
||
|
|
||
|
if (CompareString ( LOCALE_USER_DEFAULT, NORM_IGNORECASE,
|
||
|
pwszTemp, 3, TEXT("DC="), 3) == CSTR_EQUAL ) {
|
||
|
pDomainString[0] = pwszTemp;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Move to the next chunk of the DN name
|
||
|
//
|
||
|
|
||
|
while ( *pwszTemp && (*pwszTemp != TEXT(',')))
|
||
|
pwszTemp++;
|
||
|
|
||
|
if ( *pwszTemp == TEXT(','))
|
||
|
pwszTemp++;
|
||
|
|
||
|
}
|
||
|
|
||
|
if (pDomainString[0] == NULL) {
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"GetDomain: Som doesn't have DC=. failing" );
|
||
|
return ERROR_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
dwError = DsCrackNames( (HANDLE) -1,
|
||
|
DS_NAME_FLAG_SYNTACTICAL_ONLY,
|
||
|
DS_FQDN_1779_NAME,
|
||
|
DS_CANONICAL_NAME,
|
||
|
1,
|
||
|
pDomainString,
|
||
|
&pNameResult );
|
||
|
|
||
|
if ( dwError != ERROR_SUCCESS
|
||
|
|| pNameResult->cItems == 0
|
||
|
|| pNameResult->rItems[0].status != ERROR_SUCCESS
|
||
|
|| pNameResult->rItems[0].pDomain == NULL ) {
|
||
|
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"GetDomain: DsCrackNames failed with 0x%x.", dwError );
|
||
|
return dwError;
|
||
|
}
|
||
|
|
||
|
|
||
|
dbg.Msg( DEBUG_MESSAGE_VERBOSE, L"GetDomain: Som resides in domain %s", pNameResult->rItems[0].pDomain );
|
||
|
|
||
|
XPtrLF<WCHAR> xszDomain = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*(wcslen(pNameResult->rItems[0].pDomain)+2));
|
||
|
|
||
|
if (!xszDomain) {
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"CheckAccessForPolicyGeneration: AllocMem failed with %d.", GetLastError() );
|
||
|
DsFreeNameResult( pNameResult );
|
||
|
return GetLastError();
|
||
|
}
|
||
|
|
||
|
wcscpy(xszDomain, pNameResult->rItems[0].pDomain);
|
||
|
dbg.Msg( DEBUG_MESSAGE_VERBOSE, L"GetDomain: Domain for som %s = %s", szSOM, xszDomain );
|
||
|
DsFreeNameResult( pNameResult );
|
||
|
|
||
|
*pszDomain = xszDomain.Acquire();
|
||
|
|
||
|
return ERROR_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
//*************************************************************
|
||
|
//
|
||
|
// AuthenticateUser()
|
||
|
//
|
||
|
// Purpose: Authenticates whether the user has the right to do the operation
|
||
|
//
|
||
|
// Parameters: hToken - Token of the user
|
||
|
// szMachSOM - Machine SOM (optional)
|
||
|
// szUserSOM - User SOM (optional)
|
||
|
// bLogging - Logging or Planning mode
|
||
|
//
|
||
|
// Return: S_OK on success, error code otherwise
|
||
|
//
|
||
|
//*************************************************************
|
||
|
|
||
|
|
||
|
HRESULT AuthenticateUser(HANDLE hToken, LPCWSTR szMachSOM, LPCWSTR szUserSOM, BOOL bLogging, DWORD *pdwExtendedInfo)
|
||
|
{
|
||
|
if ( !szMachSOM && !szUserSOM )
|
||
|
{
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"AuthenticateUser: No mach and user som specified" );
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD dwError = ERROR_SUCCESS;
|
||
|
BOOL bMachAccess = FALSE, bUserAccess = FALSE;
|
||
|
|
||
|
|
||
|
//
|
||
|
// authenticate for machine SOM
|
||
|
//
|
||
|
|
||
|
if (szMachSOM) {
|
||
|
dwError = CheckAccessForPolicyGeneration( hToken,
|
||
|
szMachSOM,
|
||
|
NULL,
|
||
|
bLogging,
|
||
|
&bMachAccess
|
||
|
);
|
||
|
|
||
|
if ( dwError != ERROR_SUCCESS )
|
||
|
{
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"AuthenticateUser: CheckAccessForPolicyGeneration Machine returned error - %d", dwError );
|
||
|
return HRESULT_FROM_WIN32( dwError );
|
||
|
}
|
||
|
|
||
|
if ( !bMachAccess )
|
||
|
{
|
||
|
*pdwExtendedInfo |= RSOP_COMPUTER_ACCESS_DENIED;
|
||
|
dbg.Msg( DEBUG_MESSAGE_VERBOSE, L"AuthenticateUser: No access on Machine SOM");
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
bMachAccess = TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// authenticate for user SOM
|
||
|
//
|
||
|
|
||
|
if (szUserSOM) {
|
||
|
dwError = CheckAccessForPolicyGeneration( hToken,
|
||
|
szUserSOM,
|
||
|
NULL,
|
||
|
bLogging,
|
||
|
&bUserAccess
|
||
|
);
|
||
|
|
||
|
if ( dwError != ERROR_SUCCESS )
|
||
|
{
|
||
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"AuthenticateUser: CheckAccessForPolicyGeneration User returned error - %d", dwError );
|
||
|
return HRESULT_FROM_WIN32( dwError );
|
||
|
}
|
||
|
|
||
|
if ( !bUserAccess )
|
||
|
{
|
||
|
*pdwExtendedInfo |= RSOP_USER_ACCESS_DENIED;
|
||
|
dbg.Msg( DEBUG_MESSAGE_VERBOSE, L"AuthenticateUser: No access on User SOM");
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
bUserAccess = TRUE;
|
||
|
}
|
||
|
|
||
|
if ( !bUserAccess || !bMachAccess )
|
||
|
return E_ACCESSDENIED;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|