681 lines
16 KiB
C
681 lines
16 KiB
C
/*++
|
||
|
||
Copyright (c) 1992-1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
contexts.c
|
||
|
||
Abstract:
|
||
|
||
Contains routines for manipulating SNMP community structures.
|
||
|
||
Environment:
|
||
|
||
User Mode - Win32
|
||
|
||
Revision History:
|
||
|
||
10-Feb-1997 DonRyan
|
||
Rewrote to implement SNMPv2 support.
|
||
|
||
--*/
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Include files //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
#include "globals.h"
|
||
#include "contexts.h"
|
||
#include "snmpthrd.h"
|
||
|
||
#define DYN_REGISTRY_UPDATE 1
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Private procedures //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
BOOL
|
||
AddValidCommunity(
|
||
LPWSTR pCommunity,
|
||
DWORD dwAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds valid community to list.
|
||
|
||
Arguments:
|
||
|
||
pCommunity - pointer to community to add.
|
||
|
||
dwAccess - access rights for community.
|
||
|
||
Return Values:
|
||
|
||
Returns true if successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL fOk = FALSE;
|
||
PCOMMUNITY_LIST_ENTRY pCLE = NULL;
|
||
AsnOctetString CommunityOctets;
|
||
|
||
// initialize octet string info
|
||
CommunityOctets.length = wcslen(pCommunity) * sizeof(WCHAR);
|
||
CommunityOctets.stream = (LPBYTE)pCommunity;
|
||
CommunityOctets.dynamic = FALSE;
|
||
|
||
// attempt to locate in list
|
||
if (FindValidCommunity(&pCLE, &CommunityOctets)) {
|
||
|
||
SNMPDBG((
|
||
SNMP_LOG_TRACE,
|
||
"SNMP: SVC: updating community %s.\n",
|
||
StaticUnicodeToString((LPWSTR)pCommunity)
|
||
));
|
||
|
||
// update access rights
|
||
pCLE->dwAccess = dwAccess;
|
||
|
||
// success
|
||
fOk = TRUE;
|
||
|
||
} else {
|
||
|
||
// allocate community structure
|
||
if (AllocCLE(&pCLE, pCommunity)) {
|
||
|
||
SNMPDBG((
|
||
SNMP_LOG_TRACE,
|
||
"SNMP: SVC: adding community %s.\n",
|
||
CommunityOctetsToString(&(pCLE->Community), TRUE)
|
||
));
|
||
|
||
// insert into valid communities list
|
||
InsertTailList(&g_ValidCommunities, &pCLE->Link);
|
||
|
||
// update access rights
|
||
pCLE->dwAccess = dwAccess;
|
||
|
||
// success
|
||
fOk = TRUE;
|
||
}
|
||
}
|
||
|
||
return fOk;
|
||
}
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Public procedures //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
BOOL
|
||
AllocCLE(
|
||
PCOMMUNITY_LIST_ENTRY * ppCLE,
|
||
LPWSTR pCommunity
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Allocates community structure and initializes.
|
||
|
||
Arguments:
|
||
|
||
ppCLE - pointer to receive pointer to entry.
|
||
|
||
pCommunity - pointer to community string.
|
||
|
||
Return Values:
|
||
|
||
Returns true if successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL fOk = FALSE;
|
||
PCOMMUNITY_LIST_ENTRY pCLE = NULL;
|
||
|
||
// attempt to allocate structure
|
||
pCLE = AgentMemAlloc(sizeof(COMMUNITY_LIST_ENTRY));
|
||
|
||
// validate
|
||
if (pCLE != NULL) {
|
||
|
||
// determine string length
|
||
DWORD nBytes = wcslen(pCommunity) * sizeof(WCHAR);
|
||
|
||
// allocate memory for string (include terminator)
|
||
pCLE->Community.stream = SnmpUtilMemAlloc(nBytes + sizeof(WCHAR));
|
||
|
||
// validate community string stream
|
||
if (pCLE->Community.stream != NULL) {
|
||
|
||
// set length of manager string
|
||
pCLE->Community.length = nBytes;
|
||
|
||
// set memory allocation flag
|
||
pCLE->Community.dynamic = TRUE;
|
||
|
||
// transfer community string into octets
|
||
wcscpy((LPWSTR)(pCLE->Community.stream), pCommunity);
|
||
|
||
// success
|
||
fOk = TRUE;
|
||
|
||
} else {
|
||
|
||
SNMPDBG((
|
||
SNMP_LOG_ERROR,
|
||
"SNMP: SVC: could not copy community string %s.\n",
|
||
StaticUnicodeToString(pCommunity)
|
||
));
|
||
|
||
// release
|
||
FreeCLE(pCLE);
|
||
|
||
// re-init
|
||
pCLE = NULL;
|
||
}
|
||
|
||
} else {
|
||
|
||
SNMPDBG((
|
||
SNMP_LOG_ERROR,
|
||
"SNMP: SVC: could not allocate context entry for %s.\n",
|
||
StaticUnicodeToString(pCommunity)
|
||
));
|
||
}
|
||
|
||
// transfer
|
||
*ppCLE = pCLE;
|
||
|
||
return fOk;
|
||
}
|
||
|
||
|
||
BOOL
|
||
FreeCLE(
|
||
PCOMMUNITY_LIST_ENTRY pCLE
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Releases community structure.
|
||
|
||
Arguments:
|
||
|
||
pCLE - pointer to community list entry to be freed.
|
||
|
||
Return Values:
|
||
|
||
Returns true if successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
// validate pointer
|
||
if (pCLE != NULL) {
|
||
|
||
// release octet string contents
|
||
SnmpUtilOctetsFree(&pCLE->Community);
|
||
|
||
// release structure
|
||
AgentMemFree(pCLE);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
FindValidCommunity(
|
||
PCOMMUNITY_LIST_ENTRY * ppCLE,
|
||
AsnOctetString * pCommunity
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Locates valid community in list.
|
||
|
||
Arguments:
|
||
|
||
ppCLE - pointer to receive pointer to entry.
|
||
|
||
pCommunity - pointer to community to find.
|
||
|
||
Return Values:
|
||
|
||
Returns true if successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY pLE;
|
||
PCOMMUNITY_LIST_ENTRY pCLE;
|
||
|
||
// initialize
|
||
*ppCLE = NULL;
|
||
|
||
// obtain pointer to list head
|
||
pLE = g_ValidCommunities.Flink;
|
||
|
||
// process all entries in list
|
||
while (pLE != &g_ValidCommunities) {
|
||
|
||
// retrieve pointer to community structure
|
||
pCLE = CONTAINING_RECORD(pLE, COMMUNITY_LIST_ENTRY, Link);
|
||
|
||
// compare community string with entry
|
||
if (!SnmpUtilOctetsCmp(&pCLE->Community, pCommunity)) {
|
||
|
||
// transfer
|
||
*ppCLE = pCLE;
|
||
|
||
// success
|
||
return TRUE;
|
||
}
|
||
|
||
// next entry
|
||
pLE = pLE->Flink;
|
||
}
|
||
|
||
// failure
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ParsePermissionMask(
|
||
DWORD bitMask
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Translates the permission mask from the bit-mask format (registry)
|
||
into the internal constant value (constants from public\sdk\inc\snmp.h).
|
||
The function works no longer if:
|
||
- more than sizeof(DWORD)*8 permission values are defined
|
||
- constant values (access policy) changes
|
||
|
||
Arguments:
|
||
|
||
bit-mask.
|
||
|
||
Return Values:
|
||
|
||
permission's constant value.
|
||
|
||
--*/
|
||
{
|
||
DWORD dwPermission;
|
||
|
||
for(dwPermission = 0; (bitMask & ((DWORD)(-1)^1)) != 0; bitMask>>=1, dwPermission++);
|
||
|
||
return dwPermission;
|
||
}
|
||
|
||
#ifdef DYN_REGISTRY_UPDATE
|
||
LONG UpdateRegistry(
|
||
HKEY hKey,
|
||
LPWSTR wszBogus,
|
||
LPWSTR wszCommunity
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Updates the registry configuration in order to be able to associate
|
||
permission masks to each community:
|
||
name type data
|
||
old format: <whatever> REG_SZ community_name
|
||
new format: community_name REG_DWORD permission_mask
|
||
Arguments:
|
||
|
||
hKey - open handle to the key that contains the value
|
||
szBogus - old format value name; useless data
|
||
szCommunity - pointer to community name, as it was specified in the old format.
|
||
|
||
Return Values:
|
||
|
||
Returns ERROR_SUCCESS if successful.
|
||
|
||
--*/
|
||
{
|
||
LONG lStatus;
|
||
DWORD dwDataSize = MAX_PATH;
|
||
DWORD dwDataType;
|
||
|
||
// make sure the update was not tried (and breaked) before
|
||
dwDataSize = sizeof(DWORD);
|
||
lStatus = RegQueryValueExW(
|
||
hKey,
|
||
wszCommunity,
|
||
0,
|
||
&dwDataType,
|
||
NULL,
|
||
&dwDataSize);
|
||
|
||
// if no previous (breaked) update, convert community to the new format
|
||
if (lStatus != ERROR_SUCCESS || dwDataType != REG_DWORD)
|
||
{
|
||
// permissions to be assigned to community
|
||
DWORD dwPermissionMask;
|
||
|
||
// all communities that are converted to new format at this point,
|
||
// are converted to READ-CREATE permissions to ensure same functionality as
|
||
// the permisionless communities.
|
||
dwPermissionMask = 1 << SNMP_ACCESS_READ_CREATE;
|
||
|
||
// set the new format value
|
||
lStatus = RegSetValueExW(
|
||
hKey,
|
||
wszCommunity,
|
||
0,
|
||
REG_DWORD,
|
||
(CONST BYTE *)&dwPermissionMask,
|
||
sizeof(DWORD));
|
||
|
||
if (lStatus != ERROR_SUCCESS)
|
||
return lStatus;
|
||
}
|
||
|
||
// delete the old format value
|
||
lStatus = RegDeleteValueW(
|
||
hKey,
|
||
wszBogus);
|
||
|
||
return lStatus;
|
||
}
|
||
#endif
|
||
|
||
|
||
BOOL
|
||
LoadValidCommunities(
|
||
BOOL bFirstCall
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Constructs list of valid communities.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Values:
|
||
|
||
Returns true if successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
HKEY hKey;
|
||
LONG lStatus;
|
||
DWORD dwIndex;
|
||
WCHAR wszName[MAX_PATH]; // get the UNICODE encoding for szName
|
||
CHAR szName[3*MAX_PATH]; // buffer for holding the translation UNICODE->UTF8
|
||
DWORD dwNameSize;
|
||
DWORD dwDataType;
|
||
WCHAR wszData[MAX_PATH]; // get the UNICODE encoding for szData
|
||
DWORD dwDataSize;
|
||
BOOL fPolicy;
|
||
LPTSTR pszKey;
|
||
BOOL fOk = FALSE;
|
||
|
||
SNMPDBG((
|
||
SNMP_LOG_TRACE,
|
||
"SNMP: SVC: loading valid communities.\n"
|
||
));
|
||
|
||
#ifdef _POLICY
|
||
// we need to provide precedence to the parameters set through the policy
|
||
fPolicy = TRUE;
|
||
#else
|
||
fPolicy = FALSE;
|
||
#endif
|
||
|
||
do
|
||
{
|
||
// if the policy is to be enforced, check the policy registry location first
|
||
pszKey = fPolicy ? REG_POLICY_VALID_COMMUNITIES : REG_KEY_VALID_COMMUNITIES;
|
||
|
||
// open registry subkey
|
||
lStatus = RegOpenKeyEx(
|
||
HKEY_LOCAL_MACHINE,
|
||
pszKey,
|
||
0,
|
||
#ifdef DYN_REGISTRY_UPDATE
|
||
bFirstCall ? KEY_READ | KEY_SET_VALUE : KEY_READ,
|
||
#else
|
||
KEY_READ,
|
||
#endif
|
||
&hKey
|
||
);
|
||
|
||
// if the call succeeded or we were not checking the policy, break the loop
|
||
if (lStatus == ERROR_SUCCESS || !fPolicy)
|
||
break;
|
||
|
||
// being at this point, this means we were checking for the policy parameters.
|
||
// If and only if the policy is not defined (registry key is missing) we
|
||
// reset the error, mark 'fPolicy already tried' and go back into the loop
|
||
if (lStatus == ERROR_FILE_NOT_FOUND)
|
||
{
|
||
lStatus = ERROR_SUCCESS;
|
||
fPolicy = FALSE;
|
||
}
|
||
} while (lStatus == ERROR_SUCCESS);
|
||
|
||
|
||
// validate return code
|
||
if (lStatus == ERROR_SUCCESS) {
|
||
|
||
// initialize
|
||
dwIndex = 0;
|
||
|
||
// loop until error or end of list
|
||
for (dwIndex = 0;
|
||
lStatus == ERROR_SUCCESS;
|
||
dwIndex++)
|
||
|
||
{
|
||
// initialize buffer sizes
|
||
dwNameSize = sizeof(wszName) / sizeof(wszName[0]); // size in number of WCHARs, not the size in bytes
|
||
dwDataSize = sizeof(wszData); // size in number of bytes
|
||
|
||
// read next value
|
||
lStatus = RegEnumValueW(
|
||
hKey,
|
||
dwIndex,
|
||
wszName,
|
||
&dwNameSize,
|
||
NULL,
|
||
&dwDataType,
|
||
(LPBYTE)wszData,
|
||
&dwDataSize
|
||
);
|
||
|
||
// validate return code
|
||
if (lStatus == ERROR_SUCCESS) {
|
||
|
||
// dynamically update values that are not of DWORD type
|
||
if (dwDataType != REG_DWORD)
|
||
{
|
||
#ifdef DYN_REGISTRY_UPDATE
|
||
if (dwDataType == REG_SZ)
|
||
{
|
||
if (bFirstCall)
|
||
{
|
||
if(UpdateRegistry(hKey, wszName, wszData) == ERROR_SUCCESS)
|
||
{
|
||
SNMPDBG((
|
||
SNMP_LOG_WARNING,
|
||
"SNMP: SVC: updated community registration\n"
|
||
));
|
||
|
||
// current value has been deleted, need to keep the index
|
||
dwIndex--;
|
||
continue;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
SNMPDBG((
|
||
SNMP_LOG_WARNING,
|
||
"SNMP: SVC: old format community to be considered with full rights"));
|
||
|
||
wcscpy(wszName, wszData);
|
||
*(DWORD *)wszData = (1 << SNMP_ACCESS_READ_CREATE);
|
||
}
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
SNMPDBG((
|
||
SNMP_LOG_WARNING,
|
||
"SNMP: SVC: wrong format in ValidCommunities[%d] registry entry\n",
|
||
dwIndex
|
||
));
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// convert the UNICODE representation to UTF8 representation
|
||
//dwNameSize = WideCharToMultiByte(
|
||
// CP_UTF8,
|
||
// 0,
|
||
// wszName,
|
||
// wcslen(wszName),
|
||
// szName,
|
||
// sizeof(szName),
|
||
// NULL,
|
||
// NULL);
|
||
|
||
// if error, just skip this community
|
||
//if (dwNameSize == 0)
|
||
//{
|
||
// SNMPDBG((
|
||
// SNMP_LOG_WARNING,
|
||
// "SNMP: SVC: community conversion to UTF8 failed with error %d.\n", GetLastError()));
|
||
// continue;
|
||
//}
|
||
|
||
// put the '\0' terminator to the string
|
||
//szName[dwNameSize] = '\0';
|
||
|
||
// add valid community to list with related permissions
|
||
//if (AddValidCommunity(szName, ParsePermissionMask(*(DWORD *)wszData)))
|
||
if (AddValidCommunity(wszName, ParsePermissionMask(*(DWORD *)wszData)))
|
||
{
|
||
|
||
SNMPDBG((
|
||
SNMP_LOG_WARNING,
|
||
"SNMP: SVC: rights set to %d for community '%s'\n",
|
||
*(DWORD *)wszData,
|
||
StaticUnicodeToString(wszName)
|
||
));
|
||
|
||
}
|
||
else
|
||
{
|
||
// reset status to reflect failure
|
||
lStatus = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
}
|
||
else if (lStatus == ERROR_NO_MORE_ITEMS)
|
||
{
|
||
// success
|
||
fOk = TRUE;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
// it doesn't matter how the values are, the key has to exist,
|
||
// so mark as bFirstCall in order to log an event if this is not true.
|
||
bFirstCall = TRUE;
|
||
|
||
if (!fOk) {
|
||
|
||
SNMPDBG((
|
||
SNMP_LOG_ERROR,
|
||
"SNMP: SVC: error %d processing ValidCommunities subkey.\n",
|
||
lStatus
|
||
));
|
||
|
||
// report an event only for the first call (initialization of the service).
|
||
// otherwise subsequent registry ops through regedit might flood the event log with
|
||
// unsignificant records
|
||
if (bFirstCall)
|
||
// report event
|
||
ReportSnmpEvent(
|
||
SNMP_EVENT_INVALID_REGISTRY_KEY,
|
||
1,
|
||
&pszKey,
|
||
lStatus
|
||
);
|
||
}
|
||
return fOk;
|
||
}
|
||
|
||
|
||
BOOL
|
||
UnloadValidCommunities(
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Destroys list of valid communities.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Values:
|
||
|
||
Returns true if successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY pLE;
|
||
PCOMMUNITY_LIST_ENTRY pCLE;
|
||
|
||
// process entries until list is empty
|
||
while (!IsListEmpty(&g_ValidCommunities)) {
|
||
|
||
// extract next entry from head of list
|
||
pLE = RemoveHeadList(&g_ValidCommunities);
|
||
|
||
// retrieve pointer to community structure
|
||
pCLE = CONTAINING_RECORD(pLE, COMMUNITY_LIST_ENTRY, Link);
|
||
|
||
// release
|
||
FreeCLE(pCLE);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|