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.
558 lines
16 KiB
558 lines
16 KiB
#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"
|
|
#include <strsafe.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_INVARIANT, NORM_IGNORECASE,
|
|
szContainer, 3, TEXT("OU="), 3) == CSTR_EQUAL) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// See if the DN name starts with DC=
|
|
//
|
|
|
|
else if (CompareString (LOCALE_INVARIANT, 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_INVARIANT, 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 );
|
|
|
|
DWORD dwDSObjLength = wcslen(L"LDAP://") + wcslen(szDomLocal) + wcslen(szContainer) + 5;
|
|
XPtrLF<WCHAR> xszDSObject = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * ( dwDSObjLength ));
|
|
|
|
if (!xszDSObject) {
|
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"CheckAccessForPolicyGeneration: AllocMem failed with %d.", GetLastError() );
|
|
return GetLastError();
|
|
}
|
|
|
|
HRESULT hr = StringCchCopy(xszDSObject, dwDSObjLength, L"LDAP://");
|
|
if(SUCCEEDED(hr))
|
|
hr = StringCchCat(xszDSObject, dwDSObjLength, szDomLocal);
|
|
if(SUCCEEDED(hr))
|
|
hr = StringCchCat(xszDSObject, dwDSObjLength, L"/");
|
|
if(SUCCEEDED(hr))
|
|
hr = StringCchCat(xszDSObject, dwDSObjLength, szContainer);
|
|
|
|
if(FAILED(hr))
|
|
return HRESULT_CODE(hr);
|
|
|
|
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_INVARIANT, 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 );
|
|
|
|
DWORD dwDomainLength = wcslen(pNameResult->rItems[0].pDomain) + 2;
|
|
XPtrLF<WCHAR> xszDomain = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * ( dwDomainLength ));
|
|
|
|
if (!xszDomain) {
|
|
dbg.Msg( DEBUG_MESSAGE_WARNING, L"CheckAccessForPolicyGeneration: AllocMem failed with %d.", GetLastError() );
|
|
DsFreeNameResult( pNameResult );
|
|
return GetLastError();
|
|
}
|
|
|
|
HRESULT hr = StringCchCopy(xszDomain, dwDomainLength, pNameResult->rItems[0].pDomain);
|
|
|
|
if(FAILED(hr)){
|
|
DsFreeNameResult( pNameResult );
|
|
return HRESULT_CODE(hr);
|
|
}
|
|
|
|
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
|
|
//
|
|
|
|
*pdwExtendedInfo = 0;
|
|
if (szMachSOM) {
|
|
*pdwExtendedInfo |= RSOP_COMPUTER_ACCESS_DENIED;
|
|
}
|
|
|
|
|
|
if (szUserSOM) {
|
|
*pdwExtendedInfo |= RSOP_USER_ACCESS_DENIED;
|
|
}
|
|
|
|
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: Access granted 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: Access granted on User SOM");
|
|
}
|
|
}
|
|
else {
|
|
bUserAccess = TRUE;
|
|
}
|
|
|
|
if ( !bUserAccess || !bMachAccess )
|
|
return E_ACCESSDENIED;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|