//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1999 - 2000. // // File: registry.cpp // // Contents: NtMarta registry functions // // History: 4/99 philh Created // //---------------------------------------------------------------------------- #include #pragma hdrstop extern "C" { #include #include #include } #include #include #include #include #include #include #include #include #include #include #include #include #ifdef STATIC #undef STATIC #endif #define STATIC // Registry object names returned by NtQueryObject are prefixed by // the following #define REG_OBJ_TAG L"\\REGISTRY\\" #define REG_OBJ_TAG_LEN (sizeof(REG_OBJ_TAG) / sizeof(WCHAR) - 1) //+------------------------------------------------------------------------- // Registry Context data structures //-------------------------------------------------------------------------- typedef struct _REG_FIND_DATA REG_FIND_DATA, *PREG_FIND_DATA; typedef struct _REG_CONTEXT { DWORD dwRefCnt; DWORD dwFlags; // Only closed when REG_CONTEXT_CLOSE_HKEY_FLAG is set HKEY hKey; LPWSTR pwszObject; // optional, allocated // Following is allocated and updated for FindFirst, FindNext PREG_FIND_DATA pRegFindData; } REG_CONTEXT, *PREG_CONTEXT; #define REG_CONTEXT_CLOSE_HKEY_FLAG 0x1 struct _REG_FIND_DATA { PREG_CONTEXT pRegParentContext; // ref counted DWORD cSubKeys; DWORD cchMaxSubKey; DWORD iSubKey; // index of next FindNext // Following isn't allocated separately, it follows this data structure LPWSTR pwszSubKey; }; //+------------------------------------------------------------------------- // Registry allocation functions //-------------------------------------------------------------------------- #define I_MartaRegZeroAlloc(size) \ LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, size) #define I_MartaRegNonzeroAlloc(size) \ LocalAlloc(LMEM_FIXED, size) STATIC inline VOID I_MartaRegFree( IN LPVOID pv ) { if (pv) LocalFree(pv); } STATIC DWORD I_MartaRegDupString( IN LPCWSTR pwszOrig, OUT LPWSTR *ppwszDup ) /*++ Routine Description: Allocate memory and copy the given name into it. Arguments: pwszOrig - String to be duplicated. ppwszDup - To return the duplicate. Return Value: ERROR_SUCCESS in case of success. ERROR_NOT_ENOUGH_MEMORY if allocation failed. --*/ { DWORD dwErr; DWORD cchOrig; LPWSTR pwszDup; cchOrig = wcslen(pwszOrig); if (NULL == (pwszDup = (LPWSTR) I_MartaRegNonzeroAlloc( (cchOrig + 1) * sizeof(WCHAR)))) { *ppwszDup = NULL; dwErr = ERROR_NOT_ENOUGH_MEMORY; } else { memcpy(pwszDup, pwszOrig, (cchOrig + 1) * sizeof(WCHAR)); *ppwszDup = pwszDup; dwErr = ERROR_SUCCESS; } return dwErr; } STATIC DWORD I_MartaRegGetParentString( IN OUT LPWSTR pwszParent ) /*++ Routine Description: Given the name for a registry key, get the name of its parent. Does not allocate memory. Scans till the first '\' from the right and deletes the name after that. Arguments: pwszParent - Object name which will be converted to its parent name. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { DWORD dwErr; DWORD cch; LPWSTR pwsz; if (NULL == pwszParent) return ERROR_INVALID_NAME; cch = wcslen(pwszParent); pwsz = pwszParent + cch; if (0 == cch) goto InvalidNameReturn; pwsz--; // // Remove any trailing '\'s // while (L'\\' == *pwsz) { if (pwsz == pwszParent) goto InvalidNameReturn; pwsz--; } // // Peal off the last path name component // while (L'\\' != *pwsz) { if (pwsz == pwszParent) goto InvalidNameReturn; pwsz--; } // // Remove all trailing '\'s from the parent. // This could also be the leading '\\'s for a computer name. // while (L'\\' == *pwsz) { if (pwsz == pwszParent) goto InvalidNameReturn; pwsz--; } pwsz++; assert(L'\\' == *pwsz); dwErr = ERROR_SUCCESS; CommonReturn: *pwsz = L'\0'; return dwErr; InvalidNameReturn: dwErr = ERROR_INVALID_NAME; goto CommonReturn; } STATIC DWORD I_MartaRegCreateChildString( IN LPCWSTR pwszParent, IN LPCWSTR pwszSubKey, OUT LPWSTR *ppwszChild ) /*++ Routine Description: Given the name of the parent and the name of the subkey, create the full name of the child. Arguments: pwszParent - Name of the parent. pwszSubKey - Name of the subkey. ppwszChild - To return the name of the child. Return Value: ERROR_SUCCESS in case of success. ERROR_NOT_ENOUGH_MEMORY if allocation failed. --*/ { DWORD dwErr; DWORD cchParent; DWORD cchSubKey; DWORD cchChild; LPWSTR pwszChild = NULL; if (NULL == pwszParent || NULL == pwszSubKey) goto InvalidNameReturn; cchParent = wcslen(pwszParent); // // Remove any trailing '\'s from parent // while (0 < cchParent && L'\\' == pwszParent[cchParent - 1]) cchParent--; if (0 == cchParent) goto InvalidNameReturn; cchSubKey = wcslen(pwszSubKey); if (0 == cchSubKey) goto InvalidNameReturn; cchChild = cchParent + 1 + cchSubKey; if (NULL == (pwszChild = (LPWSTR) I_MartaRegNonzeroAlloc( (cchChild + 1) * sizeof(WCHAR)))) goto NotEnoughMemoryReturn; // // Construct the name of the child from the given strings. // memcpy(pwszChild, pwszParent, cchParent * sizeof(WCHAR)); pwszChild[cchParent] = L'\\'; memcpy(pwszChild + cchParent + 1, pwszSubKey, cchSubKey * sizeof(WCHAR)); pwszChild[cchChild] = L'\0'; dwErr = ERROR_SUCCESS; CommonReturn: *ppwszChild = pwszChild; return dwErr; InvalidNameReturn: dwErr = ERROR_INVALID_NAME; goto CommonReturn; NotEnoughMemoryReturn: dwErr = ERROR_NOT_ENOUGH_MEMORY; goto CommonReturn; } STATIC DWORD I_MartaRegParseName( IN OUT LPWSTR pwszObject, OUT LPWSTR *ppwszMachine, OUT LPWSTR *ppwszRemaining ) /*++ Routine Description: Parses a registry object name for the machine name. Arguments: pwszObject - the name of the object ppwszMachine - the machine the object is on ppwszRemaining - the remaining name after the machine name Return Value: ERROR_SUCCESS in case of success. ERROR_NOT_ENOUGH_MEMORY if allocation failed. --*/ { if (pwszObject == wcsstr(pwszObject, L"\\\\")) { *ppwszMachine = pwszObject + 2; *ppwszRemaining = wcschr(*ppwszMachine, L'\\'); if (*ppwszRemaining != NULL) { **ppwszRemaining = L'\0'; *ppwszRemaining += 1; } } else { *ppwszMachine = NULL; *ppwszRemaining = pwszObject; } return ERROR_SUCCESS; } STATIC DWORD I_MartaRegInitContext( OUT PREG_CONTEXT *ppRegContext ) /*++ Routine Description: Allocate and initialize memory for the context. Arguments: ppRegContext - To return the pointer to the allcoated memory. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { DWORD dwErr; PREG_CONTEXT pRegContext; if (pRegContext = (PREG_CONTEXT) I_MartaRegZeroAlloc( sizeof(REG_CONTEXT))) { pRegContext->dwRefCnt = 1; dwErr = ERROR_SUCCESS; } else { pRegContext = NULL; dwErr = ERROR_NOT_ENOUGH_MEMORY; } *ppRegContext = pRegContext; return dwErr; } DWORD MartaOpenRegistryKeyNamedObject( IN LPCWSTR pwszObject, IN ACCESS_MASK AccessMask, OUT PMARTA_CONTEXT pContext ) /*++ Routine Description: Open the given registry key with desired access mask and return a context handle. Arguments: pwszObject - Name of the registry key which will be opened. AccessMask - Desired access mask with which the registry key will be opened. pContext - To return a context handle. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { DWORD dwErr = ERROR_SUCCESS; PREG_CONTEXT pRegContext = NULL; LPWSTR pwszDupObject = NULL; HKEY hKeyRemote = NULL; HKEY hKeyBase; // not opened, don't close at return // // Following aren't allocated // LPWSTR pwszMachine, pwszRemaining, pwszBaseKey, pwszSubKey; if (NULL == pwszObject) goto InvalidNameReturn; if (ERROR_SUCCESS != (dwErr = I_MartaRegInitContext(&pRegContext))) goto ErrorReturn; // // Allocate and copy the name into the context // if (ERROR_SUCCESS != (dwErr = I_MartaRegDupString(pwszObject, &pRegContext->pwszObject))) goto ErrorReturn; // // Save another copy of the name since we must crack it. // if (ERROR_SUCCESS != (dwErr = I_MartaRegDupString(pwszObject, &pwszDupObject))) goto ErrorReturn; // // Get the optional machine name and the remaining name // if (ERROR_SUCCESS != (dwErr = I_MartaRegParseName(pwszDupObject, &pwszMachine, &pwszRemaining))) goto ErrorReturn; if (NULL == pwszRemaining) goto InvalidNameReturn; // // Get the base key name and the subkey name // pwszBaseKey = pwszRemaining; pwszSubKey = wcschr(pwszRemaining, L'\\'); if (NULL != pwszSubKey) { *pwszSubKey = L'\0'; pwszSubKey++; // // Advance past any more '\'s separating the BaseKey from the SubKey // while (L'\\' == *pwszSubKey) pwszSubKey++; } if (0 == _wcsicmp(pwszBaseKey, L"MACHINE")) { hKeyBase = HKEY_LOCAL_MACHINE; } else if (0 == _wcsicmp(pwszBaseKey, L"USERS") || 0 == _wcsicmp(pwszBaseKey, L"USER")) { hKeyBase = HKEY_USERS; } else if (NULL == pwszMachine) { // // these are only valid on the local machine // if (0 == _wcsicmp(pwszBaseKey, L"CLASSES_ROOT")) { hKeyBase = HKEY_CLASSES_ROOT; } else if (0 == _wcsicmp(pwszBaseKey, L"CURRENT_USER")) { hKeyBase = HKEY_CURRENT_USER; } else if (0 == _wcsicmp(pwszBaseKey, L"CONFIG")) { hKeyBase = HKEY_CURRENT_CONFIG; } else { goto InvalidParameterReturn; } } else { goto InvalidParameterReturn; } // // If it is a remote name, connect to that registry // if (pwszMachine) { if (ERROR_SUCCESS != (dwErr = RegConnectRegistryW( pwszMachine, hKeyBase, &hKeyRemote ))) goto ErrorReturn; hKeyBase = hKeyRemote; } if (NULL == pwszMachine && (NULL == pwszSubKey || L'\0' == *pwszSubKey)) // // Opening a predefined handle causes the previously opened handle // to be closed. Therefore, we won't reopen here. // pRegContext->hKey = hKeyBase; else { if (ERROR_SUCCESS != (dwErr = RegOpenKeyExW( hKeyBase, pwszSubKey, 0, // dwReversed AccessMask, &pRegContext->hKey))) goto ErrorReturn; pRegContext->dwFlags |= REG_CONTEXT_CLOSE_HKEY_FLAG; } dwErr = ERROR_SUCCESS; CommonReturn: I_MartaRegFree(pwszDupObject); if (hKeyRemote) RegCloseKey(hKeyRemote); *pContext = (MARTA_CONTEXT) pRegContext; return dwErr; ErrorReturn: if (pRegContext) { MartaCloseRegistryKeyContext((MARTA_CONTEXT) pRegContext); pRegContext = NULL; } assert(ERROR_SUCCESS != dwErr); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn; InvalidNameReturn: dwErr = ERROR_INVALID_NAME; goto ErrorReturn; InvalidParameterReturn: dwErr = ERROR_INVALID_PARAMETER; goto ErrorReturn; } void I_MartaRegFreeFindData( IN PREG_FIND_DATA pRegFindData ) /*++ Routine Description: Free up the memory associated with the internal structure. Arguments: pRegFindData - Internal structure to be freed. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { if (NULL == pRegFindData) return; if (pRegFindData->pRegParentContext) MartaCloseRegistryKeyContext(pRegFindData->pRegParentContext); I_MartaRegFree(pRegFindData); } DWORD MartaCloseRegistryKeyContext( IN MARTA_CONTEXT Context ) /*++ Routine Description: Close the context. Arguments: Context - Context to be closed. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { PREG_CONTEXT pRegContext = (PREG_CONTEXT) Context; if (NULL == pRegContext || 0 == pRegContext->dwRefCnt) return ERROR_INVALID_PARAMETER; // // If the ref cnt goes to zero then free the handle as well as all other // associated structures. // if (0 == --pRegContext->dwRefCnt) { if (pRegContext->pRegFindData) I_MartaRegFreeFindData(pRegContext->pRegFindData); if (pRegContext->dwFlags & REG_CONTEXT_CLOSE_HKEY_FLAG) RegCloseKey(pRegContext->hKey); I_MartaRegFree(pRegContext->pwszObject); I_MartaRegFree(pRegContext); } return ERROR_SUCCESS; } DWORD MartaAddRefRegistryKeyContext( IN MARTA_CONTEXT Context ) /*++ Routine Description: Bump up the ref count for this context. Arguments: Context - Context whose ref count should be bumped up. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { PREG_CONTEXT pRegContext = (PREG_CONTEXT) Context; if (NULL == pRegContext || 0 == pRegContext->dwRefCnt) return ERROR_INVALID_PARAMETER; pRegContext->dwRefCnt++; return ERROR_SUCCESS; } STATIC inline BOOL I_MartaRegIsPredefinedKey( IN HKEY hKey ) /*++ Routine Description: Find if the given key is a predefined key. Arguments: key - Handle to the key. Return Value: TRUE - if the key is a predefined key. FALSE - Otherwise. --*/ { if (HKEY_CURRENT_USER == hKey || HKEY_LOCAL_MACHINE == hKey || HKEY_USERS == hKey || HKEY_CLASSES_ROOT == hKey || HKEY_CURRENT_CONFIG == hKey) return TRUE; else return FALSE; } DWORD MartaOpenRegistryKeyHandleObject( IN HANDLE Handle, IN ACCESS_MASK AccessMask, OUT PMARTA_CONTEXT pContext ) /*++ Routine Description: Given a registry key handle, open the context with the desired access mask and return a context handle. Arguments: Handle - Existing registry key handle. AccessMask - Desired access mask for open. pContext - To return a handle to the context. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { DWORD dwErr; HKEY hKey = (HKEY) Handle; PREG_CONTEXT pRegContext = NULL; if (ERROR_SUCCESS != (dwErr = I_MartaRegInitContext(&pRegContext))) goto ErrorReturn; if (0 == AccessMask || I_MartaRegIsPredefinedKey(hKey)) pRegContext->hKey = hKey; else { if (ERROR_SUCCESS != (dwErr = RegOpenKeyExW( hKey, NULL, // pwszSubKey 0, // dwReversed AccessMask, &pRegContext->hKey))) goto ErrorReturn; pRegContext->dwFlags |= REG_CONTEXT_CLOSE_HKEY_FLAG; } dwErr = ERROR_SUCCESS; CommonReturn: *pContext = (MARTA_CONTEXT) pRegContext; return dwErr; ErrorReturn: if (pRegContext) { MartaCloseRegistryKeyContext((MARTA_CONTEXT) pRegContext); pRegContext = NULL; } assert(ERROR_SUCCESS != dwErr); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn; } DWORD MartaGetRegistryKeyParentContext( IN MARTA_CONTEXT Context, IN ACCESS_MASK AccessMask, OUT PMARTA_CONTEXT pParentContext ) /*++ Routine Description: Given the context for a registry key, get the context for its parent. Arguments: Context - Context for the registry key. AccessMask - Desired access mask with which the parent will be opened. pParentContext - To return the context for the parent. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { DWORD dwErr; LPWSTR pwszParentObject = NULL; if (ERROR_SUCCESS != (dwErr = MartaConvertRegistryKeyContextToName( Context, &pwszParentObject))) goto ErrorReturn; if (ERROR_SUCCESS != (dwErr = I_MartaRegGetParentString( pwszParentObject))) goto NoParentReturn; MartaOpenRegistryKeyNamedObject( pwszParentObject, AccessMask, pParentContext ); // // Ignore any open errors // dwErr = ERROR_SUCCESS; CommonReturn: I_MartaRegFree(pwszParentObject); return dwErr; NoParentReturn: dwErr = ERROR_SUCCESS; ErrorReturn: *pParentContext = NULL; goto CommonReturn; } DWORD MartaFindFirstRegistryKey( IN MARTA_CONTEXT Context, IN ACCESS_MASK AccessMask, OUT PMARTA_CONTEXT pChildContext ) /*++ Routine Description: FInd the first registry key in the given container. Arguments: Context - Context for the container. AccessMask - Desired access mask for opening the child registry key. pChildContext - To return the context for the first child in the given container. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise Note: Does not free up the current context. --*/ { DWORD dwErr; PREG_CONTEXT pRegParentContext = (PREG_CONTEXT) Context; HKEY hKeyParent = pRegParentContext->hKey; PREG_CONTEXT pRegFirstContext = NULL; PREG_FIND_DATA pRegFindData; // freed as part of pRegFirstContext DWORD cSubKeys; DWORD cchMaxSubKey; if (ERROR_SUCCESS != (dwErr = I_MartaRegInitContext(&pRegFirstContext))) goto ErrorReturn; if (ERROR_SUCCESS != (dwErr = RegQueryInfoKeyW( hKeyParent, NULL, // lpszClass NULL, // lpcchClass NULL, // lpdwReserved &cSubKeys, &cchMaxSubKey, NULL, // lpcchMaxClass NULL, // lpcValues NULL, // lpcchMaxValuesName NULL, // lpcbMaxValueData NULL, // lpcbSecurityDescriptor NULL // lpftLastWriteTime ))) goto ErrorReturn; // // Above returned count doesn't include the terminating null character // cchMaxSubKey++; // // Note: HKEY_CURRENT_CONFIG returns a cchMaxSubKey of 0 ???? // if (MAX_PATH > cchMaxSubKey) cchMaxSubKey = MAX_PATH; if (NULL == (pRegFindData = (PREG_FIND_DATA) I_MartaRegZeroAlloc( sizeof(REG_FIND_DATA) + cchMaxSubKey * sizeof(WCHAR)))) goto NotEnoughMemoryReturn; pRegFirstContext->pRegFindData = pRegFindData; MartaAddRefRegistryKeyContext((MARTA_CONTEXT) pRegParentContext); pRegFindData->pRegParentContext = pRegParentContext; pRegFindData->cSubKeys = cSubKeys; pRegFindData->cchMaxSubKey = cchMaxSubKey; pRegFindData->pwszSubKey = (LPWSTR) (((BYTE *) pRegFindData) + sizeof(REG_FIND_DATA)); // // Following closes / frees pRegFirstContext // dwErr = MartaFindNextRegistryKey( (MARTA_CONTEXT) pRegFirstContext, AccessMask, pChildContext ); CommonReturn: return dwErr; ErrorReturn: if (pRegFirstContext) MartaCloseRegistryKeyContext((MARTA_CONTEXT) pRegFirstContext); *pChildContext = NULL; assert(ERROR_SUCCESS != dwErr); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn; NotEnoughMemoryReturn: dwErr = ERROR_NOT_ENOUGH_MEMORY; goto ErrorReturn; } DWORD MartaFindNextRegistryKey( IN MARTA_CONTEXT Context, IN ACCESS_MASK AccessMask, OUT PMARTA_CONTEXT pSiblingContext ) /*++ Routine Description: Get the next object in the tree. This is the sibling for the current context. Arguments: Context - Context for the current object. AccessMask - Desired access mask for the opening the sibling. pSiblingContext - To return a handle for the sibling. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise Note: Closes the current context. --*/ { DWORD dwErr; PREG_CONTEXT pRegPrevContext = (PREG_CONTEXT) Context; PREG_CONTEXT pRegSiblingContext = NULL; // // Following don't need to be freed or closed // PREG_CONTEXT pRegParentContext; PREG_FIND_DATA pRegFindData; HKEY hKeyParent; DWORD cchMaxSubKey; LPWSTR pwszSubKey; if (ERROR_SUCCESS != (dwErr = I_MartaRegInitContext(&pRegSiblingContext))) goto ErrorReturn; // // Move the FindData on to the sibling context // pRegFindData = pRegPrevContext->pRegFindData; if (NULL == pRegFindData) goto InvalidParameterReturn; pRegPrevContext->pRegFindData = NULL; pRegSiblingContext->pRegFindData = pRegFindData; if (pRegFindData->iSubKey >= pRegFindData->cSubKeys) goto NoMoreItemsReturn; pRegParentContext = pRegFindData->pRegParentContext; hKeyParent = pRegParentContext->hKey; pwszSubKey = pRegFindData->pwszSubKey; cchMaxSubKey = pRegFindData->cchMaxSubKey; if (ERROR_SUCCESS != (dwErr = RegEnumKeyExW( hKeyParent, pRegFindData->iSubKey, pwszSubKey, &cchMaxSubKey, NULL, // lpdwReserved NULL, // lpszClass NULL, // lpcchClass NULL // lpftLastWriteTime ))) goto ErrorReturn; pRegFindData->iSubKey++; if (pRegParentContext->pwszObject) // // Ignore errors. Mainly here for testing purposes. // I_MartaRegCreateChildString( pRegParentContext->pwszObject, pwszSubKey, &pRegSiblingContext->pwszObject ); if (ERROR_SUCCESS == (dwErr = RegOpenKeyExW( hKeyParent, pwszSubKey, 0, // dwReversed AccessMask, &pRegSiblingContext->hKey))) pRegSiblingContext->dwFlags |= REG_CONTEXT_CLOSE_HKEY_FLAG; // // For an error still return this context. This allows the caller // to continue on to the next sibling object and know there was an // error with this sibling object // CommonReturn: MartaCloseRegistryKeyContext(Context); *pSiblingContext = (MARTA_CONTEXT) pRegSiblingContext; return dwErr; ErrorReturn: if (pRegSiblingContext) { MartaCloseRegistryKeyContext((MARTA_CONTEXT) pRegSiblingContext); pRegSiblingContext = NULL; } // kedar wants this mapped to success if (ERROR_NO_MORE_ITEMS == dwErr) dwErr = ERROR_SUCCESS; goto CommonReturn; InvalidParameterReturn: dwErr = ERROR_INVALID_PARAMETER; goto ErrorReturn; NoMoreItemsReturn: dwErr = ERROR_NO_MORE_ITEMS; goto ErrorReturn; } DWORD MartaConvertRegistryKeyContextToName( IN MARTA_CONTEXT Context, OUT LPWSTR *ppwszObject ) /*++ Routine Description: Returns the NT Object Name for the given context. Allocates memory. Arguments: Context - Context for the registry key. ppwszObject - To return the name of the registry key. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { DWORD dwErr = ERROR_SUCCESS; PREG_CONTEXT pRegContext = (PREG_CONTEXT) Context; LPWSTR pwszObject = NULL; BYTE Buff[512]; ULONG cLen = 0; POBJECT_NAME_INFORMATION pNI; // not allocated POBJECT_NAME_INFORMATION pAllocNI = NULL; NTSTATUS Status; if (NULL == pRegContext || 0 == pRegContext->dwRefCnt) goto InvalidParameterReturn; if (pRegContext->pwszObject) { // // Already have the object's name // if (ERROR_SUCCESS != (dwErr = I_MartaRegDupString( pRegContext->pwszObject, &pwszObject))) goto ErrorReturn; else goto SuccessReturn; } else { HKEY hKey = pRegContext->hKey; LPWSTR pwszPath; DWORD cchPath; // // First, determine the size of the buffer we need... // pNI = (POBJECT_NAME_INFORMATION) Buff; Status = NtQueryObject(hKey, ObjectNameInformation, pNI, sizeof(Buff), &cLen); if (!NT_SUCCESS(Status) || sizeof(*pNI) > cLen || 0 == pNI->Name.Length) { if (Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_INFO_LENGTH_MISMATCH || Status == STATUS_BUFFER_OVERFLOW) { // // Allocate a big enough buffer // if (NULL == (pAllocNI = (POBJECT_NAME_INFORMATION) I_MartaRegNonzeroAlloc(cLen))) goto NotEnoughMemoryReturn; pNI = pAllocNI; Status = NtQueryObject(hKey, ObjectNameInformation, pNI, cLen, NULL); if (!NT_SUCCESS(Status)) goto StatusErrorReturn; } else { // // Check if one of the predefined base keys // LPCWSTR pwszBaseKey = NULL; if (HKEY_LOCAL_MACHINE == hKey) pwszBaseKey = L"MACHINE"; else if (HKEY_USERS == hKey) pwszBaseKey = L"USERS"; else if (HKEY_CLASSES_ROOT == hKey) pwszBaseKey = L"CLASSES_ROOT"; else if (HKEY_CURRENT_USER == hKey) pwszBaseKey = L"CURRENT_USER"; else if (HKEY_CURRENT_CONFIG == hKey) pwszBaseKey = L"CONFIG"; else if (!NT_SUCCESS(Status)) goto StatusErrorReturn; else goto InvalidHandleReturn; if (ERROR_SUCCESS != (dwErr = I_MartaRegDupString( pwszBaseKey, &pwszObject))) goto ErrorReturn; else goto SuccessReturn; } } pwszPath = pNI->Name.Buffer; cchPath = pNI->Name.Length / sizeof(WCHAR); if (REG_OBJ_TAG_LEN > cchPath || 0 != _wcsnicmp(pwszPath, REG_OBJ_TAG, REG_OBJ_TAG_LEN)) goto BadPathnameReturn; pwszPath += REG_OBJ_TAG_LEN; cchPath -= REG_OBJ_TAG_LEN; if (NULL == (pwszObject = (LPWSTR) I_MartaRegNonzeroAlloc( (cchPath + 1) * sizeof(WCHAR)))) goto NotEnoughMemoryReturn; memcpy(pwszObject, pwszPath, cchPath * sizeof(WCHAR)); pwszObject[cchPath] = L'\0'; } SuccessReturn: dwErr = ERROR_SUCCESS; CommonReturn: I_MartaRegFree(pAllocNI); *ppwszObject = pwszObject; return dwErr; StatusErrorReturn: dwErr = RtlNtStatusToDosError(Status); ErrorReturn: assert(NULL == pwszObject); assert(ERROR_SUCCESS != dwErr); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn; NotEnoughMemoryReturn: dwErr = ERROR_NOT_ENOUGH_MEMORY; goto ErrorReturn; InvalidHandleReturn: dwErr = ERROR_INVALID_HANDLE; goto ErrorReturn; BadPathnameReturn: dwErr = ERROR_BAD_PATHNAME; goto ErrorReturn; InvalidParameterReturn: dwErr = ERROR_INVALID_PARAMETER; goto ErrorReturn; } DWORD MartaConvertRegistryKeyContextToHandle( IN MARTA_CONTEXT Context, OUT HANDLE *pHandle ) /*++ Routine Description: The following is for testing The returned Handle isn't duplicated. It has the same lifetime as the Context Arguments: Context - Context whose properties the caller has asked for. pHandle - To return the handle. Return Value: ERROR_SUCCESS in case of success. ERROR_NOT_ENOUGH_MEMORY if allocation failed. --*/ { DWORD dwErr; PREG_CONTEXT pRegContext = (PREG_CONTEXT) Context; if (NULL == pRegContext || 0 == pRegContext->dwRefCnt) { *pHandle = NULL; dwErr = ERROR_INVALID_PARAMETER; } else { *pHandle = (HANDLE) pRegContext->hKey; dwErr = ERROR_SUCCESS; } return dwErr; } DWORD MartaGetRegistryKeyProperties( IN MARTA_CONTEXT Context, IN OUT PMARTA_OBJECT_PROPERTIES pProperties ) /*++ Routine Description: Return the properties for registry key represented by the context. Arguments: Context - Context whose properties the caller has asked for. pProperties - To return the properties for this registry key. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { pProperties->dwFlags |= MARTA_OBJECT_IS_CONTAINER; return ERROR_SUCCESS; } DWORD MartaGetRegistryKeyTypeProperties( IN OUT PMARTA_OBJECT_TYPE_PROPERTIES pProperties ) /*++ Routine Description: Return the properties of registry key objects. Arguments: pProperties - To return the properties of registry key objects. Return Value: ERROR_SUCCESS. --*/ { const GENERIC_MAPPING GenMap = { KEY_READ, KEY_WRITE, KEY_EXECUTE, KEY_ALL_ACCESS }; pProperties->dwFlags |= MARTA_OBJECT_TYPE_MANUAL_PROPAGATION_NEEDED_FLAG; pProperties->dwFlags |= MARTA_OBJECT_TYPE_INHERITANCE_MODEL_PRESENT_FLAG; pProperties->GenMap = GenMap; return ERROR_SUCCESS; } DWORD MartaGetRegistryKeyRights( IN MARTA_CONTEXT Context, IN SECURITY_INFORMATION SecurityInfo, OUT PSECURITY_DESCRIPTOR * ppSecurityDescriptor ) /*++ Routine Description: Get the security descriptor for the given handle. Arguments: Context - Context for registry key. SecurityInfo - Type of security information to be read. ppSecurityDescriptor - To return a self-relative security decriptor pointer. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { DWORD dwErr = ERROR_SUCCESS; PREG_CONTEXT pRegContext = (PREG_CONTEXT) Context; DWORD cbSize; PISECURITY_DESCRIPTOR pSecurityDescriptor = NULL; if (NULL == pRegContext || 0 == pRegContext->dwRefCnt) goto InvalidParameterReturn; // // First, get the size we need // cbSize = 0; dwErr = RegGetKeySecurity( pRegContext->hKey, SecurityInfo, NULL, // pSecDesc &cbSize ); if (ERROR_INSUFFICIENT_BUFFER == dwErr) { if (NULL == (pSecurityDescriptor = (PISECURITY_DESCRIPTOR) I_MartaRegNonzeroAlloc(cbSize))) goto NotEnoughMemoryReturn; dwErr = RegGetKeySecurity( pRegContext->hKey, SecurityInfo, pSecurityDescriptor, &cbSize ); } else if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; if (ERROR_SUCCESS != dwErr) goto ErrorReturn; CommonReturn: *ppSecurityDescriptor = pSecurityDescriptor; return dwErr; ErrorReturn: if (pSecurityDescriptor) { I_MartaRegFree(pSecurityDescriptor); pSecurityDescriptor = NULL; } assert(ERROR_SUCCESS != dwErr); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn; NotEnoughMemoryReturn: dwErr = ERROR_NOT_ENOUGH_MEMORY; goto ErrorReturn; InvalidParameterReturn: dwErr = ERROR_INVALID_PARAMETER; goto ErrorReturn; } DWORD MartaSetRegistryKeyRights( IN MARTA_CONTEXT Context, IN SECURITY_INFORMATION SecurityInfo, IN PSECURITY_DESCRIPTOR pSecurityDescriptor ) /*++ Routine Description: Set the given security descriptor on the registry key represented by the context. Arguments: Context - Context for the registry key. SecurityInfo - Type of security info to be stamped on the registry key. pSecurityDescriptor - Security descriptor to be stamped. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { DWORD dwErr = ERROR_SUCCESS; PREG_CONTEXT pRegContext = (PREG_CONTEXT) Context; if (NULL == pRegContext || 0 == pRegContext->dwRefCnt) goto InvalidParameterReturn; dwErr = RegSetKeySecurity( pRegContext->hKey, SecurityInfo, pSecurityDescriptor ); CommonReturn: return dwErr; InvalidParameterReturn: dwErr = ERROR_INVALID_PARAMETER; goto CommonReturn; } ACCESS_MASK MartaGetRegistryKeyDesiredAccess( IN SECURITY_OPEN_TYPE OpenType, IN BOOL Attribs, IN SECURITY_INFORMATION SecurityInfo ) /*++ Routine Description: Gets the access required to open object to be able to set or get the specified security info. Arguments: OpenType - Flag indicating if the object is to be opened to read or write the security information Attribs - TRUE indicates that additional access bits should be returned. SecurityInfo - owner/group/dacl/sacl Return Value: Desired access mask with which open should be called. --*/ { ACCESS_MASK DesiredAccess = 0; if ( (SecurityInfo & OWNER_SECURITY_INFORMATION) || (SecurityInfo & GROUP_SECURITY_INFORMATION) ) { switch (OpenType) { case READ_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL; break; case WRITE_ACCESS_RIGHTS: DesiredAccess |= WRITE_OWNER; break; case MODIFY_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL | WRITE_OWNER; break; } } if (SecurityInfo & DACL_SECURITY_INFORMATION) { switch (OpenType) { case READ_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL; break; case WRITE_ACCESS_RIGHTS: DesiredAccess |= WRITE_DAC; break; case MODIFY_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL | WRITE_DAC; break; } } if (SecurityInfo & SACL_SECURITY_INFORMATION) { DesiredAccess |= READ_CONTROL | ACCESS_SYSTEM_SECURITY; } if (TRUE == Attribs) { DesiredAccess |= KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE; } return (DesiredAccess); } ACCESS_MASK MartaGetRegistryKey32DesiredAccess( IN SECURITY_OPEN_TYPE OpenType, IN BOOL Attribs, IN SECURITY_INFORMATION SecurityInfo ) /*++ Routine Description: Gets the access required to open object to be able to set or get the specified security info. Arguments: OpenType - Flag indicating if the object is to be opened to read or write the security information Attribs - TRUE indicates that additional access bits should be returned. SecurityInfo - owner/group/dacl/sacl Return Value: Desired access mask with which open should be called. --*/ { ACCESS_MASK DesiredAccess = KEY_WOW64_32KEY; if ( (SecurityInfo & OWNER_SECURITY_INFORMATION) || (SecurityInfo & GROUP_SECURITY_INFORMATION) ) { switch (OpenType) { case READ_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL; break; case WRITE_ACCESS_RIGHTS: DesiredAccess |= WRITE_OWNER; break; case MODIFY_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL | WRITE_OWNER; break; } } if (SecurityInfo & DACL_SECURITY_INFORMATION) { switch (OpenType) { case READ_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL; break; case WRITE_ACCESS_RIGHTS: DesiredAccess |= WRITE_DAC; break; case MODIFY_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL | WRITE_DAC; break; } } if (SecurityInfo & SACL_SECURITY_INFORMATION) { DesiredAccess |= READ_CONTROL | ACCESS_SYSTEM_SECURITY; } if (TRUE == Attribs) { DesiredAccess |= KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE; } return (DesiredAccess); } ACCESS_MASK MartaGetDefaultDesiredAccess( IN SECURITY_OPEN_TYPE OpenType, IN BOOL Attribs, IN SECURITY_INFORMATION SecurityInfo ) /*++ Routine Description: Gets the access required to open object to be able to set or get the specified security info. This default routine is used for all resource managers except for files/reg. Arguments: OpenType - Flag indicating if the object is to be opened to read or write the security information Attribs - TRUE indicates that additional access bits should be returned. SecurityInfo - owner/group/dacl/sacl Return Value: Desired access mask with which open should be called. --*/ { ACCESS_MASK DesiredAccess = 0; if ( (SecurityInfo & OWNER_SECURITY_INFORMATION) || (SecurityInfo & GROUP_SECURITY_INFORMATION) ) { switch (OpenType) { case READ_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL; break; case WRITE_ACCESS_RIGHTS: DesiredAccess |= WRITE_OWNER; break; case MODIFY_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL | WRITE_OWNER; break; } } if (SecurityInfo & DACL_SECURITY_INFORMATION) { switch (OpenType) { case READ_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL; break; case WRITE_ACCESS_RIGHTS: DesiredAccess |= WRITE_DAC; break; case MODIFY_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL | WRITE_DAC; break; } } if (SecurityInfo & SACL_SECURITY_INFORMATION) { DesiredAccess |= ACCESS_SYSTEM_SECURITY; } return (DesiredAccess); } DWORD MartaReopenRegistryKeyContext( IN OUT MARTA_CONTEXT Context, IN ACCESS_MASK AccessMask ) /*++ Routine Description: Given the context for a registry key, close the existing handle if one exists and reopen the context with new permissions. Arguments: Context - Context to be reopened. AccessMask - Permissions for the reopen. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { DWORD dwErr; HKEY hKey; PREG_CONTEXT pRegContext = (PREG_CONTEXT) Context; PREG_FIND_DATA pRegFindData = pRegContext->pRegFindData; PREG_CONTEXT pRegParentContext = pRegFindData->pRegParentContext; dwErr = RegOpenKeyExW( pRegParentContext->hKey, pRegFindData->pwszSubKey, 0, // dwReversed AccessMask, &hKey); if (ERROR_SUCCESS == dwErr) { if (pRegContext->dwFlags & REG_CONTEXT_CLOSE_HKEY_FLAG) RegCloseKey(pRegContext->hKey); pRegContext->hKey = hKey; pRegContext->dwFlags |= REG_CONTEXT_CLOSE_HKEY_FLAG; } return dwErr; } DWORD MartaReopenRegistryKeyOrigContext( IN OUT MARTA_CONTEXT Context, IN ACCESS_MASK AccessMask ) /*++ Routine Description: Reopen the original context with a new access mask. Close the original handle. Arguments: Context - Context to reopen. AccessMask - Desired access for open. Return Value: ERROR_SUCCESS in case of success. ERROR_NOT_ENOUGH_MEMORY if allocation failed. --*/ { DWORD dwErr; HKEY hKey; PREG_CONTEXT pRegContext = (PREG_CONTEXT) Context; dwErr = RegOpenKeyExW( pRegContext->hKey, NULL, // pwszSubKey 0, // dwReversed AccessMask, &hKey); if (ERROR_SUCCESS == dwErr) { if (pRegContext->dwFlags & REG_CONTEXT_CLOSE_HKEY_FLAG) RegCloseKey(pRegContext->hKey); pRegContext->hKey = hKey; pRegContext->dwFlags |= REG_CONTEXT_CLOSE_HKEY_FLAG; } return dwErr; } DWORD MartaGetRegistryKeyNameFromContext( IN MARTA_CONTEXT Context, OUT LPWSTR *pObjectName ) /*++ Routine Description: Get the name of the registry key from the context. This routine allocates memory required to hold the name of the object. Arguments: Context - Handle to the context. pObjectName - To return the pointer to the allocated string. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { return MartaConvertRegistryKeyContextToName( Context, pObjectName ); } DWORD MartaGetRegistryKeyParentName( IN LPWSTR ObjectName, OUT LPWSTR *pParentName ) /*++ Routine Description: Given the name of a registry key return the name of its parent. The routine allocates memory required to hold the parent name. Arguments: ObjectName - Name of the registry key. pParentName - To return the pointer to the allocated parent name. In case of the root of the tree, we return NULL parent with ERROR_SUCCESS. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { ULONG Length = wcslen(ObjectName) + 1; PWCHAR Name = (PWCHAR) I_MartaRegNonzeroAlloc(sizeof(WCHAR) * Length); DWORD dwErr = ERROR_SUCCESS; *pParentName = NULL; if (!Name) { return ERROR_NOT_ENOUGH_MEMORY; } wcscpy((WCHAR *) Name, ObjectName); dwErr = I_MartaRegGetParentString(Name); if (ERROR_SUCCESS != dwErr) { I_MartaRegFree(Name); if (ERROR_INVALID_NAME == dwErr) return ERROR_SUCCESS; return dwErr; } *pParentName = Name; return dwErr; }