/*++ Copyright (c) 1996 Microsoft Corporation Module Name: registry.c Abstract: Server side support for Cluster registry database APIs Author: John Vert (jvert) 8-Mar-1996 Revision History: --*/ #include "apip.h" PAPI_HANDLE ApipMakeKeyHandle( IN HDMKEY Key ) /*++ Routine Description: Allocates and initializes an API_HANDLE structure for the specified HDMKEY. Arguments: Key - Supplies the HDMKEY. Return Value: A pointer to the initialized API_HANDLE structure on success. NULL on memory allocation failure. --*/ { PAPI_HANDLE Handle; Handle = LocalAlloc(LMEM_FIXED, sizeof(API_HANDLE)); if (Handle == NULL) { return(NULL); } Handle->Type = API_KEY_HANDLE; Handle->Flags = 0; Handle->Key = Key; InitializeListHead(&Handle->NotifyList); return(Handle); } HKEY_RPC s_ApiGetRootKey( IN handle_t IDL_handle, IN DWORD samDesired, OUT error_status_t *Status ) /*++ Routine Description: Opens the registry key at the root of the cluster registry database Arguments: IDL_handle - Supplies RPC binding handle, not used. samDesired - Supplies requested security access Status - Returns error code, if any. Return Value: A handle to the opened registry key. --*/ { DWORD Error; HDMKEY Key; PAPI_HANDLE Handle=NULL; *Status = RpcImpersonateClient(NULL); if (*Status != RPC_S_OK) { goto FnExit; } Key = DmGetRootKey(samDesired); RpcRevertToSelf(); if (Key == NULL) { *Status = GetLastError(); } else { Handle = ApipMakeKeyHandle(Key); if (Handle == NULL) { DmCloseKey(Key); *Status = ERROR_NOT_ENOUGH_MEMORY; } else { *Status = ERROR_SUCCESS; } } FnExit: return(Handle); } HKEY_RPC s_ApiCreateKey( IN HKEY_RPC hKey, IN LPCWSTR lpSubKey, IN DWORD dwOptions, IN DWORD samDesired, IN PRPC_SECURITY_ATTRIBUTES lpSecurityAttributes, OUT LPDWORD lpdwDisposition, OUT error_status_t *Status ) /*++ Routine Description: Creates a key in the cluster registry. If the key exists, it is opened. If it does not exist, it is created on all nodes in the cluster. Arguments: hKey - Supplies the key that the create is relative to. lpSubKey - Supplies the key name relative to hKey dwOptions - Supplies any registry option flags. The only currently supported option is REG_OPTION_VOLATILE samDesired - Supplies desired security access mask lpSecurityAttributes - Supplies security for the newly created key. Disposition - Returns whether the key was opened (REG_OPENED_EXISTING_KEY) or created (REG_CREATED_NEW_KEY) Status - Returns the error code if the function is unsuccessful. Return Value: A handle to the specified key if successful NULL otherwise. --*/ { HDMKEY NewKey; PAPI_HANDLE Handle = NULL; PAPI_HANDLE RootHandle = NULL; if (hKey != NULL) { RootHandle = (PAPI_HANDLE)hKey; if (RootHandle->Type != API_KEY_HANDLE) { *Status = ERROR_INVALID_HANDLE; return(NULL); } } if (ApiState != ApiStateOnline) { *Status = ERROR_SHARING_PAUSED; return(NULL); } *Status = RpcImpersonateClient(NULL); if (*Status != RPC_S_OK) { return(NULL); } if ( ARGUMENT_PRESENT( lpSecurityAttributes ) && (lpSecurityAttributes->RpcSecurityDescriptor.lpSecurityDescriptor != NULL) && !RtlValidRelativeSecurityDescriptor( lpSecurityAttributes->RpcSecurityDescriptor.lpSecurityDescriptor, lpSecurityAttributes->RpcSecurityDescriptor.cbInSecurityDescriptor, 0 ) ) { *Status = ERROR_INVALID_SECURITY_DESCR; goto FnExit; } NewKey = DmCreateKey(hKey ? RootHandle->Key : NULL, lpSubKey, dwOptions, samDesired, ARGUMENT_PRESENT(lpSecurityAttributes) ? lpSecurityAttributes->RpcSecurityDescriptor.lpSecurityDescriptor : NULL, lpdwDisposition); if (NewKey == NULL) { *Status = GetLastError(); } else { Handle = ApipMakeKeyHandle(NewKey); if (Handle == NULL) { DmCloseKey(NewKey); *Status = ERROR_NOT_ENOUGH_MEMORY; } else { *Status = ERROR_SUCCESS; } } FnExit: RpcRevertToSelf(); return(Handle); } HKEY_RPC s_ApiOpenKey( IN HKEY_RPC hKey, IN LPCWSTR lpSubKey, IN DWORD samDesired, OUT error_status_t *Status ) /*++ Routine Description: Opens a key in the cluster registry. If the key exists, it is opened. If it does not exist, the call fails. Arguments: hKey - Supplies the key that the open is relative to. lpSubKey - Supplies the key name relative to hKey samDesired - Supplies desired security access mask Status - Returns the error code if the function is unsuccessful. Return Value: A handle to the specified key if successful NULL otherwise. --*/ { HDMKEY NewKey; PAPI_HANDLE Handle=NULL; PAPI_HANDLE RootHandle; if (hKey != NULL) { RootHandle = (PAPI_HANDLE)hKey; if (RootHandle->Type != API_KEY_HANDLE) { *Status = ERROR_INVALID_HANDLE; return(NULL); } } *Status = RpcImpersonateClient(NULL); if (*Status != RPC_S_OK) { goto FnExit; } NewKey = DmOpenKey((hKey) ? RootHandle->Key : NULL, lpSubKey, samDesired); if (NewKey == NULL) { *Status = GetLastError(); } else { Handle = ApipMakeKeyHandle(NewKey); if (Handle == NULL) { DmCloseKey(NewKey); *Status = ERROR_NOT_ENOUGH_MEMORY; } else { *Status = ERROR_SUCCESS; } } RpcRevertToSelf(); FnExit: return(Handle); } error_status_t s_ApiEnumKey( IN HKEY_RPC hKey, IN DWORD dwIndex, OUT LPWSTR *KeyName, OUT PFILETIME lpftLastWriteTime ) /*++ Routine Description: Enumerates the subkeys of a cluster registry key. Arguments: hKey - Supplies the registry key for which the subkeys should be enumerated. dwIndex - Supplies the index to be enumerated. KeyName - Returns the name of the dwIndex subkey. The memory allocated for this buffer must be freed by the client. lpftLastWriteTime - Returns the last write time. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { LONG Status; DWORD NameLength; HDMKEY DmKey; VALIDATE_KEY(DmKey, hKey); Status = DmQueryInfoKey(DmKey, NULL, &NameLength, NULL, NULL, NULL, NULL, NULL); if (Status != ERROR_SUCCESS) { return(Status); } NameLength += 1; *KeyName = MIDL_user_allocate(NameLength*sizeof(WCHAR)); if (*KeyName == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); } Status = DmEnumKey(DmKey, dwIndex, *KeyName, &NameLength, lpftLastWriteTime); if (Status != ERROR_SUCCESS) { MIDL_user_free(*KeyName); *KeyName = NULL; } return(Status); } DWORD s_ApiSetValue( IN HKEY_RPC hKey, IN LPCWSTR lpValueName, IN DWORD dwType, IN CONST UCHAR *lpData, IN DWORD cbData ) /*++ Routine Description: This routine sets the named value for the specified cluster registry key. Arguments: hKey - Supplies the cluster registry subkey whose value is to be set lpValueName - Supplies the name of the value to be set. dwType - Supplies the value data type lpData - Supplies a pointer to the value data cbData - Supplies the length of the value data. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { HDMKEY DmKey; VALIDATE_KEY(DmKey, hKey); API_CHECK_INIT(); return(DmSetValue(DmKey, lpValueName, dwType, lpData, cbData)); } DWORD s_ApiDeleteValue( IN HKEY_RPC hKey, IN LPCWSTR lpValueName ) /*++ Routine Description: Removes the specified value from a given registry subkey Arguments: hKey - Supplies the key whose value is to be deleted. lpValueName - Supplies the name of the value to be removed. Return Value: If the function succeeds, the return value is ERROR_SUCCESS. If the function fails, the return value is an error value. --*/ { HDMKEY DmKey; VALIDATE_KEY(DmKey, hKey); API_CHECK_INIT(); return(DmDeleteValue(DmKey, lpValueName)); } error_status_t s_ApiQueryValue( IN HKEY_RPC hKey, IN LPCWSTR lpValueName, OUT LPDWORD lpValueType, OUT PUCHAR lpData, IN DWORD cbData, OUT LPDWORD lpcbRequired ) /*++ Routine Description: Queries a named value for the specified cluster registry subkey Arguments: hKey - Supplies the subkey whose value should be queried lpValueName - Supplies the named value to be queried lpValueType - Returns the type of the value's data lpData - Returns the value's data cbData - Supplies the size (in bytes) of the lpData buffer Returns the number of bytes copied into the lpData buffer If lpData==NULL, cbData is set to the required buffer size and the function returns ERROR_SUCCESS Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { DWORD Status; DWORD BuffSize; HDMKEY DmKey; VALIDATE_KEY(DmKey, hKey); BuffSize = cbData; Status = DmQueryValue(DmKey, lpValueName, lpValueType, lpData, &BuffSize); if ((Status == ERROR_SUCCESS) || (Status == ERROR_MORE_DATA)) { *lpcbRequired = BuffSize; } return(Status); } DWORD s_ApiDeleteKey( IN HKEY hKey, IN LPCWSTR lpSubKey ) /*++ Routine Description: Deletes the specified key. A key that has subkeys cannot be deleted. Arguments: hKey - Supplies a handle to a currently open key. lpSubKey - Points to a null-terminated string specifying the name of the key to delete. This parameter cannot be NULL, and the specified key must not have subkeys. Return Value: If the function succeeds, the return value is ERROR_SUCCESS. If the function fails, the return value is an error value. --*/ { HDMKEY DmKey; VALIDATE_KEY(DmKey, hKey); API_CHECK_INIT(); return(DmDeleteKey(DmKey, lpSubKey)); } error_status_t s_ApiEnumValue( IN HKEY_RPC hKey, IN DWORD dwIndex, OUT LPWSTR *lpValueName, OUT LPDWORD lpType, OUT UCHAR *lpData, IN OUT LPDWORD lpcbData, OUT LPDWORD TotalSize ) /*++ Routine Description: Enumerates the specified value of a registry subkey Arguments: hKey - Supplies the registry key handle dwIndex - Supplies the index of the value to be enumerated lpValueName - Returns the name of the dwIndex'th value. The memory for this name is allocated on the server and must be freed by the client side. lpType - Returns the value data type lpData - Returns the value data lpcbData - Returns the number of bytes written to the lpData buffer. TotalSize - Returns the size of the data Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { LONG Status; DWORD OriginalNameLength; DWORD NameLength; DWORD DataLength; HDMKEY DmKey; VALIDATE_KEY(DmKey, hKey); Status = DmQueryInfoKey(DmKey, NULL, NULL, NULL, &NameLength, NULL, NULL, NULL); if (Status != ERROR_SUCCESS) { return(Status); } NameLength += 1; *lpValueName = MIDL_user_allocate(NameLength * sizeof(WCHAR)); if (*lpValueName == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); } *TotalSize = *lpcbData; // // Chittur Subbaraman (chitturs) - 3/13/2001 // // First of all, at the beginning of this function, a big enough buffer for lpValueName // is allocated. This means that ERROR_SUCCESS or ERROR_MORE_DATA will be returned by // DmEnumValue depending ONLY on the size of the lpData buffer. This info is used by // by the clusapi layer when it makes a decision based on the return code from this // function. // // Note that *TotalSize is initialized to *lpcbData just above. The TotalSize OUT variable // allows the required lpData size to be returned without touching lpcbData. This is // important since lpcbData is declared as the sizeof lpData in the IDL file and that is // what RPC will consider the lpData buffer size as. So, it is important that if the lpData // buffer is not big enough, this function does not change the value of *lpcbData from what // it was originally at IN time. // // Strange behavior of RegEnumValue: If you supply a big enough buffer for lpValueName and // a smaller than required buffer for lpData, then RegEnumValue won't bother to fill in // lpValueName and will return ERROR_MORE_DATA. This irregular behavior is handled by // DmEnumValue. // // For reference pointers, RPC won't allow a client to pass in NULL pointers. That is why // clusapi layer uses dummy variables in case some of the parameters passed in by the // client caller is NULL. // // // Behavior of RegEnumValue (assuming lpValueName buffer is big enough): // (1) If lpData = NULL and lpcbData = NULL, then returns ERROR_SUCCESS. // (2) If lpData = NULL and lpcbData != NULL, then returns ERROR_SUCCESS and sets // *lpcbData to total buffer size required. // (3) If lpData != NULL and lpcbData != NULL, but the data buffer size is smaller than // required size, then returns ERROR_MORE_DATA and sets *lpcbData to the size required. // (4) If lpData != NULL and lpcbData != NULL and the buffer is big enough, then returns // ERROR_SUCCESS and sets *lpcbData to the size of the data copied into lpData. // // OUR GOAL: ClusterRegEnumValue == RegEnumValue. // // // The following cases are handled by this function and the clusapi layer. Note that in this // analysis, we assume that the client has called into clusapi with a big enough lpValueName // buffer size. (If this is not true, then clusapi layer handles that, check ClusterRegEnumValue.) // // Case 1: Client passes in lpData=NULL, lpcbData=NULL to ClusterRegEnumValue. // // In this case, the clusapi layer will point both lpData and lpcbData to local dummy // variables and initialize *lpcbData to 0. Thus, s_ApiEnumValue will see // both lpData and lpcbData as valid pointers. If the data value is bigger than the size of // *lpcbData, then DmEnumValue will return ERROR_MORE_DATA. In this case, *TotalSize will // contain the required buffer size and *lpcbData will be untouched. The client detects this // error code and sets the return status to ERROR_SUCCESS and *lpcbData to *TotalSize. Note // that the 2nd action has less relevance since lpcbData is pointing to a local dummy variable. // If the data value is of zero size, DmEnumValue will return ERROR_SUCCESS. // In such a case, *lpcbData will be set to *TotalSize before returning by this function. // Note that since the data size is 0, DmEnumValue would set *TotalSize to 0 and hence // *lpcbData will also be set to 0. Thus, in this case, when ApiEnumValue returns to the // clusapi layer, lpValueName will be filled in, *lpData will not be changed and *lpcbData // will be set to 0. // // Case 2: Client passes in lpData=NULL, lpcbData!=NULL and *lpcbData=0 to ClusterRegEnumValue. // // In this case, lpData alone will be pointing to a dummy clusapi buffer when ApiEnumValue // is invoked. Thus, s_ApiEnumValue will get both lpData and lpcbData as valid pointers. // If the data size is non-zero, then DmEnumValue will return ERROR_MORE_DATA and // *TotalSize will contain the size of the required buffer. When this function returns, // *lpcbData will remain untouched. As in case 1, the clusapi layer will set status // to ERROR_SUCCESS and *lpcbData to *TotalSize. Thus, the client will see the required // buffer size in *lpcbData. If the data size is zero, then it is handled as in case 1. // // Case 3: Client passes in lpData!=NULL, lpcbData!=NULL, but the data buffer size is smaller than // required. // // In this case, both lpData and lpcbData will be pointing to client buffers (or RPC buffers // representing them) at the entry to s_ApiEnumValue. DmEnumValue will return ERROR_MORE_DATA // and this function will return the size required in *TotalSize. *lpcbData will not be // touched. At the clusapi layer, *lpcbData will be set to *TotalSize and ERROR_MORE_DATA // will be returned to the client. // // Case 4: Client passes in lpData!=NULL, lpcbData!=NULL and the data buffer size is big enough. // // In this case, as in case 3, s_ApiEnumValue will have lpData and lpcbData pointing to // client buffers. DmEnumValue will return ERROR_SUCCESS, data copied to lpData and // *lpcbData will be set to *TotalSize (which is the size of the data copied into the // lpData buffer), before returning. The clusapi layer will return these values to the client. // Status = DmEnumValue(DmKey, dwIndex, *lpValueName, &NameLength, lpType, lpData, TotalSize); if (Status == ERROR_MORE_DATA) { return(Status); } else if (Status != ERROR_SUCCESS) { MIDL_user_free(*lpValueName); *lpValueName = NULL; *lpcbData = 0; } else { // This tells RPC how big the lpData buffer // is so it can copy the buffer to the client. *lpcbData = *TotalSize; } return(Status); } error_status_t s_ApiQueryInfoKey( IN HKEY_RPC hKey, OUT LPDWORD lpcSubKeys, OUT LPDWORD lpcbMaxSubKeyLen, OUT LPDWORD lpcValues, OUT LPDWORD lpcbMaxValueNameLen, OUT LPDWORD lpcbMaxValueLen, OUT LPDWORD lpcbSecurityDescriptor, OUT PFILETIME lpftLastWriteTime ) /*++ Routine Description: Retrieves information about a specified cluster registry key. Arguments: hKey - Supplies the handle of the key. lpcSubKeys - Points to a variable that receives the number of subkeys contained by the specified key. lpcbMaxSubKeyLen - Points to a variable that receives the length, in characters, of the key's subkey with the longest name. The count returned does not include the terminating null character. lpcValues - Points to a variable that receives the number of values associated with the key. lpcbMaxValueNameLen - Points to a variable that receives the length, in characters, of the key's longest value name. The count returned does not include the terminating null character. lpcbMaxValueLen - Points to a variable that receives the length, in bytes, of the longest data component among the key's values. lpcbSecurityDescriptor - Points to a variable that receives the length, in bytes, of the key's security descriptor. lpftLastWriteTime - Pointer to a FILETIME structure. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { HDMKEY DmKey; DWORD Status; VALIDATE_KEY(DmKey, hKey); Status = DmQueryInfoKey(DmKey, lpcSubKeys, lpcbMaxSubKeyLen, lpcValues, lpcbMaxValueNameLen, lpcbMaxValueLen, lpcbSecurityDescriptor, lpftLastWriteTime); return(Status); } error_status_t s_ApiCloseKey( IN OUT HKEY_RPC *pKey ) /*++ Routine Description: Closes a cluster registry key Arguments: pKey - Supplies the key to be closed Returns NULL Return Value: None. --*/ { HDMKEY DmKey; DWORD Status; VALIDATE_KEY(DmKey, *pKey); Status = RpcImpersonateClient(NULL); if (Status != ERROR_SUCCESS) { return(Status); } Status = DmCloseKey(DmKey); RpcRevertToSelf(); LocalFree(*pKey); *pKey = NULL; return(Status); } void HKEY_RPC_rundown( IN HKEY_RPC Key ) /*++ Routine Description: RPC rundown routine for cluster registry keys Arguments: Key - Supplies the handle to be rundown Return Value: None. --*/ { HDMKEY DmKey; //this should not call impersonate client if (((PAPI_HANDLE)(Key))->Type == API_KEY_HANDLE) { DmKey = ((PAPI_HANDLE)(Key))->Key; \ DmCloseKey(DmKey); LocalFree(Key); } } DWORD s_ApiSetKeySecurity( IN HKEY hKey, IN DWORD SecurityInformation, IN PRPC_SECURITY_DESCRIPTOR pRpcSecurityDescriptor ) /*++ Routine Description: Sets the security on the specified registry key. Arguments: hKey - Supplies a handle to a currently open key. SecurityInformation - Supplies the type of security information to be set. pRpcSecurityDescriptor - Supplies the security information Return Value: If the function succeeds, the return value is ERROR_SUCCESS. If the function fails, the return value is an error value. --*/ { HDMKEY DmKey; DWORD Status; PSECURITY_DESCRIPTOR pSecurityDescriptor; VALIDATE_KEY(DmKey, hKey); API_CHECK_INIT(); pSecurityDescriptor = pRpcSecurityDescriptor->lpSecurityDescriptor; if (!RtlValidRelativeSecurityDescriptor( pSecurityDescriptor, pRpcSecurityDescriptor->cbInSecurityDescriptor,0)){ return(ERROR_INVALID_PARAMETER); } Status = RpcImpersonateClient(NULL); if (Status != ERROR_SUCCESS) { return(Status); } Status = DmSetKeySecurity(DmKey, SecurityInformation, pSecurityDescriptor); RpcRevertToSelf(); return(Status); } DWORD s_ApiGetKeySecurity( IN HKEY hKey, IN DWORD SecurityInformation, IN PRPC_SECURITY_DESCRIPTOR pRpcSecurityDescriptor ) /*++ Routine Description: Gets the security from the specified registry key. Arguments: hKey - Supplies a handle to a currently open key. SecurityInformation - Supplies the type of security information to be retrieved. pRpcSecurityDescriptor - Returns the security information Return Value: If the function succeeds, the return value is ERROR_SUCCESS. If the function fails, the return value is an error value. --*/ { HDMKEY DmKey; DWORD cbLength; DWORD Status; PSECURITY_DESCRIPTOR lpSD; VALIDATE_KEY(DmKey, hKey); API_CHECK_INIT(); cbLength = pRpcSecurityDescriptor->cbInSecurityDescriptor; lpSD = LocalAlloc(LMEM_FIXED, cbLength); if (lpSD == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); } Status = RpcImpersonateClient(NULL); if (Status != ERROR_SUCCESS) { LocalFree(lpSD); return(Status); } Status = DmGetKeySecurity(DmKey, SecurityInformation, lpSD, &cbLength); RpcRevertToSelf(); if (Status == ERROR_SUCCESS) { Status = MapSDToRpcSD(lpSD, pRpcSecurityDescriptor); } if (Status != ERROR_SUCCESS) { pRpcSecurityDescriptor->cbInSecurityDescriptor = cbLength; pRpcSecurityDescriptor->cbOutSecurityDescriptor = 0; } LocalFree(lpSD); return(Status); }