windows-nt/Source/XPSP1/NT/windows/appcompat/sdbapi/sdbapi.c
2020-09-26 16:20:57 +08:00

2342 lines
68 KiB
C

/*++
Copyright (c) 1989-2000 Microsoft Corporation
Module Name:
sdbapi.c
Abstract:
BUGBUG: This module implements ...
Author:
dmunsil created sometime in 1999
Revision History:
several people contributed (vadimb, clupu, ...)
--*/
#include "sdbp.h"
#include "initguid.h"
DEFINE_GUID(GUID_SYSMAIN_SDB, 0x11111111, 0x1111, 0x1111, 0x11, 0x11, 0x11, 0x11, \
0x11, 0x11, 0x11, 0x11);
DEFINE_GUID(GUID_APPHELP_SDB, 0x22222222, 0x2222, 0x2222, 0x22, 0x22, 0x22, 0x22, \
0x22, 0x22, 0x22, 0x22);
DEFINE_GUID(GUID_SYSTEST_SDB, 0x33333333, 0x3333, 0x3333, 0x33, 0x33, 0x33, 0x33, \
0x33, 0x33, 0x33, 0x33);
DEFINE_GUID(GUID_DRVMAIN_SDB, 0xF9AB2228, 0x3312, 0x4A73, 0xB6, 0xF9, 0x93, 0x6D, \
0x70, 0xE1, 0x12, 0xEF);
DEFINE_GUID(GUID_MSIMAIN_SDB, 0xD8FF6D16, 0x6A3A, 0x468A, 0x8B, 0x44, 0x01, 0x71, \
0x4D, 0xDC, 0x49, 0xEA);
DEFINE_GUID(GUID_APPHELP_SP_SDB, 0x44444444, 0x4444, 0x4444, 0x44, 0x44, 0x44, 0x44, \
0x44, 0x44, 0x44, 0x44);
#ifdef _DEBUG_SPEW
//
// Shim Debug output support
//
int g_iShimDebugLevel = SHIM_DEBUG_UNINITIALIZED;
DBGLEVELINFO g_rgDbgLevelInfo[DEBUG_LEVELS] = {
{ "Err", sdlError },
{ "Warn", sdlWarning },
{ "Fail", sdlFail },
{ "Info", sdlInfo }
};
PCH g_szDbgLevelUser = "User";
#endif // _DEBUG_SPEW
BOOL
SdbpInitializeSearchDBContext(
PSEARCHDBCONTEXT pContext
);
#if defined(KERNEL_MODE) && defined(ALLOC_PRAGMA)
#pragma alloc_text(PAGE, ShimExceptionHandler)
#pragma alloc_text(PAGE, SdbpCreateSearchDBContext)
#pragma alloc_text(PAGE, SdbpInitializeSearchDBContext)
#pragma alloc_text(PAGE, SdbpReleaseSearchDBContext)
#pragma alloc_text(PAGE, SdbpCheckForMatch)
#pragma alloc_text(PAGE, SdbpSearchDB)
#pragma alloc_text(PAGE, SdbpCreateSearchDBContext)
#pragma alloc_text(PAGE, SdbGetDatabaseMatch)
#pragma alloc_text(PAGE, SdbQueryData)
#pragma alloc_text(PAGE, SdbQueryDataEx)
#pragma alloc_text(PAGE, SdbReadEntryInformation)
#pragma alloc_text(PAGE, PrepareFormatForUnicode)
#pragma alloc_text(PAGE, ShimDbgPrint)
#endif
#if DBG
const BOOL g_bDBG = TRUE;
#else
const BOOL g_bDBG = FALSE;
#endif
//
// Exception handler
//
ULONG
ShimExceptionHandler(
PEXCEPTION_POINTERS pexi,
char* szFile,
DWORD dwLine
)
{
#ifndef KERNEL_MODE // in kmode exceptions won't work anyway
DBGPRINT((sdlError,
"ShimExceptionHandler",
"Shim Exception %#x in module \"%hs\", line %d, at address %#p. flags:%#x. !exr %#p !cxr %#p",
pexi->ExceptionRecord->ExceptionCode,
szFile,
dwLine,
CONTEXT_TO_PROGRAM_COUNTER(pexi->ContextRecord),
pexi->ExceptionRecord->ExceptionFlags,
pexi->ExceptionRecord,
pexi->ContextRecord));
//
// Special-case stack overflow exception which is likely to occur due to
// low memory conditions during stress. The process is dead anyway so we
// will not handle this exception.
//
if (pexi->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
return EXCEPTION_CONTINUE_SEARCH;
}
#if DBG
SDB_BREAK_POINT();
#endif // DBG
#endif // KERNEL_MODE
return EXCEPTION_EXECUTE_HANDLER;
}
BOOL
SdbpResolveAndSplitPath(
IN DWORD dwFlags, // context flags (SEARCHDBF_NO_LFN in particular)
IN LPCTSTR szFullPath, // a full UNC or DOS path & filename, "c:\foo\myfile.ext"
OUT LPTSTR szDir, // the drive and dir portion of the filename "c:\foo\"
OUT LPTSTR szName, // the filename portion "myfile"
OUT LPTSTR szExt // the extension portion ".ext"
)
/*++
Return: TRUE on success, FALSE otherwise.
Desc: This function takes a full path and splits it into pieces ala splitpath,
but also converts short file names to long names.
NOTE: The caller is responsible for allocating enough space
for the passed-in strings to take any portion of the path.
For safety, allocate at least MAX_PATH WCHARS for each piece.
--*/
{
TCHAR* szCursor;
TCHAR szLongFileName[MAX_PATH + 1];
BOOL bFound;
DWORD i;
assert(szFullPath && szDir && szName && szExt);
//
// Parse the directory.
//
szDir[0] = _T('\0');
szCursor = _tcsrchr(szFullPath, _T('\\')); // last backslash please
if (szCursor == NULL) {
szCursor = (LPTSTR)szFullPath;
} else {
_tcsncpy(szDir, szFullPath, szCursor - szFullPath + 1);
szDir[szCursor - szFullPath + 1] = _T('\0');
}
#ifndef KERNEL_MODE
//
// Make sure we're using the long filename
//
if (dwFlags & SEARCHDBF_NO_LFN) {
assert(_tcslen(szCursor) < CHARCOUNT(szLongFileName));
_tcscpy(szLongFileName, szCursor);
} else {
if (!SdbpGetLongFileName(szFullPath, szLongFileName)) {
return FALSE;
}
}
#else // KERNEL_MODE
//
// When we are in kernel mode, our file name is always considered to be "long".
// At this point szCursor points to the last '\\' or to the beginning of the name.
//
if (*szCursor == _T('\\')) {
++szCursor;
}
//
// Make sure that we have enough room for the name.
//
assert(wcslen(szCursor) < CHARCOUNT(szLongFileName));
wcscpy(szLongFileName, szCursor);
#endif // KERNEL_MODE
//
// Parse name & extension
//
szExt[0] = _T('\0');
szName[0] = _T('\0');
//
// Within the long file name find the last dot
//
szCursor = _tcsrchr(szLongFileName, _T('.'));
if (szCursor != NULL) {
_tcsncpy(szName, szLongFileName, szCursor - szLongFileName);
szName [szCursor-szLongFileName] = _T('\0');
_tcscpy(szExt, szCursor);
} else {
_tcscpy(szName, szLongFileName);
}
return TRUE;
}
BOOL
SdbpCreateSearchDBContext(
PSEARCHDBCONTEXT pContext,
LPCTSTR szPath,
LPCTSTR szModuleName,
LPCTSTR pEnvironment
)
/*++
Return: TRUE - search db context was successfully created
Desc: This function creates context for searching the database, in particular, the
context is initalized with the path of probable local database location,
executable path is broken down into containing directory and the filename part.
--*/
{
DWORD dwPathLen;
BOOL bReturn = FALSE;
TCHAR* szDirectory = NULL;
TCHAR* szExt = NULL;
TCHAR* szFullName = NULL;
TCHAR* szFileName = NULL;
TCHAR* szModule = NULL;
assert(NULL != szPath);
assert(NULL != pContext);
dwPathLen = _tcslen(szPath);
//
// Allocate enough to guarantee our strings will not overflow
//
szDirectory = SdbAlloc((dwPathLen + 1) * sizeof(TCHAR));
szFullName = SdbAlloc((_MAX_PATH + 1) * sizeof(TCHAR));
if (szModuleName) {
szModule = SdbAlloc((_tcslen(szModuleName) + 1) * sizeof(TCHAR));
if (!szModule) {
DBGPRINT((sdlError,
"SdbpCreateSearchDBContext",
"Unable to allocate memory for szModule.\n"));
goto out;
}
_tcscpy(szModule, szModuleName);
}
STACK_ALLOC(szExt, (_MAX_PATH + 1) * sizeof(TCHAR));
STACK_ALLOC(szFileName, (_MAX_PATH + 1) * sizeof(TCHAR));
if (!szDirectory || !szExt || !szFullName || !szFileName || !pContext) {
DBGPRINT((sdlError,
"SdbpCreateSearchDBContext",
"Unable to allocate memory for strings.\n"));
goto out;
}
if (!SdbpResolveAndSplitPath(pContext->dwFlags, szPath, szDirectory, szFileName, szExt)) {
DBGPRINT((sdlError,
"SdbpCreateSearchDBContext",
"Unable to parse executable path for \"%s\".\n",
szPath));
goto out;
}
_tcscpy(szFullName, szFileName);
_tcscat(szFullName, szExt);
pContext->pEnvironment = pEnvironment;
pContext->szDir = szDirectory;
pContext->szName = szFullName; // fullname (filename + ext)
pContext->szModuleName = szModule;
//
// We do not retain szExt (don't need it)
//
// Calculate this later -- implied by RtlZeroMemory statement above
//
pContext->pSearchParts = NULL;
pContext->szProcessHistory = NULL;
bReturn = TRUE;
out:
if (szExt != NULL) {
STACK_FREE(szExt);
}
if (szFileName != NULL) {
STACK_FREE(szFileName);
}
if (!bReturn) {
if (szDirectory != NULL) {
SdbFree(szDirectory);
}
if (szFullName != NULL) {
SdbFree(szFullName);
}
if (szModule != NULL) {
SdbFree(szModule);
}
}
return bReturn;
}
BOOL
SdbpInitializeSearchDBContext(
PSEARCHDBCONTEXT pContext
)
/*++
Return: TRUE - the context was successfully initialized with the process history
which was broken down into the separate search paths
Desc: This function prepares search context for use, obtaining and parsing process
history into separate paths. The array of these search paths is used then
by the caller to inquire about matching files that might be present in one
of the these places.
In Kernel mode use SEARCHDBF_NO_PROCESS_HISTORY flag within context
it will include only the current exe path into the process history
--*/
{
BOOL bSuccess = TRUE;
LPTSTR pszProcessHistory = NULL;
if (pContext->pSearchParts != NULL) {
return TRUE;
}
if (pContext->dwFlags & SEARCHDBF_NO_PROCESS_HISTORY) {
pszProcessHistory = pContext->szProcessHistory;
if (pszProcessHistory == NULL) {
DWORD DirLen = _tcslen(pContext->szDir);
DWORD NameLen = _tcslen(pContext->szName);
//
// We create a temporary process history
//
pContext->szProcessHistory = SdbAlloc((DirLen + NameLen + 1) * sizeof(TCHAR));
if (pContext->szProcessHistory == NULL) {
DBGPRINT((sdlError,
"SdbpInitializeSearchDBContext",
"Failed to allocate buffer %d bytes\n",
(DirLen + NameLen + 1) * sizeof(TCHAR)));
return FALSE;
}
pszProcessHistory = pContext->szProcessHistory;
RtlMoveMemory(pszProcessHistory, pContext->szDir, DirLen * sizeof(TCHAR));
RtlMoveMemory(pszProcessHistory + DirLen, pContext->szName, NameLen * sizeof(TCHAR));
*(pszProcessHistory + DirLen + NameLen) = TEXT('\0');
}
//
// When we are here -- we either have a process history or we just
// created it consisting of a single search item
//
} else {
#ifndef KERNEL_MODE
if (pContext->szProcessHistory == NULL) {
pContext->szProcessHistory = GetProcessHistory(pContext->pEnvironment,
pContext->szDir,
pContext->szName);
if (pContext->szProcessHistory == NULL) {
DBGPRINT((sdlError,
"SdbpInitializeSearchDBContext",
"Failed to retrieve process history\n"));
return FALSE;
}
}
pszProcessHistory = pContext->szProcessHistory;
#else
//
// This is the case with KERNEL_MODE. YOU HAVE TO SET SEARCHDBF_NO_PROCESS_HISTORY
//
assert(FALSE);
pszProcessHistory = NULL;
#endif
}
//
// At this point pszProcessHistory is NOT NULL
//
assert(pszProcessHistory != NULL);
DBGPRINT((sdlInfo,
"SdbpInitializeSearchDBContext",
"Using Process History: \"%s\"\n",
pszProcessHistory));
bSuccess = SdbpCreateSearchPathPartsFromPath(pszProcessHistory, &pContext->pSearchParts);
if (bSuccess) {
pContext->dwFlags |= SEARCHDBF_INITIALIZED;
}
return bSuccess;
}
void
SdbpReleaseSearchDBContext(
PSEARCHDBCONTEXT pContext
)
/*++
Return: void
Desc: Resets search DB context, frees memory allocated for each of the
temporary buffers.
--*/
{
if (pContext == NULL) {
return;
}
if (pContext->szProcessHistory != NULL) {
SdbFree(pContext->szProcessHistory);
pContext->szProcessHistory = NULL;
}
if (pContext->pSearchParts != NULL) {
SdbFree(pContext->pSearchParts);
pContext->pSearchParts = NULL;
}
if (pContext->szDir != NULL) {
SdbFree(pContext->szDir);
pContext->szDir = NULL;
}
if (pContext->szName != NULL) {
SdbFree(pContext->szName);
pContext->szName = NULL;
}
if (pContext->szModuleName != NULL) {
SdbFree(pContext->szModuleName);
pContext->szModuleName = NULL;
}
}
BOOL
SdbpIsExeEntryEnabled(
IN PDB pdb,
IN TAGID tiExe,
OUT GUID* pGUID,
OUT DWORD* pdwFlags
)
{
TAGID tiExeID;
DWORD i;
BOOL fSuccess = FALSE;
//
// Get the EXE's GUID
//
tiExeID = SdbFindFirstTag(pdb, tiExe, TAG_EXE_ID);
if (tiExeID == TAGID_NULL) {
DBGPRINT((sdlError,
"SdbpIsExeEntryEnabled",
"Failed to read TAG_EXE_ID for tiExe 0x%x !\n",
tiExe));
goto error;
}
if (!SdbReadBinaryTag(pdb, tiExeID, (PBYTE)pGUID, sizeof(GUID))) {
DBGPRINT((sdlError,
"SdbpIsExeEntryEnabled",
"Failed to read the GUID for tiExe 0x%x !\n",
tiExe));
goto error;
}
if (!SdbGetEntryFlags(pGUID, pdwFlags)) {
DBGPRINT((sdlWarning,
"SdbpIsExeEntryEnabled",
"No flags for tiExe 0x%lx\n",
tiExe));
*pdwFlags = 0;
} else {
DBGPRINT((sdlInfo,
"SdbpIsExeEntryEnabled",
"Retrieved flags for this app 0x%x.\n",
*pdwFlags));
}
if (!(*pdwFlags & SHIMREG_DISABLE_SHIM)) {
fSuccess = TRUE;
}
error:
return fSuccess;
}
#define EXTRA_BUF_SPACE (16 * sizeof(TCHAR))
//
// Matching an entry:
//
// 1. We check whether each file exists by calling SdbGetFileInfo
// 2. Each file's info is stored in FILEINFOCHAINITEM (allocated on the stack) - such as pointer
// to the actual FILEINFO structure (stored in file attribute cache) and tiMatch denoting
// the entry in the database for a given MATCHING_FILE
// 3. After we have verified that all the matching files do exist -- we proceed to walk the
// chain of FILEINFOCHAINITEM structures and call SdbCheckAllAttributes to check on all the
// other attributes of the file
// 4. Cleanup: File attribute cache is destroyed when the database is closed via call to
// SdbCleanupAttributeMgr
// 5. No cleanup is needed for FILEINFOCHAINITEM structures (they are allocated on the stack and
// just "go away")
//
//
typedef struct tagFILEINFOCHAINITEM {
PVOID pFileInfo; // pointer to the actual FILEINFO
// structure (from attribute cache)
TAGID tiMatch; // matching entry in the database
struct tagFILEINFOCHAINITEM* pNextItem; // pointer to the next matching file
} FILEINFOCHAINITEM, *PFILEINFOCHAINITEM;
BOOL
SdbpCheckForMatch(
IN HSDB hSDB, // context ptr
IN PDB pdb, // pdb to get match criteria from
IN TAGID tiExe, // TAGID of exe record to get match criteria from
IN PSEARCHDBCONTEXT pContext, // search db context (includes name/path)
OUT PMATCHMODE pMatchMode, // the match mode of this EXE
OUT GUID* pGUID,
OUT DWORD* pdwFlags
)
/*++
Return: TRUE if match is good, FALSE if this EXE doesn't match.
Desc: Given an EXE tag and a name and dir, checks the DB for MATCHING_FILE
tags, and checks all the matching info available for each the
files listed. If all the files check out, returns TRUE. If any of
the files don't exist, or don't match on one of the given
criteria, returns FALSE.
--*/
{
BOOL bReturn = FALSE;
BOOL bMatchLogicNot = FALSE;
BOOL bAllAttributesMatch = FALSE;
TAGID tiMatch;
TCHAR* szTemp = NULL;
LONG nFullPathBufSize = 0;
LONG nFullPathReqBufSize = 0;
LPTSTR szFullPath = NULL;
LONG i;
LONG NameLen = _tcslen(pContext->szName);
LONG MatchFileLen;
PSEARCHPATHPARTS pSearchPath;
PSEARCHPATHPART pSearchPathPart;
NTSTATUS Status;
PFILEINFOCHAINITEM pFileInfoItem = NULL;
PFILEINFOCHAINITEM pFileInfoItemList = NULL; // holds the list of matching files
// which were found
PFILEINFOCHAINITEM pFileInfoItemNext; // holds the next item in the list
PVOID pFileInfo = NULL; // points to the current file's
// information structure
BOOL bDisableAttributeCache = FALSE; // will be set according to search
TAGID tiName, tiTemp, tiMatchLogicNot;
TCHAR* szMatchFile = NULL;
HANDLE hFileHandle; // handle for the file we're checking, optimization
LPVOID pImageBase; // pointer to the image
DWORD dwImageSize = 0;
WORD wDefaultMatchMode;
//
// Check context's flags
//
if (pContext->dwFlags & SEARCHDBF_NO_ATTRIBUTE_CACHE) {
bDisableAttributeCache = TRUE;
}
//
// Loop through matching criteria.
//
tiMatch = SdbFindFirstTag(pdb, tiExe, TAG_MATCHING_FILE);
while (tiMatch != TAGID_NULL) {
tiMatchLogicNot = SdbFindFirstTag(pdb, tiMatch, TAG_MATCH_LOGIC_NOT);
bMatchLogicNot = (tiMatchLogicNot != TAGID_NULL);
tiName = SdbFindFirstTag(pdb, tiMatch, TAG_NAME);
if (!tiName) {
goto out;
}
szTemp = SdbGetStringTagPtr(pdb, tiName);
if (szTemp == NULL) {
DBGPRINT((sdlError,
"SdbpCheckForMatch",
"Failed to get the string from the database.\n"));
goto out;
}
if (szTemp[0] == TEXT('*')) {
//
// This is a signal that we should use the exe name.
//
szMatchFile = pContext->szName;
MatchFileLen = NameLen;
hFileHandle = pContext->hMainFile;
pImageBase = pContext->pImageBase;
dwImageSize = pContext->dwImageSize;
} else {
szMatchFile = szTemp;
MatchFileLen = _tcslen(szMatchFile);
hFileHandle = INVALID_HANDLE_VALUE;
pImageBase = NULL;
}
//
// When searching for files, we look in all process' exe directories,
// starting with the current process and working backwards through the process
// tree.
//
//
// See that the context is good...
//
if (!(pContext->dwFlags & SEARCHDBF_INITIALIZED)) {
if (!SdbpInitializeSearchDBContext(pContext)) {
DBGPRINT((sdlError,
"SdbpCheckForMatch",
"Failed to initialize SEARCHDBCONTEXT.\n"));
goto out;
}
}
pSearchPath = pContext->pSearchParts;
assert(pSearchPath != NULL);
for (i = 0; i < (LONG)pSearchPath->PartCount && NULL == pFileInfo; ++i) {
pSearchPathPart = &pSearchPath->Parts[i];
//
// There are two ways to specify a matching file: A relative path
// from the EXE, or an absolute path. To specify an absolute path,
// an environment variable (like "%systemroot%") must be used
// as the base of the path. Therefore, we check for the first character
// of the matching file to be % and if so, we assume that it is an
// absolute path.
//
#ifndef KERNEL_MODE
if (szMatchFile[0] == TEXT('%')) {
//
// Absolute path. Contains environment variables, get expanded size.
//
nFullPathReqBufSize = SdbExpandEnvironmentStrings(szMatchFile, NULL, 0);
} else
#endif // KERNEL_MODE
{
//
// Relative path. Determine size of full path.
//
nFullPathReqBufSize = (pSearchPathPart->PartLength + MatchFileLen + 1) * sizeof(TCHAR);
}
if (nFullPathBufSize < nFullPathReqBufSize) {
//
// Need to realloc the buffer.
//
if (szFullPath == NULL) {
nFullPathBufSize = _MAX_PATH * sizeof(TCHAR);
if (nFullPathReqBufSize >= nFullPathBufSize) {
nFullPathBufSize = nFullPathReqBufSize + EXTRA_BUF_SPACE;
}
} else {
STACK_FREE(szFullPath);
nFullPathBufSize = nFullPathReqBufSize + EXTRA_BUF_SPACE;
}
STACK_ALLOC(szFullPath, nFullPathBufSize);
}
if (szFullPath == NULL) {
DBGPRINT((sdlError,
"SdbpCheckForMatch",
"Failed to allocate %d bytes for FullPath.\n",
nFullPathBufSize));
goto out;
}
#ifndef KERNEL_MODE
if (szMatchFile[0] == TEXT('%')) {
//
// Absolute Path. Path contains environment variables, expand it.
//
if (!SdbExpandEnvironmentStrings(szMatchFile, szFullPath, nFullPathBufSize)) {
DBGPRINT((sdlError,
"SdbpCheckForMatch",
"SdbExpandEnvironmentStrings failed to expand strings for %s.\n",
szMatchFile));
goto out;
}
} else
#endif // KERNEL_MODE
{
//
// Relative path. Concatenate EXE directory with specified relative path.
//
RtlMoveMemory(szFullPath,
pSearchPathPart->pszPart,
pSearchPathPart->PartLength * sizeof(TCHAR));
RtlMoveMemory(szFullPath + pSearchPathPart->PartLength,
szMatchFile,
(MatchFileLen + 1) * sizeof(TCHAR));
}
pFileInfo = SdbGetFileInfo(hSDB,
szFullPath,
hFileHandle,
pImageBase,
dwImageSize, // this will be set ONLY if pImageBase != NULL
bDisableAttributeCache);
//
// This is not a bug, attributes are cleaned up when the database
// context is released.
//
}
if (pFileInfo == NULL && !bMatchLogicNot) {
DBGPRINT((sdlInfo,
"SdbpCheckForMatch",
"Matching file \"%s\" not found.\n",
szMatchFile));
goto out;
}
//
// Create and store a new FILEINFOITEM on the stack
//
STACK_ALLOC(pFileInfoItem, sizeof(*pFileInfoItem));
if (pFileInfoItem == NULL) {
DBGPRINT((sdlError,
"SdbpCheckForMatch",
"Failed to allocate %d bytes for FILEINFOITEM\n",
sizeof(*pFileInfoItem)));
goto out;
}
pFileInfoItem->pFileInfo = pFileInfo;
pFileInfoItem->tiMatch = tiMatch;
pFileInfoItem->pNextItem = pFileInfoItemList;
pFileInfoItemList = pFileInfoItem;
//
// We have the matching file.
// Remember where it is for the second pass when we check all the file attributes.
//
tiMatch = SdbFindNextTag(pdb, tiExe, tiMatch);
//
// Reset the file matching. we don't touch this file again for now, it's info
// is safely linked in pFileInfoItemList
//
pFileInfo = NULL;
}
//
// We are still here. That means all the matching files have been found.
// Check all the other attributes using fileinfoitemlist information.
//
pFileInfoItem = pFileInfoItemList;
while (pFileInfoItem != NULL) {
tiMatchLogicNot = SdbFindFirstTag(pdb, pFileInfoItem->tiMatch, TAG_MATCH_LOGIC_NOT);
bMatchLogicNot = (tiMatchLogicNot != TAGID_NULL);
if (pFileInfoItem->pFileInfo != NULL) {
bAllAttributesMatch = SdbpCheckAllAttributes(hSDB,
pdb,
pFileInfoItem->tiMatch,
pFileInfoItem->pFileInfo);
} else {
bAllAttributesMatch = FALSE;
}
if (bAllAttributesMatch && bMatchLogicNot) {
DBGPRINT((sdlInfo,
"SdbpCheckForMatch",
"All attributes match, but LOGIC=\"NOT\" was used which negates the match.\n"));
goto out;
}
if (!bAllAttributesMatch && !bMatchLogicNot) {
//
// Debug output happened inside SdbpCheckAllAttributes, no
// need for further spew here.
//
goto out;
}
//
// Advance to the next item.
//
pFileInfoItem = pFileInfoItem->pNextItem;
}
//
// It's a match! get the match mode
//
if (pMatchMode) {
//
// Important: depending on a particular database, we may use a different mode if
// there is match mode tag
//
// For Custom DB: default is the all-additive mode
// For Main DB: default is normal mode
//
#ifndef KERNEL_MODE
wDefaultMatchMode = SdbpIsMainPDB(hSDB, pdb) ? MATCHMODE_DEFAULT_MAIN :
MATCHMODE_DEFAULT_CUSTOM;
#else // KERNEL_MODE
wDefaultMatchMode = MATCHMODE_DEFAULT_MAIN;
#endif // KERNEL_MODE
tiTemp = SdbFindFirstTag(pdb, tiExe, TAG_MATCH_MODE);
if (tiTemp) {
pMatchMode->dwMatchMode = SdbReadWORDTag(pdb, tiTemp, wDefaultMatchMode);
} else {
pMatchMode->dwMatchMode = wDefaultMatchMode;
}
}
bReturn = TRUE;
out:
pFileInfoItem = pFileInfoItemList;
while (pFileInfoItem != NULL) {
pFileInfoItemNext = pFileInfoItem->pNextItem;
if (pFileInfoItem->pFileInfo != NULL && bDisableAttributeCache) {
SdbFreeFileInfo(pFileInfoItem->pFileInfo);
}
STACK_FREE(pFileInfoItem);
pFileInfoItem = pFileInfoItemNext;
}
if (szFullPath != NULL) {
STACK_FREE(szFullPath);
}
if (bReturn) {
//
// One last matching criteria: verify the entry is not disabled.
//
bReturn = SdbpIsExeEntryEnabled(pdb, tiExe, pGUID, pdwFlags);
}
return bReturn;
}
typedef enum _ADDITIVE_MODE {
AM_NORMAL,
AM_ADDITIVE_ONLY,
AM_NO_ADDITIVE
} ADDITIVE_MODE, *PADDITIVE_MODE;
LPCTSTR
SdbpFormatMatchModeType(
DWORD dwMatchMode
)
{
LPCTSTR pszMatchMode;
switch (dwMatchMode) {
case MATCH_ADDITIVE:
pszMatchMode = _T("Additive");
break;
case MATCH_EXCLUSIVE:
pszMatchMode = _T("Exclusive");
break;
case MATCH_NORMAL:
pszMatchMode = _T("Normal");
break;
default:
pszMatchMode = _T("Unknown");
break;
}
return pszMatchMode;
}
LPCTSTR
SdbpFormatMatchMode(
PMATCHMODE pMatchMode
)
{
static TCHAR szMatchMode[MAX_PATH];
LPTSTR pszMatchMode = szMatchMode;
int nChars = CHARCOUNT(szMatchMode);
int nLen;
nLen = _sntprintf(pszMatchMode,
nChars,
_T("0x%.2x%.2x [Mode: %s"),
pMatchMode->Flags,
pMatchMode->Type,
SdbpFormatMatchModeType(pMatchMode->Type));
if (nLen < 0) {
goto eh;
}
nChars -= nLen;
pszMatchMode += nLen;
eh:
//
// Just in case, truncate
//
if (nLen < 0) {
szMatchMode[CHARCOUNT(szMatchMode) - 1] = _T('\0');
}
return szMatchMode;
}
/*++
SdbpCheckExe
Checks a particular instance of an application in an SDB against for a match
Information on the file is passed through pContext parameter
result is returned in ptiExes
--*/
BOOL
SdbpCheckExe(
IN HSDB hSDB, //
IN PDB pdb, //
IN TAGID tiExe, // tag for an exe in the database
IN OUT PDWORD pdwNumExes, // returns (and passes in) the number of accumulated exe matches
IN OUT PSEARCHDBCONTEXT pContext, // information about the file which we match against
IN ADDITIVE_MODE eMode, // target Match mode, we filter entries based on this parameter
IN BOOL bDebug, // debug flag
OUT PMATCHMODE pMatchMode, // returns match mode used if success
OUT TAGID* ptiExes, // returns another entry in array of matched exes
OUT GUID* pGUID, // matched exe id
OUT DWORD* pdwFlags // matched exe flags
)
{
BOOL bSuccess = FALSE;
TAGID tiAppName = TAGID_NULL;
LPTSTR szAppName = NULL;
LPCTSTR pszMatchMode = NULL;
TAGID tiRuntimePlatform;
DWORD dwRuntimePlatform;
TAGID tiOSSKU;
DWORD dwOSSKU;
TAGID tiSP;
DWORD dwSPMask;
MATCHMODE MatchMode;
//
// For debug purposes we'd like to know the name of the app, which
// is more useful when the exe name is, say, AUTORUN.EXE or SETUP.EXE
//
tiAppName = SdbFindFirstTag(pdb, tiExe, TAG_APP_NAME);
if (tiAppName != TAGID_NULL) {
szAppName = SdbGetStringTagPtr(pdb, tiAppName);
}
DBGPRINT((sdlInfo, "SdbpCheckExe", "---------\n"));
DBGPRINT((sdlInfo,
"SdbpCheckExe",
"Index entry found for App: \"%s\" Exe: \"%s\"\n",
szAppName,
pContext->szName));
#ifndef KERNEL_MODE
//
// Check whether this exe is good for this platform first.
//
tiRuntimePlatform = SdbFindFirstTag(pdb, tiExe, TAG_RUNTIME_PLATFORM);
if (tiRuntimePlatform) {
dwRuntimePlatform = SdbReadDWORDTag(pdb, tiRuntimePlatform, RUNTIME_PLATFORM_ANY);
//
// Check for the platform match
//
if (!SdbpCheckRuntimePlatform(hSDB, szAppName, dwRuntimePlatform)) {
//
// Not the right platform. Debug spew would have occured in SdbpCheckRuntimePlatform
//
goto out;
}
}
tiOSSKU = SdbFindFirstTag(pdb, tiExe, TAG_OS_SKU);
if (tiOSSKU) {
dwOSSKU = SdbReadDWORDTag(pdb, tiOSSKU, OS_SKU_ALL);
if (dwOSSKU != OS_SKU_ALL) {
PSDBCONTEXT pDBContext = (PSDBCONTEXT)hSDB;
//
// Check for the OS SKU match
//
if (!(dwOSSKU & pDBContext->dwOSSKU)) {
DBGPRINT((sdlInfo,
"SdbpCheckExe",
"OS SKU Mismatch for \"%s\" Database(0x%lx) vs 0x%lx\n",
(szAppName ? szAppName : TEXT("Unknown")),
dwOSSKU,
pDBContext->dwOSSKU));
goto out;
}
}
}
tiSP = SdbFindFirstTag(pdb, tiExe, TAG_OS_SERVICE_PACK);
if (tiSP) {
dwSPMask = SdbReadDWORDTag(pdb, tiSP, 0xFFFFFFFF);
if (dwSPMask != 0xFFFFFFFF) {
PSDBCONTEXT pDBContext = (PSDBCONTEXT)hSDB;
//
// Check for the OS SKU match
//
if (!(dwSPMask & pDBContext->dwSPMask)) {
DBGPRINT((sdlInfo,
"SdbpCheckExe",
"OS SP Mismatch for \"%s\" Database(0x%lx) vs 0x%lx\n",
(szAppName ? szAppName : TEXT("Unknown")),
dwSPMask,
pDBContext->dwSPMask));
goto out;
}
}
}
#endif // KERNEL_MODE
if (!SdbpCheckForMatch(hSDB, pdb, tiExe, pContext, &MatchMode, pGUID, pdwFlags)) {
goto out;
}
if (eMode == AM_ADDITIVE_ONLY && MatchMode.Type != MATCH_ADDITIVE) {
goto out;
}
if (eMode == AM_NO_ADDITIVE && MatchMode.Type == MATCH_ADDITIVE) {
goto out;
}
pszMatchMode = SdbpFormatMatchMode(&MatchMode);
//
// If we're in debug mode, don't actually put the ones we find on the
// list, just put up an error.
//
if (bDebug) {
//
// We are in debug mode, do not add the match
//
DBGPRINT((sdlError,
"SdbpCheckExe",
"-----------------------------------------------------\n"));
DBGPRINT((sdlError|sdlLogPipe,
"SdbpCheckExe",
"!!!! Multiple matches! App: '%s', Exe: '%s', Mode: %s\n",
hSDB, // so that the pipe would use hPipe if needed
szAppName,
pContext->szName,
pszMatchMode));
DBGPRINT((sdlError,
"SdbpCheckExe",
"-----------------------------------------------------\n"));
} else {
DBGPRINT((sdlWarning|sdlLogPipe,
"SdbpCheckExe",
"++++ Successful match for App: '%s', Exe: '%s', Mode: %s\n",
hSDB,
szAppName,
pContext->szName,
pszMatchMode));
//
// If this is an exclusive match, kill anything we've found up to now
//
if (MatchMode.Type == MATCH_EXCLUSIVE) {
RtlZeroMemory(ptiExes, sizeof(TAGID) * SDB_MAX_EXES);
*pdwNumExes = 0;
}
//
// Save this match on the list
//
ptiExes[*pdwNumExes] = tiExe;
(*pdwNumExes)++;
bSuccess = TRUE;
}
out:
//
// In case of success, return match mode information
//
if (bSuccess && pMatchMode != NULL) {
pMatchMode->dwMatchMode = MatchMode.dwMatchMode;
}
return bSuccess;
}
DWORD
SdbpSearchDB(
IN HSDB hSDB,
IN PDB pdb, // pdb to search in
IN TAG tiSearchTag, // OPTIONAL - target tag (TAG_EXE or TAG_APPHELP_EXE)
IN PSEARCHDBCONTEXT pContext,
OUT TAGID* ptiExes, // caller needs to provide array of size SDB_MAX_EXES
OUT GUID* pLastExeGUID,
OUT DWORD* pLastExeFlags,
OUT PMATCHMODE pMatchMode // reason why we stopped scanning
)
/*++
Return: TAGID of found EXE record, TAGID_NULL if not found.
Desc: This function searches a given shimDB for any EXEs with the given filename.
If it finds one, it checks all the MATCHING_FILE records by
calling SdbpCheckForMatch.
If any EXEs are found, the number of EXEs found is returned in ptiExes.
If not, it returns 0.
when we get the matching mode out of the particular exe -- it is checked
to see whether we need to continue and then this matching mode is returned
It will never return more than SDB_MAX_EXES EXE entries.
Debug Output is controlled by three factors
-- a global one (controlled via the ifdef DBG), TRUE on checked builds
-- a pipe handle in hSDB which is activated when we init the context
-- a local variable that is set when we are in one of the conditions above
when the variable bDebug is set -- we do not actually store the matches
--*/
{
TAGID tiDatabase, tiExe;
FIND_INFO FindInfo;
TAGID tiAppName = TAGID_NULL;
TCHAR* szAppName = _T("(unknown)");
BOOL bUsingIndex = FALSE;
DWORD dwNumExes = 0;
DWORD dwMatchMode = MATCH_NORMAL;
DWORD i;
DWORD dwAdditiveMode = MATCH_NORMAL;
BOOL bDebug = FALSE;
BOOL bMultiple = FALSE;
BOOL bSuccess = FALSE;
MATCHMODE MatchMode; // internal match mode
MATCHMODE MatchModeExe;
#ifndef KERNEL_MODE
if (pMatchMode) {
MatchMode.dwMatchMode = pMatchMode->dwMatchMode;
} else {
MatchMode.dwMatchMode = SdbpIsMainPDB(hSDB, pdb) ? MATCHMODE_DEFAULT_MAIN :
MATCHMODE_DEFAULT_CUSTOM;
}
#else // KERNEL_MODE
MatchMode.dwMatchMode = MATCHMODE_DEFAULT_MAIN;
#endif
if (!tiSearchTag) {
tiSearchTag = TAG_EXE;
}
//
// ADDITIVE MATCHES -- wildcards
//
if (tiSearchTag == TAG_EXE && SdbIsIndexAvailable(pdb, TAG_EXE, TAG_WILDCARD_NAME)) {
tiExe = SdbpFindFirstIndexedWildCardTag(pdb,
TAG_EXE,
TAG_WILDCARD_NAME,
pContext->szName,
&FindInfo);
while (tiExe != TAGID_NULL) {
bSuccess = SdbpCheckExe(hSDB,
pdb,
tiExe,
&dwNumExes,
pContext,
AM_ADDITIVE_ONLY, // match mode we request for this db
bDebug,
&MatchModeExe, // this is the matched tag from the db
ptiExes,
pLastExeGUID,
pLastExeFlags);
if (bSuccess) {
if (bDebug) {
bMultiple = TRUE; // if bDebug is set -- we already seen a match
} else {
//
// We got a match, update the state and make decision on whether to continue
//
MatchMode = MatchModeExe;
if (MatchModeExe.Type != MATCH_ADDITIVE) {
bDebug = (g_bDBG || SDBCONTEXT_IS_INSTRUMENTED(hSDB));
if (!bDebug) {
goto out;
}
}
}
}
tiExe = SdbpFindNextIndexedWildCardTag(pdb, &FindInfo);
}
}
//
// Normal EXEs
//
bUsingIndex = SdbIsIndexAvailable(pdb, tiSearchTag, TAG_NAME);
if (bUsingIndex) {
//
// Look in the index.
//
tiExe = SdbFindFirstStringIndexedTag(pdb,
tiSearchTag,
TAG_NAME,
pContext->szName,
&FindInfo);
if (tiExe == TAGID_NULL) {
DBGPRINT((sdlInfo,
"SdbpSearchDB",
"SdbFindFirstStringIndexedTag failed to locate exe: \"%s\".\n",
pContext->szName));
}
} else {
//
// Searching without an index...
//
DBGPRINT((sdlInfo, "SdbpSearchDB", "Searching database with no index.\n"));
//
// First get the DATABASE
//
tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
if (tiDatabase != TAGID_NULL) {
DBGPRINT((sdlError, "SdbpSearchDB", "No DATABASE tag found.\n"));
goto out;
}
//
// Then get the first EXE.
//
tiExe = SdbFindFirstNamedTag(pdb, tiDatabase, tiSearchTag, TAG_NAME, pContext->szName);
}
while (tiExe != TAGID_NULL) {
bSuccess = SdbpCheckExe(hSDB,
pdb,
tiExe,
&dwNumExes,
pContext,
AM_NORMAL,
bDebug,
&MatchModeExe,
ptiExes,
pLastExeGUID,
pLastExeFlags);
if (bSuccess) {
if (bDebug) {
bMultiple = TRUE; // if bDebug is set -- we already seen a match
} else {
//
// We got a match, update the state and make decision on whether to continue
// if we're not additive, we may go into debug mode
//
MatchMode = MatchModeExe;
if (MatchModeExe.Type != MATCH_ADDITIVE) {
bDebug = (g_bDBG || SDBCONTEXT_IS_INSTRUMENTED(hSDB));
if (!bDebug) {
goto out;
}
}
}
}
if (bUsingIndex) {
tiExe = SdbFindNextStringIndexedTag(pdb, &FindInfo);
} else {
tiExe = SdbpFindNextNamedTag(pdb, tiDatabase, tiExe, TAG_NAME, pContext->szName);
}
}
#ifndef KERNEL_MODE
//
// Now we search by module name, if one is available
// this case falls into 16-bit flags category
//
if (tiSearchTag == TAG_EXE && pContext->szModuleName) {
bUsingIndex = SdbIsIndexAvailable(pdb, tiSearchTag, TAG_16BIT_MODULE_NAME);
if (bUsingIndex) {
//
// Look in the index.
//
tiExe = SdbFindFirstStringIndexedTag(pdb,
tiSearchTag,
TAG_16BIT_MODULE_NAME,
pContext->szModuleName,
&FindInfo);
if (tiExe == TAGID_NULL) {
DBGPRINT((sdlInfo,
"SdbpSearchDB",
"SdbFindFirstStringIndexedTag failed to locate exe (MODNAME): \"%s\".\n",
pContext->szModuleName));
}
} else {
//
// Searching without an index...
//
DBGPRINT((sdlInfo, "SdbpSearchDB", "Searching database with no index.\n"));
//
// First get the DATABASE
//
tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
if (tiDatabase != TAGID_NULL) {
DBGPRINT((sdlError, "SdbpSearchDB", "No DATABASE tag found.\n"));
goto out;
}
//
// Then get the first EXE.
//
tiExe = SdbFindFirstNamedTag(pdb,
tiDatabase,
tiSearchTag,
TAG_16BIT_MODULE_NAME,
pContext->szModuleName);
}
while (tiExe != TAGID_NULL) {
bSuccess = SdbpCheckExe(hSDB,
pdb,
tiExe,
&dwNumExes,
pContext,
AM_NORMAL,
bDebug,
&MatchModeExe,
ptiExes,
pLastExeGUID,
pLastExeFlags);
if (bSuccess) {
if (bDebug) {
bMultiple = TRUE; // if bDebug is set -- we already seen a match
} else {
//
// We got a match, update the state and make decision on whether to continue
//
MatchMode = MatchModeExe;
if (MatchModeExe.Type != MATCH_ADDITIVE) {
bDebug = (g_bDBG || SDBCONTEXT_IS_INSTRUMENTED(hSDB));
if (!bDebug) {
goto out;
}
}
}
}
if (bUsingIndex) {
tiExe = SdbFindNextStringIndexedTag(pdb, &FindInfo);
} else {
tiExe = SdbpFindNextNamedTag(pdb,
tiDatabase,
tiExe,
TAG_16BIT_MODULE_NAME,
pContext->szModuleName);
}
}
}
#endif // KERNEL_MODE
//
// Now check for wild-card non-additive exes.
//
if (tiSearchTag == TAG_EXE && SdbIsIndexAvailable(pdb, TAG_EXE, TAG_WILDCARD_NAME)) {
tiExe = SdbpFindFirstIndexedWildCardTag(pdb,
TAG_EXE,
TAG_WILDCARD_NAME,
pContext->szName,
&FindInfo);
while (tiExe != TAGID_NULL) {
bSuccess = SdbpCheckExe(hSDB,
pdb,
tiExe,
&dwNumExes,
pContext,
AM_NO_ADDITIVE,
bDebug,
&MatchModeExe,
ptiExes,
pLastExeGUID,
pLastExeFlags);
if (bSuccess) {
if (bDebug) {
bMultiple = TRUE; // if bDebug is set -- we already seen a match
} else {
//
// we got a match, update the state and make decision on whether to continue
//
MatchMode = MatchModeExe;
if (MatchModeExe.Type != MATCH_ADDITIVE) {
bDebug = (g_bDBG || SDBCONTEXT_IS_INSTRUMENTED(hSDB));
if (!bDebug) {
goto out;
}
}
}
}
tiExe = SdbpFindNextIndexedWildCardTag(pdb, &FindInfo);
}
}
out:
//
// Now report the final resolution of the match.
//
for (i = 0; i < dwNumExes; ++i) {
tiAppName = SdbFindFirstTag(pdb, ptiExes[i], TAG_APP_NAME);
if (tiAppName != TAGID_NULL) {
szAppName = SdbGetStringTagPtr(pdb, tiAppName);
} else {
szAppName = _T("(Unknown)");
}
DBGPRINT((sdlWarning,
"SdbpSearchDB",
"--------------------------------------------------------\n"));
DBGPRINT((sdlWarning|sdlLogPipe,
"SdbpSearchDB",
"+ Final match is App: \"%s\", exe: \"%s\".\n",
hSDB,
szAppName,
pContext->szName));
DBGPRINT((sdlWarning,
"SdbpSearchDB",
"--------------------------------------------------------\n"));
}
if (bMultiple) {
DBGPRINT((sdlError,
"SdbpSearchDB",
"--------------------------------------------------------\n"));
DBGPRINT((sdlError|sdlLogPipe,
"SdbpSearchDB",
"!!!!!!! Multiple non-additive matches. !!!!!\n",
hSDB));
DBGPRINT((sdlError,
"SdbpSearchDB",
"--------------------------------------------------------\n"));
}
if (pMatchMode != NULL) {
pMatchMode->dwMatchMode = MatchMode.dwMatchMode;
}
return dwNumExes;
}
TAGREF
SdbGetDatabaseMatch(
IN HSDB hSDB,
IN LPCTSTR szPath,
IN HANDLE FileHandle OPTIONAL,
IN LPVOID pImageBase OPTIONAL,
IN DWORD dwImageSize OPTIONAL
)
/*++
Return: BUGBUG: ?
Desc: BUGBUG: ?
--*/
{
SEARCHDBCONTEXT Context;
PSDBCONTEXT pSdbContext = (PSDBCONTEXT)hSDB;
TAGID tiExe = TAGID_NULL;
TAGID atiExes[SDB_MAX_EXES];
TAGREF trExe = TAGREF_NULL;
DWORD dwNumExes = 0;
GUID guid;
DWORD dwFlags = 0;
assert(pSdbContext->pdbMain && szPath);
RtlZeroMemory(&Context, sizeof(Context)); // do this so that we don't trip later
RtlZeroMemory(atiExes, sizeof(atiExes));
Context.dwFlags |= (SEARCHDBF_NO_PROCESS_HISTORY | SEARCHDBF_NO_ATTRIBUTE_CACHE);
if (FileHandle != INVALID_HANDLE_VALUE || pImageBase != NULL) {
Context.dwFlags |= SEARCHDBF_NO_LFN;
}
Context.hMainFile = FileHandle; // used to optimize attribute retrieval
Context.pImageBase = pImageBase; // this will be used and not a file handle
Context.dwImageSize = dwImageSize; // size of the image
DBGPRINT((sdlInfo, "SdbGetDatabaseMatch", "Looking for \"%s\"\n", szPath));
//
// Create search db context, no process history needed.
//
if (!SdbpCreateSearchDBContext(&Context, szPath, NULL, NULL)) {
DBGPRINT((sdlError,
"SdbGetDatabaseMatch",
"Failed to create search DB context.\n"));
goto out;
}
//
// We will be searching the main db
//
dwNumExes = SdbpSearchDB(pSdbContext,
pSdbContext->pdbMain,
TAG_EXE,
&Context,
atiExes,
&guid,
&dwFlags,
NULL);
//
// Convert to TAGREF
//
if (dwNumExes) {
//
// Always use the last exe in the list, as it will be the most specific
//
tiExe = atiExes[dwNumExes - 1];
if (!SdbTagIDToTagRef(hSDB, pSdbContext->pdbMain, tiExe, &trExe)) {
DBGPRINT((sdlError,
"SdbGetDatabaseMatch",
"Failed to convert tagid to tagref\n"));
goto out;
}
}
out:
SdbpReleaseSearchDBContext(&Context);
return trExe;
}
DWORD
SdbQueryData(
IN HSDB hSDB, // database handle
IN TAGREF trExe, // tagref of the matching exe
IN LPCTSTR lpszDataName, // if this is null, will try to return all the policy names
OUT LPDWORD lpdwDataType, // pointer to data type (REG_SZ, REG_BINARY, etc)
OUT LPVOID lpBuffer, // buffer to fill with information
IN OUT LPDWORD lpdwBufferSize // pointer to buffer size
)
{
return SdbQueryDataEx(hSDB, trExe, lpszDataName, lpdwDataType, lpBuffer, lpdwBufferSize, NULL);
}
DWORD
SdbQueryDataExTagID(
IN PDB pdb, // database handle
IN TAGID tiExe, // tagref of the matching exe
IN LPCTSTR lpszDataName, // if this is null, will try to return all the policy names
OUT LPDWORD lpdwDataType, // pointer to data type (REG_SZ, REG_BINARY, etc)
OUT LPVOID lpBuffer, // buffer to fill with information
IN OUT LPDWORD lpdwBufferSize, // pointer to buffer size
OUT TAGID* ptiData // optional pointer to the retrieved data tag
)
/*++
Return: Error code or ERROR_SUCCESS if successful
Desc: See complete description with sample code
in doc subdirectory
--*/
{
TAGID tiData;
BOOL bSuccess;
TAGID tiParent;
TAGID tiName;
TAGID tiValue;
TAGID tiValueType;
DWORD cbSize;
DWORD dwValueType;
LPCTSTR pszName;
LPTSTR pszNameBuffer = NULL;
LPTSTR pSlash;
LPTSTR pchBuffer;
DWORD dwData;
TAG tData;
ULONGLONG ullData;
LPVOID lpValue;
DWORD Status = ERROR_NOT_SUPPORTED; // have it initialized
if (lpszDataName == NULL) {
if (lpdwBufferSize == NULL) {
Status = ERROR_INVALID_PARAMETER;
goto ErrHandle;
}
cbSize = 0;
tiData = SdbFindFirstTag(pdb, tiExe, TAG_DATA);
if (!tiData) {
//
// Bad entry.
//
DBGPRINT((sdlError,
"SdbQueryDataExTagID",
"The entry 0x%x does not appear to have data\n",
tiExe));
Status = ERROR_INTERNAL_DB_CORRUPTION;
goto ErrHandle;
}
while (tiData) {
//
// Pass one: Calculate the size needed.
//
tiName = SdbFindFirstTag(pdb, tiData, TAG_NAME);
if (!tiName) {
DBGPRINT((sdlError,
"SdbQueryDataExTagID",
"The entry 0x%x does not contain a name tag\n",
tiData));
Status = ERROR_INTERNAL_DB_CORRUPTION;
goto ErrHandle;
}
pszName = SdbGetStringTagPtr(pdb, tiName);
if (!pszName) {
DBGPRINT((sdlError,
"SdbQueryDataExTagID",
"The entry 0x%x contains NULL name\n",
tiName));
Status = ERROR_INTERNAL_DB_CORRUPTION;
goto ErrHandle;
}
cbSize += (_tcslen(pszName) + 1) * sizeof(*pszName);
tiData = SdbFindNextTag(pdb, tiExe, tiData);
}
cbSize += sizeof(*pszName); // for the final 0
//
// We are done, compare the size.
//
if (lpBuffer == NULL || *lpdwBufferSize < cbSize) {
*lpdwBufferSize = cbSize;
Status = ERROR_INSUFFICIENT_BUFFER;
goto ErrHandle;
}
//
// lpBuffer != NULL here and there is enough room
//
pchBuffer = (LPTSTR)lpBuffer;
tiData = SdbFindFirstTag(pdb, tiExe, TAG_DATA);
while (tiData) {
tiName = SdbFindFirstTag(pdb, tiData, TAG_NAME);
if (tiName) {
pszName = SdbGetStringTagPtr(pdb, tiName);
if (pszName) {
_tcscpy(pchBuffer, pszName);
pchBuffer += _tcslen(pchBuffer) + 1;
}
}
tiData = SdbFindNextTag(pdb, tiExe, tiData);
}
//
// The buffer has been filled, terminate.
//
*pchBuffer++ = TEXT('\0');
//
// Save the size written to the buffer
//
*lpdwBufferSize = (DWORD)((ULONG_PTR)pchBuffer - (ULONG_PTR)lpBuffer);
//
// Save data type
//
if (lpdwDataType != NULL) {
*lpdwDataType = REG_MULTI_SZ;
}
return ERROR_SUCCESS;
}
//
// In this case we allow the query to proceed if
// the buffer is null and lpdwBufferSize is not null or lpBufferSize is not null
//
if (lpBuffer == NULL && lpdwBufferSize == NULL) {
DBGPRINT((sdlError,
"SdbQueryDataExTagID",
"One of lpBuffer or lpdwBufferSize should not be null\n"));
Status = ERROR_INVALID_PARAMETER;
goto ErrHandle;
}
//
// Expect the name to be in format "name1\name2..."
//
STACK_ALLOC(pszNameBuffer, (_tcslen(lpszDataName) + 1) * sizeof(*pszNameBuffer));
if (pszNameBuffer == NULL) {
DBGPRINT((sdlError,
"SdbQueryDataExTagID",
"Cannot allocate temporary buffer for parsing the name \"%s\"\n",
lpszDataName));
Status = ERROR_NOT_ENOUGH_MEMORY;
goto ErrHandle;
}
tiParent = tiExe;
tiData = TAGID_NULL;
do {
pSlash = _tcschr(lpszDataName, TEXT('\\'));
if (pSlash == NULL) {
_tcscpy(pszNameBuffer, lpszDataName);
lpszDataName = NULL;
} else {
_tcsncpy(pszNameBuffer, lpszDataName, pSlash - lpszDataName);
pszNameBuffer[pSlash - lpszDataName] = TEXT('\0');
lpszDataName = pSlash + 1; // go to the next char
}
tiData = SdbFindFirstNamedTag(pdb, tiParent, TAG_DATA, TAG_NAME, pszNameBuffer);
tiParent = tiData;
} while (lpszDataName != NULL && *lpszDataName != TEXT('\0') && tiData != TAGID_NULL);
if (!tiData) {
DBGPRINT((sdlError,
"SdbQueryDataExTagID",
"The entry \"%s\" not found\n",
pszNameBuffer));
Status = ERROR_NOT_FOUND;
goto ErrHandle;
}
//
// Looks like we found the entry, query value type
//
dwValueType = REG_NONE;
tiValueType = SdbFindFirstTag(pdb, tiData, TAG_DATA_VALUETYPE);
if (!tiValueType) {
DBGPRINT((sdlWarning,
"SdbQueryDataExTagID",
"The entry 0x%x does not have valuetype information\n",
tiData));
} else {
dwValueType = SdbReadDWORDTag(pdb, tiValueType, REG_NONE);
}
cbSize = 0;
lpValue = NULL;
if (dwValueType != REG_NONE) {
//
// Find data tag
//
cbSize = 0;
switch (dwValueType) {
case REG_SZ:
//
// string data
//
tData = TAG_DATA_STRING;
break;
case REG_DWORD:
tData = TAG_DATA_DWORD;
break;
case REG_QWORD:
tData = TAG_DATA_QWORD;
break;
case REG_BINARY:
tData = TAG_DATA_BITS;
break;
default:
DBGPRINT((sdlError,
"SdbQueryDataExTagID",
"The entry 0x%x contains bad valuetype information 0x%x\n",
tiData,
dwValueType));
Status = ERROR_INTERNAL_DB_CORRUPTION;
goto ErrHandle;
break;
}
tiValue = SdbFindFirstTag(pdb, tiData, tData);
//
// Find what the data size is if needed
//
if (!tiValue) {
DBGPRINT((sdlWarning,
"SdbQueryDataExTagID",
"The entry 0x%x contains no value\n",
tiData));
Status = ERROR_NOT_FOUND;
goto ErrHandle;
}
//
// For those who have no size quite yet...
// (binary and a string)
//
switch (dwValueType) {
case REG_SZ:
pchBuffer = SdbGetStringTagPtr(pdb, tiValue);
if (pchBuffer == NULL) {
DBGPRINT((sdlWarning,
"SdbQueryDataExTagID",
"The entry 0x%x contains bad string value 0x%x\n",
tiData,
tiValue));
Status = ERROR_NOT_FOUND;
goto ErrHandle;
}
cbSize = (_tcslen(pchBuffer) + 1) * sizeof(*pchBuffer);
lpValue = (LPVOID)pchBuffer;
break;
case REG_BINARY:
cbSize = SdbGetTagDataSize(pdb, tiValue); // binary tag
lpValue = SdbpGetMappedTagData(pdb, tiValue);
if (lpValue == NULL) {
DBGPRINT((sdlWarning,
"SdbQueryDataExTagID",
"The entry 0x%x contains bad binary value 0x%x\n",
tiData,
tiValue));
Status = ERROR_NOT_FOUND;
goto ErrHandle;
}
break;
case REG_DWORD:
dwData = SdbReadDWORDTag(pdb, tiValue, 0);
cbSize = sizeof(dwData);
lpValue = (LPVOID)&dwData;
break;
case REG_QWORD:
ullData = SdbReadQWORDTag(pdb, tiValue, 0);
cbSize = sizeof(ullData);
lpValue = (LPVOID)&ullData;
break;
}
//
// At this point we have everything we need to get the pointer to data.
//
}
//
// Fix the output params and exit.
//
Status = ERROR_SUCCESS;
if (cbSize == 0) {
goto SkipCopy;
}
if (lpBuffer == NULL || (lpdwBufferSize != NULL && *lpdwBufferSize < cbSize)) {
Status = ERROR_INSUFFICIENT_BUFFER;
goto SkipCopy;
}
//
// Buffer size checked out, now if buffer exists -- copy
//
if (lpBuffer != NULL) {
RtlMoveMemory(lpBuffer, lpValue, cbSize);
}
SkipCopy:
if (lpdwBufferSize) {
*lpdwBufferSize = cbSize;
}
if (lpdwDataType) {
*lpdwDataType = dwValueType;
}
if (ptiData) {
*ptiData = tiData;
}
ErrHandle:
if (pszNameBuffer != NULL) {
STACK_FREE(pszNameBuffer);
}
return Status;
}
DWORD
SdbQueryDataEx(
IN HSDB hSDB, // database handle
IN TAGREF trExe, // tagref of the matching exe
IN LPCTSTR lpszDataName, // if this is null, will try to return all the policy names
OUT LPDWORD lpdwDataType, // pointer to data type (REG_SZ, REG_BINARY, etc)
OUT LPVOID lpBuffer, // buffer to fill with information
IN OUT LPDWORD lpdwBufferSize, // pointer to buffer size
OUT TAGREF* ptrData // optional pointer to the retrieved data tag
)
{
BOOL bSuccess;
PDB pdb = NULL;
TAGID tiExe = TAGID_NULL;
TAGID tiData;
NTSTATUS Status;
bSuccess = SdbTagRefToTagID(hSDB, trExe, &pdb, &tiExe);
if (!bSuccess) {
DBGPRINT((sdlError,
"SdbQueryDataEx",
"Failed to convert tagref 0x%x to tagid\n",
trExe));
Status = ERROR_INVALID_PARAMETER;
goto ErrHandle;
}
Status = SdbQueryDataExTagID(pdb,
tiExe,
lpszDataName,
lpdwDataType,
lpBuffer,
lpdwBufferSize,
&tiData);
//
// See that we convert the output param
//
if (ptrData != NULL && NT_SUCCESS(Status)) {
if (!SdbTagIDToTagRef(hSDB, pdb, tiData, ptrData)) {
Status = ERROR_INVALID_DATA;
}
}
ErrHandle:
return Status;
}
BOOL
SdbReadEntryInformation(
IN HSDB hSDB,
IN TAGREF trExe,
OUT PSDBENTRYINFO pEntryInfo
)
/*++
Return: BUGBUG: ?
Desc: BUGBUG: ?
--*/
{
BOOL bSuccess = FALSE;
TAGID tiExe;
TAGID tiData;
TAGID tiExeID;
TAGID tiPolicy;
TAGID tiRegPath;
PDB pdb;
SDBENTRYINFO EntryInfo;
RtlZeroMemory(&EntryInfo, sizeof(EntryInfo));
bSuccess = SdbTagRefToTagID(hSDB, trExe, &pdb, &tiExe);
if (!bSuccess) {
DBGPRINT((sdlError,
"SdbReadEntryInformation",
"Failed to convert tagref 0x%x to tagid\n",
trExe));
goto ErrHandle;
}
//
// Get the EXE's ID
//
tiExeID = SdbFindFirstTag(pdb, tiExe, TAG_EXE_ID);
if (tiExeID == TAGID_NULL) {
DBGPRINT((sdlError,
"SdbReadEntryInformation",
"Failed to read TAG_EXE_ID for tiExe 0x%x !\n",
tiExe));
goto ErrHandle;
}
bSuccess = SdbReadBinaryTag(pdb,
tiExeID,
(PBYTE)&EntryInfo.guidID,
sizeof(EntryInfo.guidID));
if (!bSuccess) {
DBGPRINT((sdlError,
"SdbReadEntryInformation",
"Failed to read GUID referenced by 0x%x\n",
tiExeID));
goto ErrHandle;
}
//
// Get the database id
//
if (!SdbGetDatabaseID(pdb, &EntryInfo.guidDB)) {
DBGPRINT((sdlError,
"SdbReadEntryInformation",
"Failed to read GUID of the database\n"));
goto ErrHandle;
}
//
// Retrieve entry flags as referenced by the registry
//
if (!SdbGetEntryFlags(&EntryInfo.guidID, &EntryInfo.dwFlags)) {
DBGPRINT((sdlWarning,
"SdbReadEntryInformation",
"No flags for tiExe 0x%x\n",
tiExe));
EntryInfo.dwFlags = 0;
} else {
DBGPRINT((sdlInfo,
"SdbReadEntryInformation",
"Retrieved flags for this app 0x%x.\n",
EntryInfo.dwFlags));
}
//
// Read the data tags
//
tiData = SdbFindFirstTag(pdb, tiExe, TAG_DATA);
EntryInfo.tiData = tiData;
if (tiData == TAGID_NULL) {
//
// This is not a data entry
//
DBGPRINT((sdlWarning,
"SdbReadEntryInformation",
"Entry tiExe 0x%x does not contain TAG_DATA.\n",
tiExe));
}
if (pEntryInfo != NULL) {
RtlMoveMemory(pEntryInfo, &EntryInfo, sizeof(*pEntryInfo));
}
bSuccess = TRUE;
ErrHandle:
return bSuccess;
}
//
// We may be compiled UNICODE or ANSI
// If we are compiled UNICODE we need to use UNICODE sprintf and convert
// the result back to ANSI for output with DbgPrint. This is accomplished
// by %ls format in DbgPrint. Format and Function name are always passed
// in as ANSI though. TCHAR strings are formatted just with %s
//
void
PrepareFormatForUnicode(
PCH fmtUnicode,
PCH format
)
{
PCH pfmt;
CHAR ch;
size_t nch;
long width;
PCH pend;
strcpy(fmtUnicode, format);
pfmt = fmtUnicode;
while('\0' != (ch = *pfmt++)) {
if (ch == '%') {
if (*pfmt == '%') {
continue;
}
//
// Skip the characters that relate to - + 0 ' ' #
//
nch = strspn(pfmt, "-+0 #");
pfmt += nch;
//
// Parse the width.
//
if (*pfmt == '*') {
//
// Parameter defines the width
//
++pfmt;
} else {
//
// See whether we have width
//
if (isdigit(*pfmt)) {
pend = NULL;
width = atol(pfmt);
while (isdigit(*pfmt)) {
++pfmt;
}
}
}
//
// Now we can have: .precision
//
if (*pfmt == '.') {
++pfmt;
width = atol(pfmt);
while (isdigit(*pfmt)) {
++pfmt;
}
}
//
// Now is the format (one of: h, l, L, I64)
//
ch = *pfmt;
pend = strchr("hlLNFw", ch);
if (pend != NULL) {
++pfmt; // move past the modifier char
} else {
if (ch == 'I' && !strncpy(pfmt, "I64", 3)) {
pfmt += 3;
}
}
//
// We should have a type character here.
//
if (*pfmt == 's') {
//
// Convert to UPPER, making it UNICODE string with ansi vsnprintf
//
*pfmt = 'S';
}
//
// Move past the format char if we are not at the end
//
if (*pfmt != '\0') {
++pfmt;
}
}
}
}