////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1999-2001 Microsoft Corporation // // Module Name: // CClusSvcAccountConfig.cpp // // Description: // Contains the definition of the CClusSvcAccountConfig class. // // Maintained By: // David Potter (DavidP) 30-MAR-2001 // Vij Vasu (Vvasu) 08-MAR-2000 // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Include Files ////////////////////////////////////////////////////////////////////////////// // The precompiled header. #include "Pch.h" // The header for this file #include "CClusSvcAccountConfig.h" // For the CBaseClusterAddNode class. #include "CBaseClusterAddNode.h" // For the net local group functions. #include // For NERR_Success #include ////////////////////////////////////////////////////////////////////////////// // Global Variables ////////////////////////////////////////////////////////////////////////////// // Array of the names of rights to be granted to the cluster service account. static const WCHAR * const gs_rgpcszRightsArray[] = { SE_SERVICE_LOGON_NAME , SE_BACKUP_NAME , SE_RESTORE_NAME , SE_INCREASE_QUOTA_NAME , SE_INC_BASE_PRIORITY_NAME , SE_TCB_NAME }; const UINT gc_uiRightsArraySize = sizeof( gs_rgpcszRightsArray ) / sizeof( gs_rgpcszRightsArray[0] ); ////////////////////////////////////////////////////////////////////////////// //++ // // CClusSvcAccountConfig::CClusSvcAccountConfig() // // Description: // Constructor of the CClusSvcAccountConfig class // // Arguments: // pbcanParentActionIn // Pointer to the base cluster action of which this action is a part. // // Return Value: // None. // // Exceptions Thrown: // CRuntimeError // If any of the APIs fail. // // Any exceptions thrown by underlying functions // //-- ////////////////////////////////////////////////////////////////////////////// CClusSvcAccountConfig::CClusSvcAccountConfig( CBaseClusterAddNode * pbcanParentActionIn ) : m_pbcanParentAction( pbcanParentActionIn ) , m_fWasAreadyInGroup( true ) , m_fRightsGrantSuccessful( false ) , m_fRemoveAllRights( false ) { BCATraceScope( "" ); DWORD dwError = ERROR_SUCCESS; UINT uiIndex; // Indicate that action can be rolled back. SetRollbackPossible( true ); do { PSID psidAdministrators = NULL; SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY; DWORD dwNameSize = 0; DWORD dwDomainSize = 0; SID_NAME_USE snuSidNameUse; // // Get the Admins SID // if ( AllocateAndInitializeSid( &siaNtAuthority // identifier authority , 2 // count of subauthorities , SECURITY_BUILTIN_DOMAIN_RID // subauthority 0 , DOMAIN_ALIAS_RID_ADMINS // subauthority 1 , 0 // subauthority 2 , 0 // subauthority 3 , 0 // subauthority 4 , 0 // subauthority 5 , 0 // subauthority 6 , 0 // subauthority 7 , &psidAdministrators // pointer to pointer to SID ) == 0 ) { DWORD dwError = TW32( GetLastError() ); TraceFlow1( "Error %#08x occurred trying get the Administrators group SID.", dwError ); break; } // if: AllocateAndInitializeSid() failed // Assign the allocated SID to to the member variable. m_ssidAdminSid.Assign( psidAdministrators ); // // Look up the administrators group name and store it. // // Find out how much space is required by the name. if ( LookupAccountSid( NULL , psidAdministrators , NULL , &dwNameSize , NULL , &dwDomainSize , &snuSidNameUse ) == FALSE ) { dwError = GetLastError(); if ( dwError != ERROR_INSUFFICIENT_BUFFER ) { TW32( dwError ); TraceFlow( "LookupAccountSid() failed while querying for required buffer size." ); break; } // if: something else has gone wrong. else { // This is expected. dwError = ERROR_SUCCESS; } // if: ERROR_INSUFFICIENT_BUFFER was returned. } // if: LookupAccountSid failed // Allocate memory for the admin group name and the domain name. m_sszAdminGroupName.Assign( new WCHAR[ dwNameSize ] ); SmartSz sszDomainName( new WCHAR[ dwDomainSize ] ); if ( m_sszAdminGroupName.FIsEmpty() || sszDomainName.FIsEmpty() ) { dwError = ERROR_OUTOFMEMORY; break; } // if: there wasn't enough memory // Get the admin group name. if ( LookupAccountSid( NULL , psidAdministrators , m_sszAdminGroupName.PMem() , &dwNameSize , sszDomainName.PMem() , &dwDomainSize , &snuSidNameUse ) == FALSE ) { dwError = GetLastError(); TraceFlow( "LookupAccountSid() failed while attempting to get the admin group name." ); break; } // if: LookupAccountSid failed } while( false ); // dummy do-while loop to avoid gotos. if ( dwError != ERROR_SUCCESS ) { LogMsg( "Error %#08x occurred trying to get information about the administrators group.", dwError ); TraceFlow1( "Error %#08x occurred trying to get information about the administrators group. Throwing exception.", dwError ); THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( dwError ), IDS_ERROR_GET_ADMIN_GROUP_INFO ); } // if: something went wrong. } //*** CClusSvcAccountConfig::CClusSvcAccountConfig() ////////////////////////////////////////////////////////////////////////////// //++ // // CClusSvcAccountConfig::~CClusSvcAccountConfig() // // Description: // Destructor of the CClusSvcAccountConfig class. // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // Any exceptions thrown by underlying functions // //-- ////////////////////////////////////////////////////////////////////////////// CClusSvcAccountConfig::~CClusSvcAccountConfig( void ) { BCATraceScope( "" ); } //*** CClusSvcAccountConfig::~CClusSvcAccountConfig() ////////////////////////////////////////////////////////////////////////////// //++ // // void // CClusSvcAccountConfig::Commit() // // Description: // Grant the required rights to the account. // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // Any that are thrown by the contained actions. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusSvcAccountConfig::Commit( void ) { BCATraceScope( "" ); // Call the base class commit method. BaseClass::Commit(); try { // Configure the account. ConfigureAccount(); } // try: catch( ... ) { // If we are here, then something went wrong with the create. TraceFlow( "Caught exception during commit." ); // // Cleanup anything that the failed create might have done. // Catch any exceptions thrown during Cleanup to make sure that there // is no collided unwind. // try { RevertAccount(); } catch( ... ) { // // The rollback of the committed action has failed. // There is nothing that we can do. // We certainly cannot rethrow this exception, since // the exception that caused the rollback is more important. // THR( E_UNEXPECTED ); TraceFlow( "Caught exception during cleanup." ); LogMsg( "THIS COMPUTER MAY BE IN AN INVALID STATE. An error has occurred during cleanup." ); } // catch: all // Rethrow the exception thrown by commit. throw; } // catch: all // If we are here, then everything went well. SetCommitCompleted( true ); } //*** CClusSvcAccountConfig::Commit() ////////////////////////////////////////////////////////////////////////////// //++ // // void // CClusSvcAccountConfig::Rollback() // // Description: // Roll the account back to the state it was in before we tried to // grant it the required privileges. // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // Any that are thrown by the underlying functions. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusSvcAccountConfig::Rollback( void ) { BCATraceScope( "" ); // Call the base class rollback method. BaseClass::Rollback(); // Bring the account back to its original state. RevertAccount(); SetCommitCompleted( false ); } //*** CClusSvcAccountConfig::Rollback() ////////////////////////////////////////////////////////////////////////////// //++ // // void // CClusSvcAccountConfig::ConfigureAccount() // // Description: // Grant the account that will be the cluster service account the requried // privileges. // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // CRuntimeError // If any of the APIs fail. // // Any that are thrown by the underlying functions. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusSvcAccountConfig::ConfigureAccount( void ) { BCATraceScope( "" ); typedef CSmartResource< CHandleTrait< PLSA_UNICODE_STRING , NTSTATUS , reinterpret_cast< NTSTATUS (*)( PLSA_UNICODE_STRING ) >( LsaFreeMemory ) , reinterpret_cast< PLSA_UNICODE_STRING >( NULL ) > > SmartLsaUnicodeStringPtr; NTSTATUS ntStatus; PLSA_UNICODE_STRING plusAccountRights = NULL; ULONG ulOriginalRightsCount = 0; ULONG rgulToBeGrantedIndex[ gc_uiRightsArraySize ]; ULONG ulIndex; ULONG ulIndexInner; DWORD dwError; CStatusReport srConfigAcct( m_pbcanParentAction->PBcaiGetInterfacePointer() , TASKID_Major_Configure_Cluster_Services , TASKID_Minor_Configuring_Cluster_Service_Account , 0, 1 , IDS_TASK_CONFIG_CLUSSVC_ACCOUNT ); // Send the next step of this status report. srConfigAcct.SendNextStep( S_OK ); // Add the cluster service account to the local admin group. m_fWasAreadyInGroup = FChangeAdminGroupMembership( m_pbcanParentAction->PSidGetServiceAccountSID() , true ); LogMsg( "Determining the rights that need to be granted to the cluster service account." ); // Get the list of rights already granted to the cluster service account. ntStatus = LsaEnumerateAccountRights( m_pbcanParentAction->HGetLSAPolicyHandle() , m_pbcanParentAction->PSidGetServiceAccountSID() , &plusAccountRights , &ulOriginalRightsCount ); if ( ntStatus != STATUS_SUCCESS ) { // // LSA returns this error code if the account has no rights granted or denied to it // locally. This is not an error as far as we are concerned. // if ( ntStatus == STATUS_OBJECT_NAME_NOT_FOUND ) { ntStatus = STATUS_SUCCESS; TraceFlow( "The account has no locally assigned rights." ); m_fRemoveAllRights = true; plusAccountRights = NULL; ulOriginalRightsCount = 0; } // if: the account does not have any rights assigned locally to it. else { THR( ntStatus ); LogMsg( "Error %#08x occurred trying to enumerate the cluster service account rights.", ntStatus ); TraceFlow1( "Error %#08x occurred trying to enumerate the cluster service account rights. Throwing exception", ntStatus ); THROW_RUNTIME_ERROR( ntStatus, IDS_ERROR_ACCOUNT_RIGHTS_CONFIG ); } // else: something went wrong. } // if: LsaEnumerateAccountRights() failed // Store the account rights just enumerated in a smart pointer for automatic release. SmartLsaUnicodeStringPtr splusOriginalRights( plusAccountRights ); // Initialize the count of rights to be granted. m_ulRightsToBeGrantedCount = 0; // Determine which of the rights that we are going to grant the account are already granted. for ( ulIndex = 0; ulIndex < gc_uiRightsArraySize; ++ulIndex ) { bool fRightAlreadyGranted = false; for ( ulIndexInner = 0; ulIndexInner < ulOriginalRightsCount; ++ulIndexInner ) { const WCHAR * pchGrantedRight = plusAccountRights[ ulIndexInner ].Buffer; USHORT usCharCount = plusAccountRights[ ulIndexInner ].Length / sizeof( *pchGrantedRight ); const WCHAR * pcszToBeGrantedRight = gs_rgpcszRightsArray[ ulIndex ]; // Do our own string compare since LSA_UNICODE_STRING may not be '\0' terminated. while ( ( usCharCount > 0 ) && ( *pcszToBeGrantedRight != L'\0' ) ) { if ( *pchGrantedRight != *pcszToBeGrantedRight ) { break; } // if: the current characters are not the same. --usCharCount; ++pcszToBeGrantedRight; ++pchGrantedRight; } // while: there are still characters to be compared // The strings are equal. if ( ( usCharCount == 0 ) && ( *pcszToBeGrantedRight == L'\0' ) ) { fRightAlreadyGranted = true; break; } // if: the strings are equal } // for: loop through the list of rights already granted to the account // Is the current right already granted. if ( ! fRightAlreadyGranted ) { // The current right is not already granted. rgulToBeGrantedIndex[ m_ulRightsToBeGrantedCount ] = ulIndex; // One more right to be granted. ++m_ulRightsToBeGrantedCount; } // if: the current right was not already granted } // for: loop through the list of rights that we want to grant the account // // Create an array of LSA_UNICODE_STRINGs of right names to be granted and store it in the // member variable. // m_srglusRightsToBeGrantedArray.Assign( new LSA_UNICODE_STRING[ m_ulRightsToBeGrantedCount ] ); if ( m_srglusRightsToBeGrantedArray.FIsEmpty() ) { LogMsg( "A memory allocation error occurred trying to grant account rights." ); TraceFlow1( "Could not allocate %d bytes of memory. Throwing exception.", m_ulRightsToBeGrantedCount ); THROW_RUNTIME_ERROR( E_OUTOFMEMORY , IDS_ERROR_ACCOUNT_RIGHTS_CONFIG ); } // if: memory allocation failed. // Initialize the array. for ( ulIndex = 0; ulIndex < m_ulRightsToBeGrantedCount; ++ ulIndex ) { ULONG ulCurrentRightIndex = rgulToBeGrantedIndex[ ulIndex ]; TraceFlow1( "The '%ws' right will be granted.", gs_rgpcszRightsArray[ ulCurrentRightIndex ] ); // Add it to the list of rights to be granted. InitLsaString( const_cast< WCHAR * >( gs_rgpcszRightsArray[ ulCurrentRightIndex ] ) , m_srglusRightsToBeGrantedArray.PMem() + ulIndex ); } // for: iterate through the list of rights that need to be granted // Grant the rights. ntStatus = THR( LsaAddAccountRights( m_pbcanParentAction->HGetLSAPolicyHandle() , m_pbcanParentAction->PSidGetServiceAccountSID() , m_srglusRightsToBeGrantedArray.PMem() , m_ulRightsToBeGrantedCount ) ); if ( ntStatus != STATUS_SUCCESS ) { LogMsg( "Error %#08x occurred trying to grant the cluster service account rights.", ntStatus ); TraceFlow1( "Error %#08x occurred trying to grant the cluster service account rights. Throwing exception", ntStatus ); THROW_RUNTIME_ERROR( ntStatus, IDS_ERROR_ACCOUNT_RIGHTS_CONFIG ); } // if: LsaAddAccountRights() failed m_fRightsGrantSuccessful = true; // Send the last step of this status report. srConfigAcct.SendNextStep( S_OK ); } //*** CClusSvcAccountConfig::ConfigureAccount() ////////////////////////////////////////////////////////////////////////////// //++ // // void // CClusSvcAccountConfig::RevertAccount() // // Description: // Bring the account back to its original state. // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // Any that are thrown by the underlying functions. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusSvcAccountConfig::RevertAccount( void ) { BCATraceScope( "" ); // Check if we granted any rights to the service account. If we did, revoke them. if ( m_fRightsGrantSuccessful ) { NTSTATUS ntStatus; // Revoke the rights. ntStatus = THR( LsaRemoveAccountRights( m_pbcanParentAction->HGetLSAPolicyHandle() , m_pbcanParentAction->PSidGetServiceAccountSID() , m_fRemoveAllRights , m_srglusRightsToBeGrantedArray.PMem() , m_ulRightsToBeGrantedCount ) ); if ( ntStatus != STATUS_SUCCESS ) { LogMsg( "Error %#08x occurred trying to remove the granted cluster service account rights.", ntStatus ); TraceFlow1( "Error %#08x occurred trying to remove the granted the cluster service account rights. Throwing exception", ntStatus ); THROW_RUNTIME_ERROR( ntStatus, IDS_ERROR_ACCOUNT_RIGHTS_CONFIG ); } // if: LsaRemoveAccountRights() failed } // if: we granted the service account any rights. // Check if we added the account to the admin group. If we did, remove it. if ( ! m_fWasAreadyInGroup ) { FChangeAdminGroupMembership( m_pbcanParentAction->PSidGetServiceAccountSID(), false ); } // if: we added the account to the admin group. } //*** CClusSvcAccountConfig::RevertAccount() ////////////////////////////////////////////////////////////////////////////// //++ // // void // CClusSvcAccountConfig::InitLsaString() // // Description: // Initialize a LSA_UNICODE_STRING structure // // Arguments: // pszSourceIn // The string used to initialize the unicode string structure. // // plusUnicodeStringOut, // The output unicode string structure. // // Return Value: // None. // // Exceptions Thrown: // None. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusSvcAccountConfig::InitLsaString( LPWSTR pszSourceIn , PLSA_UNICODE_STRING plusUnicodeStringOut ) { if ( pszSourceIn == NULL ) { plusUnicodeStringOut->Buffer = NULL; plusUnicodeStringOut->Length = 0; plusUnicodeStringOut->MaximumLength = 0; } // if: input string is NULL else { plusUnicodeStringOut->Buffer = pszSourceIn; plusUnicodeStringOut->Length = static_cast< USHORT >( wcslen( pszSourceIn ) * sizeof( *pszSourceIn ) ); plusUnicodeStringOut->MaximumLength = static_cast< USHORT >( plusUnicodeStringOut->Length + sizeof( *pszSourceIn ) ); } // else: input string is not NULL } //*** CClusSvcAccountConfig::InitLsaString() ////////////////////////////////////////////////////////////////////////////// //++ // // bool // CClusSvcAccountConfig::FChangeAdminGroupMembership() // // Description: // Adds/removes an account to/from the administrators group. // // Arguments: // psidAccountSidIn // Pointer to the SID the of account to add/remove to/from administrators // group. // // fAddIn // The account is added to the administrators group if this parameter // is true. The account is removed from the group otherwise. // // Return Value: // true if the accound was already present/absent in/from the group. // false otherwise. // // Exceptions Thrown: // CRuntimeError // If any of the APIs fail. // //-- ////////////////////////////////////////////////////////////////////////////// bool CClusSvcAccountConfig::FChangeAdminGroupMembership( PSID psidAccountSidIn , bool fAddIn ) { BCATraceScope( "" ); bool fWasAlreadyInGroup = false; LOCALGROUP_MEMBERS_INFO_0 lgmiLocalGroupMemberInfo; NET_API_STATUS nasStatus; lgmiLocalGroupMemberInfo.lgrmi0_sid = psidAccountSidIn; if ( fAddIn ) { CStatusReport srAddAcctToAdminGroup( m_pbcanParentAction->PBcaiGetInterfacePointer() , TASKID_Major_Configure_Cluster_Services , TASKID_Minor_Make_Cluster_Service_Account_Admin , 0, 1 , IDS_TASK_MAKING_CLUSSVC_ACCOUNT_ADMIN ); srAddAcctToAdminGroup.SendNextStep( S_OK ); nasStatus = NetLocalGroupAddMembers( NULL , m_sszAdminGroupName.PMem() , 0 , reinterpret_cast< LPBYTE >( &lgmiLocalGroupMemberInfo ) , 1 ); if ( nasStatus == ERROR_MEMBER_IN_ALIAS ) { LogMsg( "The account was already a member of the admin group." ); TraceFlow( "The account was already a member of the admin group." ); nasStatus = NERR_Success; fWasAlreadyInGroup = true; srAddAcctToAdminGroup.SendLastStep( S_OK, IDS_TASK_CLUSSVC_ACCOUNT_ALREADY_ADMIN ); } // if: the account was already a member of the admin group. else { if ( nasStatus == NERR_Success ) { TraceFlow( "The account has been added to the admin group." ); srAddAcctToAdminGroup.SendLastStep( S_OK ); fWasAlreadyInGroup = false; } // if: everything was ok else { HRESULT hr = HRESULT_FROM_WIN32( TW32( nasStatus ) ); srAddAcctToAdminGroup.SendLastStep( hr ); TraceFlow( "NetLocalGroupAddMembers() returned an error." ); } // else: something went wrong } // else: the account was not already a member of the admin group. } // if: the account has to be added to the admin group else { LogMsg( "The account needs to be removed from the administrators group." ); TraceFlow( "The account needs to be removed from the administrators group." ); nasStatus = NetLocalGroupDelMembers( NULL , m_sszAdminGroupName.PMem() , 0 , reinterpret_cast< LPBYTE >( &lgmiLocalGroupMemberInfo ) , 1 ); if ( nasStatus == ERROR_NO_SUCH_MEMBER ) { LogMsg( "The account was not a member of the admin group to begin with." ); TraceFlow( "The account was not a member of the admin group to begin with." ); nasStatus = NERR_Success; fWasAlreadyInGroup = false; } // if: the account was not a member of the admin group. else { if ( nasStatus == NERR_Success ) { TraceFlow( "The account has been deleted from the admin group." ); fWasAlreadyInGroup = true; } // if: everything was ok else { TW32( nasStatus ); TraceFlow( "NetLocalGroupDelMembers() returned an error." ); } // else: something went wrong } // else; the account was a member of the admin group. } // else: the account has to be deleted from the admin group if ( nasStatus != ERROR_SUCCESS ) { LogMsg( "Error %#08x occurred trying to change membership in administrators group.", nasStatus ); TraceFlow1( "Error %#08x occurred trying to change membership in administrators group. Throwing exception.", nasStatus ); THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( nasStatus ), IDS_ERROR_ADMIN_GROUP_ADD_REMOVE ); } // if: something went wrong. else { LogMsg( "The account was successfully added/deleted to/from the group '%s'.", m_sszAdminGroupName.PMem() ); TraceFlow1( "The account was successfully added/deleted to/from the group '%s'.", m_sszAdminGroupName.PMem() ); } // else: everything was hunky-dory return fWasAlreadyInGroup; } //*** CClusSvcAccountConfig::FChangeAdminGroupMembership()