/*++ Copyright (c) 1992-1997 Microsoft Corporation Module Name: startup.c Abstract: Contains routines for starting SNMP master agent. Environment: User Mode - Win32 Revision History: 10-Feb-1997 DonRyan Rewrote to implement SNMPv2 support. --*/ /////////////////////////////////////////////////////////////////////////////// // // // Include files // // // /////////////////////////////////////////////////////////////////////////////// #include "globals.h" #include "startup.h" #include "network.h" #include "registry.h" #include "snmpthrd.h" #include "regthrd.h" #include "trapthrd.h" #include "args.h" #include "mem.h" #include "snmpmgmt.h" /////////////////////////////////////////////////////////////////////////////// // // // Global variables // // // /////////////////////////////////////////////////////////////////////////////// HANDLE g_hAgentThread = NULL; HANDLE g_hRegistryThread = NULL; // Used to track registry changes CRITICAL_SECTION g_RegCriticalSectionA; CRITICAL_SECTION g_RegCriticalSectionB; CRITICAL_SECTION g_RegCriticalSectionC; // protect the generation of trap from // registry changes // All CriticalSection inited or not, this flag is only used in this file. static BOOL g_fCriticalSectionsInited = FALSE; // privileges that we want to keep static const LPCWSTR c_arrszPrivilegesToKeep[] = { L"SeChangeNotifyPrivilege", L"SeSecurityPrivilege" }; /////////////////////////////////////////////////////////////////////////////// // // // Private procedures // // // /////////////////////////////////////////////////////////////////////////////// BOOL LoadWinsock( ) /*++ Routine Description: Startup winsock. Arguments: None. Return Values: Returns true if successful. --*/ { WSADATA WsaData; WORD wVersionRequested = MAKEWORD(2,0); INT nStatus; // attempt to startup winsock nStatus = WSAStartup(wVersionRequested, &WsaData); // validate return code if (nStatus == SOCKET_ERROR) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: error %d starting winsock.\n", WSAGetLastError() )); // failure return FALSE; } // success return TRUE; } BOOL UnloadWinsock( ) /*++ Routine Description: Shutdown winsock. Arguments: None. Return Values: Returns true if successful. --*/ { INT nStatus; // cleanup nStatus = WSACleanup(); // validate return code if (nStatus == SOCKET_ERROR) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: error %d stopping winsock.\n", WSAGetLastError() )); // failure return FALSE; } // success return TRUE; } /*++ Routine Description: Get all privileges to be removed except the ones specified in c_arrszPrivilegesToKeep. Arguments: hToken [IN] An opened access token ppPrivs [OUT] An array of privileges to be returned pdwNumPrivs [OUT] Number of privileges in ppPrivs Return Values: Returns ERROR_SUCCESS if successful. Note: It is caller's responsibility to call AgentMemFree on *ppPrivs if ERROR_SUCCESS is returned and *ppPrivs is not NULL --*/ static DWORD GetAllPrivilegesToRemoveExceptNeeded( HANDLE hToken, PLUID_AND_ATTRIBUTES* ppPrivs, PDWORD pdwNumPrivs) { DWORD dwRet = ERROR_SUCCESS; PTOKEN_PRIVILEGES pTokenPrivs = NULL; PLUID_AND_ATTRIBUTES pPrivsToRemove = NULL; DWORD dwPrivsToDel = 0; DWORD dwPrivNameSize = 0; // init return values *ppPrivs = NULL; *pdwNumPrivs = 0; do { LPWSTR pszPrivName = NULL; DWORD i, dwSize = 0; GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize); if (0 == dwSize) { // process has no privileges to be removed return dwRet; } pTokenPrivs = (PTOKEN_PRIVILEGES) AgentMemAlloc(dwSize); if (NULL == pTokenPrivs) { dwRet = ERROR_OUTOFMEMORY; break; } if (!GetTokenInformation( hToken, TokenPrivileges, pTokenPrivs, dwSize, &dwSize)) { dwRet = GetLastError(); break; } pPrivsToRemove = (PLUID_AND_ATTRIBUTES) AgentMemAlloc( sizeof(LUID_AND_ATTRIBUTES) * pTokenPrivs->PrivilegeCount); if (NULL == pPrivsToRemove) { dwRet = ERROR_OUTOFMEMORY; break; } // LookupPrivilegeName need the buffer size in char and NOT including // the NULL terminator dwPrivNameSize = MAX_PATH; pszPrivName = (LPWSTR) AgentMemAlloc((dwPrivNameSize + 1) * sizeof(WCHAR)); if (NULL == pszPrivName) { dwRet = ERROR_OUTOFMEMORY; break; } for (i=0; i < pTokenPrivs->PrivilegeCount; i++) { BOOL bFound; DWORD j; DWORD dwTempSize = dwPrivNameSize; ZeroMemory(pszPrivName, (dwPrivNameSize + 1) * sizeof(WCHAR)); if (!LookupPrivilegeNameW(NULL, &pTokenPrivs->Privileges[i].Luid, pszPrivName, &dwTempSize)) { dwRet = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER == dwRet && dwTempSize > dwPrivNameSize) { //reallocate a bigger buffer dwRet = ERROR_SUCCESS; AgentMemFree(pszPrivName); pszPrivName = (LPWSTR) AgentMemAlloc((dwTempSize + 1) * sizeof(WCHAR)); if (NULL == pszPrivName) { dwRet = ERROR_OUTOFMEMORY; break; } // AgentMemAlloc zero'ed the allocated memory dwPrivNameSize = dwTempSize; //try it again if (!LookupPrivilegeNameW(NULL, &pTokenPrivs->Privileges[i].Luid, pszPrivName, &dwTempSize)) { dwRet = GetLastError(); break; } } else { break; } } bFound = FALSE; for (j = 0; j < sizeof(c_arrszPrivilegesToKeep)/sizeof(c_arrszPrivilegesToKeep[0]); ++j) { if (0 == lstrcmpiW(pszPrivName, c_arrszPrivilegesToKeep[j])) { bFound = TRUE; break; } } if (bFound) continue; pPrivsToRemove[dwPrivsToDel] = pTokenPrivs->Privileges[i]; dwPrivsToDel++; } // free memory if necessary AgentMemFree(pszPrivName); } while (FALSE); // free memory if necessary AgentMemFree(pTokenPrivs); if (ERROR_SUCCESS == dwRet) { // transfer values *pdwNumPrivs = dwPrivsToDel; *ppPrivs = pPrivsToRemove; } else if (pPrivsToRemove) { AgentMemFree(pPrivsToRemove); } return dwRet; } /*++ Routine Description: BuildTokenPrivileges Arguments: pPrivs [IN] An array of privileges dwNumPrivs [IN] Number of privileges in pPrivs ppTokenPrivs [OUT] Pointer to TOKEN_PRIVILEGES to be returned pdwTokenPrivsBufferSize [OUT] Buffer size in bytes that *ppTokenPrivs has Return Values: Returns ERROR_SUCCESS if successful. Note: It's caller's responsibility to call AgentMemFree on *ppPrivs if ERROR_SUCCESS is returned and *ppPrivs is not NULL --*/ static DWORD BuildTokenPrivileges( PLUID_AND_ATTRIBUTES pPrivs, DWORD dwNumPrivs, DWORD dwAttributes, PTOKEN_PRIVILEGES* ppTokenPrivs, PDWORD pdwTokenPrivsBufferSize) { PTOKEN_PRIVILEGES pTokenPrivs = NULL; DWORD i, dwBufferSize; // design by contract, parameters have to be valid. e.g. // dwNumPrivs > 0 // *ppTokenPrivs == NULL // *pdwTokenPrivsBufferSize == 0 // allocate the privilege buffer dwBufferSize = sizeof(TOKEN_PRIVILEGES) + ((dwNumPrivs-1) * sizeof(LUID_AND_ATTRIBUTES)); pTokenPrivs = (PTOKEN_PRIVILEGES) AgentMemAlloc(dwBufferSize); if (NULL == pTokenPrivs) { return ERROR_OUTOFMEMORY; } // build the desired TOKEN_PRIVILEGES pTokenPrivs->PrivilegeCount = dwNumPrivs; for (i = 0; i < dwNumPrivs; ++i) { pTokenPrivs->Privileges[i].Luid = pPrivs[i].Luid; pTokenPrivs->Privileges[i].Attributes = dwAttributes; } // transfer values *ppTokenPrivs = pTokenPrivs; *pdwTokenPrivsBufferSize = dwBufferSize; return ERROR_SUCCESS; } /*++ Routine Description: RemoveUnnecessaryTokenPrivileges Arguments: Return Values: Returns TRUE if successful. --*/ static BOOL RemoveUnnecessaryTokenPrivileges() { DWORD dwRet = ERROR_SUCCESS; HANDLE hProcessToken = NULL; PLUID_AND_ATTRIBUTES pPrivsToRemove = NULL; DWORD dwNumPrivs = 0; PTOKEN_PRIVILEGES pTokenPrivs = NULL; DWORD dwTokenPrivsBufferSize = 0; do { if (!OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken )) { dwRet = GetLastError(); break; } dwRet = GetAllPrivilegesToRemoveExceptNeeded(hProcessToken, &pPrivsToRemove, &dwNumPrivs); if (ERROR_SUCCESS != dwRet ) { break; } // Assert: dwRet == ERROR_SUCCESS if ( (NULL==pPrivsToRemove) || (0==dwNumPrivs) ) { SNMPDBG(( SNMP_LOG_VERBOSE, "SNMP: SVC: No privileges need to be removed.\n" )); break; } dwRet = BuildTokenPrivileges(pPrivsToRemove, dwNumPrivs, SE_PRIVILEGE_REMOVED, &pTokenPrivs, &dwTokenPrivsBufferSize); if (ERROR_SUCCESS != dwRet ) { break; } if (!AdjustTokenPrivileges(hProcessToken, FALSE, pTokenPrivs, dwTokenPrivsBufferSize, NULL, NULL)) { dwRet = GetLastError(); break; } } while(FALSE); // free resources if necessary if (hProcessToken) CloseHandle(hProcessToken); AgentMemFree(pPrivsToRemove); AgentMemFree(pTokenPrivs); if (dwRet != ERROR_SUCCESS) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: RemoveUnnecessaryTokenPrivileges failed 0x%x\n", dwRet)); return FALSE; } else return TRUE; } /////////////////////////////////////////////////////////////////////////////// // // // Public procedures // // // /////////////////////////////////////////////////////////////////////////////// BOOL StartupAgent( ) /*++ Routine Description: Performs essential initialization of master agent. Arguments: None. Return Values: Returns true if successful. --*/ { BOOL fOk = TRUE; DWORD dwThreadId = 0; DWORD regThreadId = 0; INT nCSOk = 0; // counts the number of CS that were successfully initialized // initialize management variables mgmtInit(); // initialize list heads InitializeListHead(&g_Subagents); InitializeListHead(&g_SupportedRegions); InitializeListHead(&g_ValidCommunities); InitializeListHead(&g_TrapDestinations); InitializeListHead(&g_PermittedManagers); InitializeListHead(&g_IncomingTransports); InitializeListHead(&g_OutgoingTransports); __try { InitializeCriticalSection(&g_RegCriticalSectionA); nCSOk++; InitializeCriticalSection(&g_RegCriticalSectionB); nCSOk++; InitializeCriticalSection(&g_RegCriticalSectionC); nCSOk++; } __except(EXCEPTION_EXECUTE_HANDLER) { if (nCSOk == 1) DeleteCriticalSection(&g_RegCriticalSectionA); if (nCSOk == 2) { DeleteCriticalSection(&g_RegCriticalSectionA); DeleteCriticalSection(&g_RegCriticalSectionB); } // nCSOk can't be 3 as far as we are here fOk = FALSE; } SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: Initialize critical sections...%s\n", fOk? "Ok" : "Failed")); if (fOk) { g_fCriticalSectionsInited = TRUE; } fOk = fOk && (g_hRegistryEvent = CreateEvent(NULL, FALSE, TRUE, NULL)) != NULL; g_dwUpTimeReference = SnmpSvcInitUptime(); // retreive system uptime reference SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: Getting system uptime...%d\n", g_dwUpTimeReference)); // allocate essentials fOk = fOk && AgentHeapCreate(); SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: Creating agent heap...%s\n", fOk? "Ok" : "Failed")); if (fOk) { // Remove unnecessary privileges from the service RemoveUnnecessaryTokenPrivileges(); // any error is ignored } fOk = fOk && LoadWinsock(); SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: Loading Winsock stack...%s\n", fOk? "Ok" : "Failed")); fOk = fOk && LoadIncomingTransports(); SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: Loading Incoming transports...%s\n", fOk? "Ok" : "Failed")); fOk = fOk && LoadOutgoingTransports(); SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: Loading Outgoing transports...%s\n", fOk? "Ok" : "Failed")); fOk = fOk && // attempt to start main thread (g_hAgentThread = CreateThread( NULL, // lpThreadAttributes 0, // dwStackSize ProcessSnmpMessages, NULL, // lpParameter CREATE_SUSPENDED, // dwCreationFlags &dwThreadId )) != NULL; SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: Starting ProcessSnmpMessages thread...%s\n", fOk? "Ok" : "Failed")); fOk = fOk && // attempt to start registry listener thread (g_hRegistryThread = CreateThread( NULL, 0, ProcessRegistryMessage, NULL, CREATE_SUSPENDED, ®ThreadId)) != NULL; SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: Starting ProcessRegistryMessages thread...%s\n", fOk? "Ok" : "Failed")); return fOk; } BOOL ShutdownAgent( ) /*++ Routine Description: Performs final cleanup of master agent. Arguments: None. Return Values: Returns true if successful. --*/ { BOOL fOk; DWORD dwStatus; // make sure shutdown signalled fOk = SetEvent(g_hTerminationEvent); if (!fOk) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: error %d signalling termination.\n", GetLastError() )); } // check if thread created if ((g_hAgentThread != NULL) && (g_hRegistryThread != NULL)) { HANDLE hEvntArray[2]; hEvntArray[0] = g_hAgentThread; hEvntArray[1] = g_hRegistryThread; dwStatus = WaitForMultipleObjects(2, hEvntArray, TRUE, SHUTDOWN_WAIT_HINT); // validate return status if ((dwStatus != WAIT_OBJECT_0) && (dwStatus != WAIT_OBJECT_0 + 1) && (dwStatus != WAIT_TIMEOUT)) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: error %d waiting for thread(s) termination.\n", GetLastError() )); } } else if (g_hAgentThread != NULL) { // wait for pdu processing thread to terminate dwStatus = WaitForSingleObject(g_hAgentThread, SHUTDOWN_WAIT_HINT); // validate return status if ((dwStatus != WAIT_OBJECT_0) && (dwStatus != WAIT_TIMEOUT)) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: error %d waiting for main thread termination.\n", GetLastError() )); } } else if (g_hRegistryThread != NULL) { // wait for registry processing thread to terminate dwStatus = WaitForSingleObject(g_hRegistryThread, SHUTDOWN_WAIT_HINT); // validate return status if ((dwStatus != WAIT_OBJECT_0) && (dwStatus != WAIT_TIMEOUT)) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: error %d waiting for registry thread termination.\n", GetLastError() )); } } if (g_fCriticalSectionsInited) { // in case registry processing thread hasn't terminated yet, we need // to make sure critical section are safe for deletion and // common resources in UnloadRegistryParameters() are still protected. EnterCriticalSection(&g_RegCriticalSectionA); EnterCriticalSection(&g_RegCriticalSectionB); EnterCriticalSection(&g_RegCriticalSectionC); } // unload incoming transports UnloadIncomingTransports(); // unload outgoing transports UnloadOutgoingTransports(); // unload registry info UnloadRegistryParameters(); // unload the winsock stack UnloadWinsock(); // cleanup the internal management buffers mgmtCleanup(); // nuke private heap AgentHeapDestroy(); if (g_fCriticalSectionsInited) { LeaveCriticalSection(&g_RegCriticalSectionC); LeaveCriticalSection(&g_RegCriticalSectionB); LeaveCriticalSection(&g_RegCriticalSectionA); // clean up critical section resources DeleteCriticalSection(&g_RegCriticalSectionA); DeleteCriticalSection(&g_RegCriticalSectionB); DeleteCriticalSection(&g_RegCriticalSectionC); } ReportSnmpEvent( SNMP_EVENT_SERVICE_STOPPED, 0, NULL, 0); return TRUE; }