/************************************************************************ 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 #include #include #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 Buffer; try { Buffer = auto_ptr( 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 pSD; // auto_ptr 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( 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 pSD; // auto_ptr apparently doesn't work PACL sdDacl; PACL sdSacl; SafeReadFile( hFile, &SdSize ); pSD = auto_ptr( 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 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; }