723 lines
17 KiB
C++
723 lines
17 KiB
C++
/************************************************************************
|
|
|
|
Copyright (c) 2000 - 2000 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
csd.cpp
|
|
|
|
Abstract :
|
|
|
|
Main code file for SID and SECURITY_DESCRIPTOR abstraction.
|
|
|
|
Author :
|
|
|
|
Revision History :
|
|
|
|
***********************************************************************/
|
|
|
|
#include "stdafx.h"
|
|
#include <accctrl.h>
|
|
#include <malloc.h>
|
|
#include <aclapi.h>
|
|
|
|
#if !defined(BITS_V12_ON_NT4)
|
|
#include "csd.tmh"
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
CNestedImpersonation::CNestedImpersonation(
|
|
SidHandle sid
|
|
)
|
|
: m_Sid( sid ),
|
|
m_ImpersonationToken( NULL ),
|
|
m_fImpersonated( false ),
|
|
m_fDeleteToken( true )
|
|
{
|
|
try
|
|
{
|
|
THROW_HRESULT( g_Manager->CloneUserToken( m_Sid, ANY_SESSION, &m_ImpersonationToken ));
|
|
|
|
Impersonate();
|
|
}
|
|
catch( ComError Error )
|
|
{
|
|
Revert();
|
|
|
|
if (m_ImpersonationToken && m_fDeleteToken)
|
|
{
|
|
CloseHandle( m_ImpersonationToken );
|
|
}
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
CNestedImpersonation::CNestedImpersonation(
|
|
HANDLE token
|
|
)
|
|
: m_ImpersonationToken( token ),
|
|
m_fImpersonated( false ),
|
|
m_fDeleteToken( false )
|
|
{
|
|
Impersonate();
|
|
}
|
|
|
|
CNestedImpersonation::CNestedImpersonation()
|
|
: m_ImpersonationToken( NULL ),
|
|
m_fImpersonated( false ),
|
|
m_fDeleteToken( true )
|
|
{
|
|
//
|
|
// Failure will cause the base object's destructor to restore the old thread token.
|
|
//
|
|
|
|
try
|
|
{
|
|
HRESULT hr = CoImpersonateClient();
|
|
|
|
switch (hr)
|
|
{
|
|
case S_OK:
|
|
{
|
|
m_fImpersonated = true;
|
|
m_ImpersonationToken = CopyThreadToken();
|
|
|
|
#if defined(BITS_V12_ON_NT4)
|
|
RTL_VERIFY( SUCCEEDED( CoRevertToSelf() ) );
|
|
m_fImpersonated = false;
|
|
RTL_VERIFY( SetThreadToken( NULL, m_ImpersonationToken ) );
|
|
m_fImpersonated = true;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case RPC_E_CALL_COMPLETE:
|
|
{
|
|
m_ImpersonationToken = CopyThreadToken();
|
|
if (m_ImpersonationToken)
|
|
{
|
|
//
|
|
// thread was already impersonating someone when it called the BITS routine.
|
|
//
|
|
m_fImpersonated = true;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Thread is not impersonating. Impersonate the process owner.
|
|
//
|
|
if (!ImpersonateSelf( SecurityImpersonation ))
|
|
{
|
|
throw ComError( HRESULT_FROM_WIN32( GetLastError() ));
|
|
}
|
|
|
|
m_fImpersonated = true;
|
|
m_ImpersonationToken = CopyThreadToken();
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
throw ComError( hr );
|
|
}
|
|
}
|
|
catch( ComError err )
|
|
{
|
|
if (m_ImpersonationToken)
|
|
{
|
|
CloseHandle( m_ImpersonationToken );
|
|
m_ImpersonationToken = NULL;
|
|
}
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void
|
|
CNestedImpersonation::SwitchToLogonToken()
|
|
{
|
|
HANDLE token = m_ImpersonationToken;
|
|
|
|
SidHandle sid = CopyTokenSid( m_ImpersonationToken );
|
|
|
|
THROW_HRESULT( g_Manager->CloneUserToken( sid,
|
|
GetSession(),
|
|
&m_ImpersonationToken ));
|
|
|
|
m_fImpersonated = false;
|
|
|
|
if (m_fDeleteToken)
|
|
{
|
|
CloseHandle( token );
|
|
}
|
|
|
|
m_fDeleteToken = true;
|
|
|
|
Impersonate();
|
|
}
|
|
|
|
DWORD
|
|
CNestedImpersonation::GetSession()
|
|
{
|
|
|
|
#if defined(BITS_V12_ON_NT4)
|
|
return 0;
|
|
#else
|
|
DWORD session;
|
|
DWORD used;
|
|
|
|
if (!GetTokenInformation( m_ImpersonationToken,
|
|
TokenSessionId,
|
|
&session,
|
|
sizeof(DWORD),
|
|
&used))
|
|
{
|
|
ThrowLastError();
|
|
}
|
|
|
|
return session;
|
|
#endif
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
GENERIC_MAPPING CJobSecurityDescriptor::s_AccessMapping =
|
|
{
|
|
STANDARD_RIGHTS_READ,
|
|
STANDARD_RIGHTS_WRITE,
|
|
STANDARD_RIGHTS_EXECUTE,
|
|
STANDARD_RIGHTS_ALL
|
|
};
|
|
|
|
CJobSecurityDescriptor::CJobSecurityDescriptor(
|
|
SidHandle OwnerSid
|
|
)
|
|
{
|
|
PACL pACL = NULL;
|
|
PSECURITY_DESCRIPTOR pSD = 0;
|
|
|
|
try
|
|
{
|
|
EXPLICIT_ACCESS ea[2];
|
|
size_t SizeNeeded;
|
|
|
|
pSD = (PSECURITY_DESCRIPTOR) new char[SECURITY_DESCRIPTOR_MIN_LENGTH];
|
|
|
|
if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
|
|
{
|
|
HRESULT HrError = HRESULT_FROM_WIN32( GetLastError() );
|
|
LogError( "InitializeSecurityDescriptor Error %!winerr!", HrError );
|
|
throw ComError( HrError );
|
|
}
|
|
|
|
if (!SetSecurityDescriptorOwner( pSD, OwnerSid.get(), TRUE))
|
|
{
|
|
HRESULT HrError = HRESULT_FROM_WIN32( GetLastError() );
|
|
LogError( "SetSecurityDescriptorOwner Error %!winerr!", HrError );
|
|
throw ComError( HrError );
|
|
}
|
|
|
|
if (!SetSecurityDescriptorGroup( pSD, OwnerSid.get(), TRUE))
|
|
{
|
|
HRESULT HrError = HRESULT_FROM_WIN32( GetLastError() );
|
|
LogError( "SetSecurityDescriptorGroup Error %!winerr!", HrError );
|
|
throw ComError( HrError );
|
|
}
|
|
|
|
// Initialize an EXPLICIT_ACCESS structure for an ACE.
|
|
// The ACE will allow the Administrators group full access to the key.
|
|
memset(ea, 0, sizeof(ea));
|
|
|
|
ea[0].grfAccessPermissions = KEY_ALL_ACCESS;
|
|
ea[0].grfAccessMode = SET_ACCESS;
|
|
ea[0].grfInheritance= NO_INHERITANCE;
|
|
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ea[0].Trustee.TrusteeType = TRUSTEE_IS_USER;
|
|
ea[0].Trustee.ptstrName = (LPTSTR) OwnerSid.get();
|
|
|
|
// Initialize an EXPLICIT_ACCESS structure for an ACE.
|
|
// The ACE will allow the Administrators group full access to the key.
|
|
|
|
ea[1].grfAccessPermissions = KEY_ALL_ACCESS;
|
|
ea[1].grfAccessMode = SET_ACCESS;
|
|
ea[1].grfInheritance= NO_INHERITANCE;
|
|
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
|
|
ea[1].Trustee.ptstrName = (LPTSTR) g_GlobalInfo->m_AdministratorsSid.get();
|
|
|
|
// Create a new ACL that contains the new ACEs.
|
|
|
|
DWORD s = SetEntriesInAcl(2, ea, NULL, &pACL);
|
|
if (s != ERROR_SUCCESS)
|
|
{
|
|
HRESULT HrError = HRESULT_FROM_WIN32( s );
|
|
LogError( "create SD : SetEntriesInAcl failed %!winerr!", HrError );
|
|
throw ComError( HrError );
|
|
}
|
|
|
|
// Add the ACL to the security descriptor.
|
|
|
|
if (!SetSecurityDescriptorDacl( pSD,
|
|
TRUE, // fDaclPresent flag
|
|
pACL,
|
|
TRUE)) // a default DACL
|
|
{
|
|
HRESULT HrError = HRESULT_FROM_WIN32( GetLastError() );
|
|
LogError( "SetSecurityDescriptorDacl Error %!winerr!", HrError );
|
|
throw ComError( HrError );
|
|
}
|
|
|
|
//
|
|
// Add the pointers our object.
|
|
//
|
|
m_sd = pSD;
|
|
m_sdOwnerSid = OwnerSid;
|
|
m_sdGroupSid = OwnerSid;
|
|
m_Dacl = pACL;
|
|
}
|
|
catch( ComError exception )
|
|
{
|
|
if (pACL)
|
|
LocalFree(pACL);
|
|
|
|
if (pSD)
|
|
delete[] ((char*)pSD);
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
CJobSecurityDescriptor::CJobSecurityDescriptor(
|
|
PSECURITY_DESCRIPTOR sd,
|
|
SidHandle sdOwnerSid,
|
|
SidHandle sdGroupSid,
|
|
PACL sdDacl
|
|
)
|
|
{
|
|
m_sd = sd;
|
|
m_sdOwnerSid = sdOwnerSid;
|
|
m_sdGroupSid = sdGroupSid;
|
|
m_Dacl = sdDacl;
|
|
}
|
|
|
|
|
|
CJobSecurityDescriptor::~CJobSecurityDescriptor()
|
|
{
|
|
if (m_Dacl)
|
|
LocalFree(m_Dacl);
|
|
|
|
delete m_sd;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CJobSecurityDescriptor::_ModifyAcl(
|
|
PSID sid,
|
|
BOOL fGroupSid,
|
|
DWORD access,
|
|
BOOL fAdd
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwRes;
|
|
PACL pNewAcl = NULL;
|
|
EXPLICIT_ACCESS ea;
|
|
|
|
// Initialize an EXPLICIT_ACCESS structure for the new ACE.
|
|
|
|
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
|
|
ea.grfAccessPermissions = access;
|
|
ea.grfAccessMode = (fAdd) ? SET_ACCESS : REVOKE_ACCESS;
|
|
ea.grfInheritance = NO_INHERITANCE;
|
|
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ea.Trustee.TrusteeType = (fGroupSid) ? TRUSTEE_IS_GROUP : TRUSTEE_IS_USER;
|
|
ea.Trustee.ptstrName = LPTSTR(sid);
|
|
|
|
// Create a new ACL that merges the new ACE
|
|
// into the existing DACL.
|
|
|
|
dwRes = SetEntriesInAcl( 1, &ea, m_Dacl, &pNewAcl );
|
|
if (ERROR_SUCCESS != dwRes)
|
|
{
|
|
hr = HRESULT_FROM_WIN32( dwRes );
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Attach the new ACL as the object's DACL.
|
|
|
|
if (!SetSecurityDescriptorDacl( m_sd,
|
|
TRUE, // fDaclPresent flag
|
|
pNewAcl,
|
|
FALSE )) // a default DACL
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
LogError( "SetSecurityDescriptorDacl Error %!winerr!", hr );
|
|
goto Cleanup;
|
|
}
|
|
|
|
LocalFree( m_Dacl );
|
|
|
|
m_Dacl = pNewAcl;
|
|
|
|
pNewAcl = NULL;
|
|
|
|
hr = S_OK;
|
|
|
|
Cleanup:
|
|
|
|
if(pNewAcl)
|
|
LocalFree((HLOCAL) pNewAcl);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CJobSecurityDescriptor::CheckTokenAccess(
|
|
HANDLE hToken,
|
|
DWORD RequestedAccess,
|
|
DWORD * pAllowedAccess,
|
|
BOOL * pSuccess
|
|
)
|
|
{
|
|
|
|
PRIVILEGE_SET * PrivilegeSet = 0;
|
|
DWORD PrivilegeSetSize;
|
|
//
|
|
// Get space for the privilege set. I don't expect to use any...
|
|
//
|
|
PrivilegeSetSize = sizeof(PRIVILEGE_SET) + sizeof(LUID_AND_ATTRIBUTES);
|
|
auto_ptr<char> Buffer;
|
|
|
|
try
|
|
{
|
|
Buffer = auto_ptr<char>( new char[ PrivilegeSetSize ] );
|
|
}
|
|
catch( ComError Error )
|
|
{
|
|
return Error.Error();
|
|
}
|
|
|
|
PrivilegeSet = (PRIVILEGE_SET *) Buffer.get();
|
|
|
|
//
|
|
// See whether the security descriptor allows access.
|
|
//
|
|
if (!AccessCheck( m_sd,
|
|
hToken,
|
|
RequestedAccess,
|
|
&s_AccessMapping,
|
|
PrivilegeSet,
|
|
&PrivilegeSetSize,
|
|
pAllowedAccess,
|
|
pSuccess
|
|
))
|
|
{
|
|
HRESULT HrError = HRESULT_FROM_WIN32( GetLastError() );
|
|
LogError( "AccessCheck failed, error %!winerr!", HrError );
|
|
return HrError;
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
HRESULT
|
|
CJobSecurityDescriptor::Serialize(
|
|
HANDLE hFile
|
|
)
|
|
{
|
|
try
|
|
{
|
|
ULONG SdSize;
|
|
auto_ptr<char> pSD; // auto_ptr<void> apparently doesn't work
|
|
|
|
//
|
|
// Convert the security descriptor into self-relative format for storage.
|
|
//
|
|
SdSize = 0;
|
|
MakeSelfRelativeSD( m_sd, NULL, &SdSize );
|
|
if (SdSize == 0)
|
|
{
|
|
throw ComError( HRESULT_FROM_WIN32(GetLastError()) );
|
|
}
|
|
|
|
pSD = auto_ptr<char>( new char[ SdSize ] );
|
|
|
|
if (!MakeSelfRelativeSD( m_sd, pSD.get(), &SdSize ))
|
|
{
|
|
throw ComError( HRESULT_FROM_WIN32(GetLastError()) );
|
|
}
|
|
|
|
SafeWriteFile( hFile, SdSize );
|
|
SafeWriteFile( hFile, pSD.get(), SdSize );
|
|
}
|
|
catch( ComError err )
|
|
{
|
|
LogError("SD serialize failed with %!winerr!", err.Error() );
|
|
|
|
throw;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
CJobSecurityDescriptor *
|
|
CJobSecurityDescriptor::Unserialize(
|
|
HANDLE hFile
|
|
)
|
|
{
|
|
//
|
|
// Allocations here must match the deletes in the destructor.
|
|
//
|
|
char * SdBuf = 0;
|
|
char * DaclBuf = 0;
|
|
CJobSecurityDescriptor * pObject = NULL;
|
|
|
|
try
|
|
{
|
|
DWORD SdSize = 0;
|
|
DWORD DaclSize = 0;
|
|
DWORD SaclSize = 0;
|
|
DWORD OwnerSize = 0;
|
|
DWORD GroupSize = 0;
|
|
|
|
PSECURITY_DESCRIPTOR sd;
|
|
auto_ptr<char> pSD; // auto_ptr<void> apparently doesn't work
|
|
|
|
PACL sdDacl;
|
|
PACL sdSacl;
|
|
|
|
|
|
SafeReadFile( hFile, &SdSize );
|
|
|
|
pSD = auto_ptr<char>( new char[ SdSize ] );
|
|
|
|
SafeReadFile( hFile, pSD.get(), SdSize );
|
|
|
|
MakeAbsoluteSD( pSD.get(),
|
|
NULL, &SdSize,
|
|
NULL, &DaclSize,
|
|
NULL, &SaclSize,
|
|
NULL, &OwnerSize,
|
|
NULL, &GroupSize
|
|
);
|
|
|
|
if (!SdSize || !DaclSize || !OwnerSize || !GroupSize)
|
|
{
|
|
throw ComError( HRESULT_FROM_WIN32(GetLastError()));
|
|
}
|
|
|
|
SdBuf = new char[ SdSize + SaclSize ];
|
|
SidHandle OwnerSid = new char[ OwnerSize ];
|
|
SidHandle GroupSid = new char[ GroupSize ];
|
|
|
|
DaclBuf = (char *) LocalAlloc( LMEM_FIXED, DaclSize );
|
|
|
|
sdDacl = (PACL) DaclBuf;
|
|
sd = (PSECURITY_DESCRIPTOR) SdBuf;
|
|
sdSacl = (PACL) (SdBuf+SdSize);
|
|
|
|
if (!MakeAbsoluteSD( pSD.get(),
|
|
sd, &SdSize,
|
|
sdDacl, &DaclSize,
|
|
sdSacl, &SaclSize,
|
|
OwnerSid.get(), &OwnerSize,
|
|
GroupSid.get(), &GroupSize
|
|
))
|
|
{
|
|
throw ComError( HRESULT_FROM_WIN32(GetLastError()));
|
|
}
|
|
|
|
pObject = new CJobSecurityDescriptor( sd,
|
|
OwnerSid,
|
|
GroupSid,
|
|
sdDacl
|
|
);
|
|
}
|
|
catch (ComError exception)
|
|
{
|
|
delete[] SdBuf;
|
|
|
|
LocalFree( DaclBuf );
|
|
delete pObject;
|
|
|
|
throw;
|
|
}
|
|
|
|
return pObject;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
PSID
|
|
CopyTokenSid(
|
|
HANDLE Token
|
|
)
|
|
{
|
|
TOKEN_USER * TokenData;
|
|
DWORD SizeNeeded;
|
|
|
|
// Get the size first.
|
|
if (!GetTokenInformation(
|
|
Token,
|
|
TokenUser,
|
|
0,
|
|
0,
|
|
&SizeNeeded
|
|
))
|
|
{
|
|
DWORD dwLastError = GetLastError();
|
|
|
|
if (ERROR_INSUFFICIENT_BUFFER != dwLastError)
|
|
{
|
|
THROW_HRESULT( HRESULT_FROM_WIN32( GetLastError()));
|
|
}
|
|
}
|
|
|
|
auto_ptr<char> Buffer( new char[ SizeNeeded ] );
|
|
TokenData = (TOKEN_USER *) Buffer.get();
|
|
|
|
if (!GetTokenInformation(
|
|
Token,
|
|
TokenUser,
|
|
TokenData,
|
|
SizeNeeded,
|
|
&SizeNeeded
|
|
))
|
|
{
|
|
THROW_HRESULT( HRESULT_FROM_WIN32( GetLastError()));
|
|
}
|
|
|
|
PSID sid = DuplicateSid( TokenData->User.Sid );
|
|
if (sid == NULL)
|
|
{
|
|
THROW_HRESULT( E_OUTOFMEMORY);
|
|
}
|
|
|
|
return sid;
|
|
}
|
|
|
|
|
|
HANDLE
|
|
CopyThreadToken()
|
|
/*
|
|
|
|
Makes a copy of the current thread's impersonation token.
|
|
Returns NULL if not impersonating.
|
|
Throws an exception if an error occurs.
|
|
|
|
*/
|
|
{
|
|
HANDLE token = NULL;
|
|
|
|
if (OpenThreadToken( GetCurrentThread(),
|
|
MAXIMUM_ALLOWED,
|
|
TRUE,
|
|
&token))
|
|
{
|
|
return token;
|
|
}
|
|
else if (GetLastError() == ERROR_NO_TOKEN)
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
throw ComError( HRESULT_FROM_WIN32( GetLastError() ));
|
|
}
|
|
}
|
|
|
|
SidHandle
|
|
GetThreadClientSid()
|
|
/*
|
|
|
|
Returns the SID of the current thread's COM client.
|
|
Throws an exception if an error occurs.
|
|
|
|
*/
|
|
{
|
|
CNestedImpersonation imp;
|
|
|
|
return imp.CopySid();
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
IsRemoteUser()
|
|
{
|
|
return CheckClientGroupMembership( g_GlobalInfo->m_NetworkUsersSid );
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CheckClientGroupMembership(
|
|
SidHandle group
|
|
)
|
|
{
|
|
try
|
|
{
|
|
BOOL fIsMember;
|
|
|
|
CNestedImpersonation imp;
|
|
|
|
if (!CheckTokenMembership( imp.QueryToken(),
|
|
group.get(),
|
|
&fIsMember))
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
if (fIsMember)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
catch( ComError Error )
|
|
{
|
|
return Error.Error();
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
DenyRemoteAccess()
|
|
{
|
|
HRESULT hr = CheckClientGroupMembership( g_GlobalInfo->m_NetworkUsersSid );
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
return BG_E_REMOTE_NOT_SUPPORTED;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
DenyNonAdminAccess()
|
|
{
|
|
HRESULT hr = CheckClientGroupMembership( g_GlobalInfo->m_AdministratorsSid );
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if (hr == S_FALSE)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|