/*++ Copyright (c) 1998 Microsoft Corporation Module Name: connsink.c Abstract: Implements a COM sink object on which to receive connection folder events Author: CharlWi (Charlie Wickham) 11/30/98 - heavily ripped off from net\config\shell\folder\notify.cpp, originally authored by ShaunCo (Shaun Cox) Revision History: --*/ #define UNICODE 1 #include "connsink.h" #include extern "C" { #include "nmp.h" VOID ProcessNameChange( GUID * GuidId, LPCWSTR NewName ); } EXTERN_C const CLSID CLSID_ConnectionManager; EXTERN_C const IID IID_INetConnectionNotifySink; #define INVALID_COOKIE -1 CComModule _Module; DWORD AdviseCookie = INVALID_COOKIE; //static HRESULT CConnectionNotifySink::CreateInstance ( REFIID riid, VOID** ppv) { HRESULT hr = E_OUTOFMEMORY; // Initialize the output parameter. // *ppv = NULL; CConnectionNotifySink* pObj; pObj = new CComObject ; if (pObj) { // Do the standard CComCreator::CreateInstance stuff. // pObj->SetVoid (NULL); pObj->InternalFinalConstructAddRef (); hr = pObj->FinalConstruct (); pObj->InternalFinalConstructRelease (); if (SUCCEEDED(hr)) { hr = pObj->QueryInterface (riid, ppv); } if (FAILED(hr)) { delete pObj; } } if ( FAILED( hr )) { ClRtlLogPrint(LOG_CRITICAL, "[NM] Unable to create Net Connection Manager advise sink " "object, status %08X.\n", hr); } return hr; } // CConnectionNotifySink::CreateInstance //+--------------------------------------------------------------------------- // // Member: CConnectionNotifySink::~CConnectionNotifySink // // Purpose: Clean up the sink object // // Arguments: // (none) // // Returns: // CConnectionNotifySink::~CConnectionNotifySink() { } // // all we really care about are renaming events hence the rest of the routines // are stubbed out // HRESULT CConnectionNotifySink::ConnectionAdded ( const NETCON_PROPERTIES_EX* pPropsEx) { return E_NOTIMPL; } HRESULT CConnectionNotifySink::ConnectionBandWidthChange ( const GUID* pguidId) { return E_NOTIMPL; } HRESULT CConnectionNotifySink::ConnectionDeleted ( const GUID* pguidId) { return E_NOTIMPL; } HRESULT CConnectionNotifySink::ConnectionModified ( const NETCON_PROPERTIES_EX* pPropsEx) { ProcessNameChange(const_cast(&(pPropsEx->guidId)), pPropsEx->bstrName ); return S_OK; } HRESULT CConnectionNotifySink::ConnectionRenamed ( const GUID* GuidId, LPCWSTR NewName) { ProcessNameChange(( GUID *)GuidId, NewName ); return S_OK; } // CConnectionNotifySink::ConnectionRenamed HRESULT CConnectionNotifySink::ConnectionStatusChange ( const GUID* pguidId, NETCON_STATUS Status) { return E_NOTIMPL; } HRESULT CConnectionNotifySink::RefreshAll () { return E_NOTIMPL; } HRESULT CConnectionNotifySink::ConnectionAddressChange ( const GUID* pguidId ) { return E_NOTIMPL; } HRESULT CConnectionNotifySink::ShowBalloon( IN const GUID* pguidId, IN const BSTR szCookie, IN const BSTR szBalloonText) { return E_NOTIMPL; } HRESULT CConnectionNotifySink::DisableEvents( IN const BOOL fDisable, IN const ULONG ulDisableTimeout) { return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // Function: HrGetNotifyConPoint // // Purpose: Common code for getting the connection point for use in // NotifyAdd and NotifyRemove // // Arguments: // ppConPoint [out] Return ptr for IConnectionPoint // // Returns: // // Author: jeffspr 24 Aug 1998 // // Notes: // HRESULT HrGetNotifyConPoint( IConnectionPoint ** ppConPoint) { HRESULT hr; IConnectionPointContainer * pContainer = NULL; CL_ASSERT(ppConPoint); // Get the debug interface from the connection manager // hr = CoCreateInstance(CLSID_ConnectionManager, NULL, CLSCTX_LOCAL_SERVER, IID_IConnectionPointContainer, (LPVOID*)&pContainer); if (SUCCEEDED(hr)) { IConnectionPoint * pConPoint = NULL; // Get the connection point itself and fill in the return param // on success // hr = pContainer->FindConnectionPoint( IID_INetConnectionNotifySink, &pConPoint); if (SUCCEEDED(hr)) { // // set up a proxy on the connection point interface that will // identify ourselves as ourselves. // hr = CoSetProxyBlanket(pConPoint, RPC_C_AUTHN_WINNT, // use NT default security RPC_C_AUTHZ_NONE, // use NT default authentication NULL, // must be null if default RPC_C_AUTHN_LEVEL_CALL, // call RPC_C_IMP_LEVEL_IMPERSONATE, NULL, // use process token EOAC_NONE); if (SUCCEEDED(hr)) { *ppConPoint = pConPoint; } else { ClRtlLogPrint(LOG_CRITICAL, "[NM] Couldn't set proxy blanket on Net Connection " "point, status %1!08X!.\n", hr); pConPoint->Release(); } } else { ClRtlLogPrint(LOG_CRITICAL, "[NM] Couldn't find notify sink connection point on Net Connection " "Manager, status %1!08X!.\n", hr); } pContainer->Release(); } else { ClRtlLogPrint(LOG_CRITICAL, "[NM] Couldn't establish connection point with Net Connection " "Manager, status %1!08X!.\n", hr); } return hr; } EXTERN_C { HRESULT NmpGrantAccessToNotifySink( VOID ) /*++ Routine Description: allow localsystem, cluster service account and backup operators group access to make callbacks into the service. stolen from private\admin\snapin\netsnap\remrras\server\remrras.cpp and code reviewed by SajiA Arguments: None Return Value: ERROR_SUCCESS if everything went ok. --*/ { IAccessControl* pAccessControl = NULL; SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY; PSID pSystemSid = NULL; HANDLE processToken = NULL; ULONG tokenUserSize; PTOKEN_USER processTokenUser = NULL; DWORD status; PSID pBackupOperatorsSid = NULL; PSID pAdminGroupSid = NULL; HRESULT hr = CoCreateInstance(CLSID_DCOMAccessControl, NULL, CLSCTX_INPROC_SERVER, IID_IAccessControl, (void**)&pAccessControl); if( FAILED( hr ) ) { ClRtlLogPrint(LOG_CRITICAL, "[NM] Couldn't create access control class object " "status 0x%1!08X!.\n", hr); goto Error; } // // Setup the property list. We use the NULL property because we are trying // to adjust the security of the object itself // ACTRL_ACCESSW access; ACTRL_PROPERTY_ENTRYW propEntry; access.cEntries = 1; access.pPropertyAccessList = &propEntry; ACTRL_ACCESS_ENTRY_LISTW entryList; propEntry.lpProperty = NULL; propEntry.pAccessEntryList = &entryList; propEntry.fListFlags = 0; // // Setup the access control list for the default property // ACTRL_ACCESS_ENTRYW entry[3]; entryList.cEntries = 3; entryList.pAccessList = entry; // // Setup the access control entry for localsystem // entry[0].fAccessFlags = ACTRL_ACCESS_ALLOWED; entry[0].Access = COM_RIGHTS_EXECUTE; entry[0].ProvSpecificAccess = 0; entry[0].Inheritance = NO_INHERITANCE; entry[0].lpInheritProperty = NULL; // // NT requires the system account to have access (for launching) // entry[0].Trustee.pMultipleTrustee = NULL; entry[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; entry[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; entry[0].Trustee.TrusteeType = TRUSTEE_IS_USER; // // allocate and init the SYSTEM sid // if ( !AllocateAndInitializeSid( &siaNtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSystemSid ) ) { status = GetLastError(); hr = HRESULT_FROM_WIN32( status ); ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed in allocating and initializing system SID, status %1!u!.\n", status); goto Error; } entry[0].Trustee.ptstrName = ( PWCHAR ) pSystemSid; #if 0 // // Setup the access control entry for cluster service account // entry[1].fAccessFlags = ACTRL_ACCESS_ALLOWED; entry[1].Access = COM_RIGHTS_EXECUTE; entry[1].ProvSpecificAccess = 0; entry[1].Inheritance = NO_INHERITANCE; entry[1].lpInheritProperty = NULL; entry[1].Trustee.pMultipleTrustee = NULL; entry[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; entry[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; entry[1].Trustee.TrusteeType = TRUSTEE_IS_USER; status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_QUERY, &processToken ); if ( !NT_SUCCESS( status ) ) { ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed in opening cluster service process token, status 0x%1!08lx!.\n", status); hr = HRESULT_FROM_NT( status ); goto Error; } // // find out the size of token, allocate and requery to get info // status = NtQueryInformationToken( processToken, TokenUser, NULL, 0, &tokenUserSize ); CL_ASSERT( status == STATUS_BUFFER_TOO_SMALL ); if ( status != STATUS_BUFFER_TOO_SMALL ) { ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed in querying cluster service process token info, status 0x%1!08lx!.\n", status); hr = HRESULT_FROM_NT( status ); goto Error; } processTokenUser = (PTOKEN_USER) LocalAlloc( 0, tokenUserSize ); if (( processToken == NULL ) || ( processTokenUser == NULL ) ) { status = STATUS_INSUFFICIENT_RESOURCES; ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed in memory alloc for cluster service process token, status 0x%1!08lx!.\n", status); hr = HRESULT_FROM_NT( status ); goto Error; } status = NtQueryInformationToken( processToken, TokenUser, processTokenUser, tokenUserSize, &tokenUserSize ); if ( !NT_SUCCESS( status ) ) { ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed in querying cluster service process token info with alloced buffer, status 0x%1!08lx!.\n", status); hr = HRESULT_FROM_NT( status ); goto Error; } entry[1].Trustee.ptstrName = (PWCHAR)processTokenUser->User.Sid; #else // // Setup the access control entry for administrators group // entry[1].fAccessFlags = ACTRL_ACCESS_ALLOWED; entry[1].Access = COM_RIGHTS_EXECUTE; entry[1].ProvSpecificAccess = 0; entry[1].Inheritance = NO_INHERITANCE; entry[1].lpInheritProperty = NULL; entry[1].Trustee.pMultipleTrustee = NULL; entry[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; entry[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; entry[1].Trustee.TrusteeType = TRUSTEE_IS_USER; if ( !AllocateAndInitializeSid( &siaNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminGroupSid ) ) { status = GetLastError(); hr = HRESULT_FROM_WIN32( status ); ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed in allocating and initializing admin group SID, status %1!u!.\n", status); goto Error; } entry[1].Trustee.ptstrName = ( PWCHAR ) pAdminGroupSid; #endif // // Setup the access control entry for backup operators // entry[2].fAccessFlags = ACTRL_ACCESS_ALLOWED; entry[2].Access = COM_RIGHTS_EXECUTE; entry[2].ProvSpecificAccess = 0; entry[2].Inheritance = NO_INHERITANCE; entry[2].lpInheritProperty = NULL; entry[2].Trustee.pMultipleTrustee = NULL; entry[2].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; entry[2].Trustee.TrusteeForm = TRUSTEE_IS_SID; entry[2].Trustee.TrusteeType = TRUSTEE_IS_USER; if ( !AllocateAndInitializeSid( &siaNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_BACKUP_OPS, 0, 0, 0, 0, 0, 0, &pBackupOperatorsSid ) ) { status = GetLastError(); hr = HRESULT_FROM_WIN32( status ); ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed in allocating and initializing backup operators SID, status %1!u!.\n", status); goto Error; } entry[2].Trustee.ptstrName = ( PWCHAR ) pBackupOperatorsSid; // // grant access to this mess // hr = pAccessControl->GrantAccessRights(&access); if( SUCCEEDED(hr) ) { hr = CoInitializeSecurity(pAccessControl, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_ACCESS_CONTROL, NULL); } Error: if( pAccessControl ) { pAccessControl->Release(); } if ( processTokenUser != NULL ) { LocalFree( processTokenUser ); } if ( processToken != NULL ) { NtClose( processToken ); } if( pSystemSid != NULL ) { FreeSid( pSystemSid ); } if ( pBackupOperatorsSid != NULL ) { FreeSid( pBackupOperatorsSid ); } if ( pAdminGroupSid != NULL ) { FreeSid( pAdminGroupSid ); } return hr; } HRESULT NmpInitializeConnectoidAdviseSink( VOID ) /*++ Routine Description: Get an instance pointer to the conn mgr's connection point object and hook up our advice sink so we can catch connectoid rename events Arguments: None Return Value: ERROR_SUCCESS if everything worked... --*/ { HRESULT hr = S_OK; // Not returned, but used for debugging IConnectionPoint * pConPoint = NULL; INetConnectionNotifySink * pSink = NULL; PSECURITY_DESCRIPTOR sinkSD; hr = NmpGrantAccessToNotifySink(); if ( SUCCEEDED( hr )) { hr = HrGetNotifyConPoint(&pConPoint); if (SUCCEEDED(hr)) { // Create the notify sink // hr = CConnectionNotifySink::CreateInstance( IID_INetConnectionNotifySink, (LPVOID*)&pSink); if (SUCCEEDED(hr)) { CL_ASSERT(pSink); hr = pConPoint->Advise(pSink, &AdviseCookie); if ( !SUCCEEDED( hr )) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Couldn't initialize Net Connection Manager advise " "sink, status %1!08X!\n", hr); } pSink->Release(); } else { hr = GetLastError(); ClRtlLogPrint(LOG_UNUSUAL, "[NM] Couldn't create sink instance, status %1!08X!\n", hr); } pConPoint->Release(); } } else { ClRtlLogPrint(LOG_UNUSUAL, "[NM] CoInitializeSecurity failed, status %1!08X!\n", hr); } if ( FAILED( hr )) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Couldn't initialize Net Connection Manager advise " "sink, status %1!08X!\n", hr); AdviseCookie = INVALID_COOKIE; } return hr; } // NmpInitializeConnectoidAdviseSink VOID NmCloseConnectoidAdviseSink( VOID ) /*++ Routine Description: close down the conn mgr event sink. this routine is public since it is called prior to CoUninitialize in ClusterShutdown() Arguments: None Return Value: None --*/ { HRESULT hr = S_OK; IConnectionPoint * pConPoint = NULL; if ( AdviseCookie != INVALID_COOKIE ) { hr = HrGetNotifyConPoint(&pConPoint); if (SUCCEEDED(hr)) { // Unadvise // hr = pConPoint->Unadvise(AdviseCookie); AdviseCookie = INVALID_COOKIE; pConPoint->Release(); } if ( FAILED( hr )) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Couldn't close Net Connection Manager advise sink, status %1!08X!\n", hr); } } } // NmCloseConnectoidAdviseSink VOID ProcessNameChange( GUID * GuidId, LPCWSTR NewName ) /*++ Routine Description: wrapper that enums the net interfaces Arguments: GuidId - pointer to connectoid that changed NewName - pointer to new name of connectoid Return Value: None --*/ { RPC_STATUS rpcStatus; LPWSTR connectoidId = NULL; CL_ASSERT(GuidId); CL_ASSERT(NewName); rpcStatus = UuidToString( (GUID *) GuidId, &connectoidId); if ( rpcStatus == RPC_S_OK ) { PNM_INTERFACE netInterface; DWORD status; PLIST_ENTRY entry; ClRtlLogPrint(LOG_NOISE, "[NM] Received notification that name for connectoid %1!ws! was changed " "to '%2!ws!'\n", connectoidId, NewName); NmpAcquireLock(); // // enum the interfaces, looking for the connectoid GUID as the // adapter ID // for (entry = NmpInterfaceList.Flink; entry != &NmpInterfaceList; entry = entry->Flink ) { netInterface = CONTAINING_RECORD(entry, NM_INTERFACE, Linkage); if ( lstrcmpiW( connectoidId , netInterface->AdapterId ) == 0 ) { PNM_NETWORK network = netInterface->Network; LPWSTR networkName = (LPWSTR) OmObjectName( network ); BOOL nameMatch = (lstrcmpW( networkName, NewName ) == 0); // Ignore this callback if there is a pending GUM update // with a different network name. if (NmpIsNetworkNameChangePending(network)) { if (nameMatch) { // Assume this is the callback we're waiting for. // Clear the pending flag and the timer. network->Flags &= ~NM_FLAG_NET_NAME_CHANGE_PENDING; NmpStartNetworkNameChangePendingTimer(network, 0); } else { // Ignore. NmpReleaseLock(); ClRtlLogPrint(LOG_NOISE, "[NM] Ignoring notification that name for " "connectoid %1!ws! was changed to '%2!ws!' " "because a network name global update " "is pending for network %3!ws!.\n", connectoidId, NewName, networkName ); break; } } if ( !nameMatch ) { NM_NETWORK_INFO netInfo; // // For some reason, OmReferenceObject causes a compiler // error here. Likely a header ordering problem. The // function has been wrappered as a workaround. // NmpReferenceNetwork(network); NmpReleaseLock(); netInfo.Id = (LPWSTR) OmObjectId( network ); netInfo.Name = (LPWSTR) NewName; status = NmpSetNetworkName( &netInfo ); if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( LOG_UNUSUAL, "[NM] Couldn't rename network '%1!ws!' to '%2!ws!', status %3!u!\n", networkName, NewName, status ); //If the error condition is due to the object //already existing revert back to the old name. if(status == ERROR_OBJECT_ALREADY_EXISTS) { DWORD tempStatus = ERROR_SUCCESS; INetConnection *connectoid; ClRtlLogPrint(LOG_UNUSUAL, "[NM] Reverting back network name to '%1!ws!', from '%2!ws!\n", networkName, NewName ); connectoid = ClRtlFindConnectoidByGuid(connectoidId); if(connectoid != NULL){ tempStatus = ClRtlSetConnectoidName( connectoid, networkName); } if((tempStatus != ERROR_SUCCESS) || (connectoid == NULL)) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to set name of network connection" "%1!ws!, status %2!u!\n", networkName, tempStatus); } } } NmpDereferenceNetwork(network); } else { NmpReleaseLock(); } break; } } if ( entry == &NmpInterfaceList ) { NmpReleaseLock(); ClRtlLogPrint(LOG_UNUSUAL, "[NM] Couldn't find net interface for connectoid '%1!ws!'\n", connectoidId ); } RpcStringFree( &connectoidId ); } return; } // ProcessNameChange } // EXTERN_C