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

722 lines
17 KiB

/************************************************************************
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;
}