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