/*++ Copyright (c) 1992-1993 Microsoft Corporation Module Name: ConfExp.c Abstract: This module contains NetpExpandConfigString(). Author: JR (John Rogers, JohnRo@Microsoft) 26-May-1992 Revision History: 26-May-1992 JohnRo Created. [but didn't use until April '93 --JR] 13-Apr-1993 JohnRo RAID 5483: server manager: wrong path given in repl dialog. --*/ // These must be included first: #include // NT_SUCCESS(), etc. #include #include #include // IN, LPCTSTR, OPTIONAL, etc. #include // NET_API_STATUS. // These may be included in any order: #include // My prototype. #include // NetpGetWinRegConfigMaxSizes(). #include // ENV_KEYWORD_SYSTEMROOT, etc. #include // IF_DEBUG() #include // NO_ERROR, ERROR_ and NERR_ equates. #include // NetpKdPrint(). #include // NetpMemoryAllocate(), etc. #include // NetpNtStatusToApiStatus(). #include // PREFIX_ equates. #include // NetpAlloc{type}From{type}, STRSIZE(), TCHAR_EOS, etc. #define DEFAULT_ROOT_KEY HKEY_LOCAL_MACHINE #define REG_PATH_TO_ENV (LPTSTR) \ TEXT("System\\CurrentControlSet\\Control\\Session Manager") #define REG_PATH_TO_SYSROOT (LPTSTR) \ TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion") #ifndef TCHAR_PERCENT #define TCHAR_PERCENT TEXT('%') #endif DBGSTATIC NET_API_STATUS NetpAddValueToTempEnvironment( IN OUT PVOID TemporaryEnvironment, IN LPCTSTR KeywordW, IN LPCTSTR ValueW ) { NET_API_STATUS ApiStatus = NO_ERROR; UNICODE_STRING KeywordString; NTSTATUS NtStatus; UNICODE_STRING ValueString; RtlInitUnicodeString( &KeywordString, // dest KeywordW ); // src RtlInitUnicodeString( &ValueString, // dest ValueW ); // src NtStatus = RtlSetEnvironmentVariable( &TemporaryEnvironment, &KeywordString, // name &ValueString ); // value if ( !NT_SUCCESS( NtStatus ) ) { ApiStatus = NetpNtStatusToApiStatus( NtStatus ); NetpAssert( ApiStatus != NO_ERROR ); goto Cleanup; } ApiStatus = NO_ERROR; Cleanup: return (ApiStatus); } // NetpAddValueToTempEnvironment NET_API_STATUS NetpExpandConfigString( IN LPCTSTR UncServerName OPTIONAL, IN LPCTSTR UnexpandedString, OUT LPTSTR * ValueBufferPtr // Must be freed by NetApiBufferFree(). ) /*++ Routine Description: This function expands a value string (which may include references to environment variables). For instance, an unexpanded string might be: %SystemRoot%\System32\Repl\Export This could be expanded to: c:\nt\System32\Repl\Export The expansion makes use of environment variables on UncServerName, if given. This allows remote administration of the directory replicator. Arguments: UncServerName - assumed to NOT BE EXPLICIT LOCAL SERVER NAME. UnexpandedString - points to source string to be expanded. ValueBufferPtr - indicates a pointer which will be set by this routine. This routine will allocate memory for a null-terminated string. The caller must free this with NetApiBufferFree() or equivalent. Return Value: NET_API_STATUS --*/ { NET_API_STATUS ApiStatus = NO_ERROR; LPTSTR ExpandedString = NULL; DWORD LastAllocationSize = 0; NTSTATUS NtStatus; LPTSTR RandomKeywordW = NULL; DWORD RandomValueSize; LPTSTR RandomValueW = NULL; HKEY RootKey = DEFAULT_ROOT_KEY; HKEY SectionKey = DEFAULT_ROOT_KEY; PVOID TemporaryEnvironment = NULL; NetpAssert( sizeof(DWORD) == sizeof(ULONG) ); // Mixing win32 and NT APIs. // // Check for caller errors. // if (ValueBufferPtr == NULL) { // Can't goto Cleanup here, as it assumes this pointer is valid. return (ERROR_INVALID_PARAMETER); } *ValueBufferPtr = NULL; // assume error until proven otherwise. if ( (UnexpandedString == NULL) || ((*UnexpandedString) == TCHAR_EOS ) ) { ApiStatus = ERROR_INVALID_PARAMETER; goto Cleanup; } // // This is probably just a constant string. Can we do it the easy way? // if (STRCHR( UnexpandedString, TCHAR_PERCENT ) == NULL) { // Just need to allocate a copy of the input string. ExpandedString = NetpAllocWStrFromWStr( (LPTSTR) UnexpandedString ); if (ExpandedString == NULL) { ApiStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } // That's all, folks! ApiStatus = NO_ERROR; // // Otherwise, is this local? Maybe we can // handle local expansion the easy (fast) way: using win32 API. // } else if ( (UncServerName==NULL) || ((*UncServerName)==TCHAR_EOS) ) { DWORD CharsRequired = STRLEN( UnexpandedString )+1; NetpAssert( CharsRequired > 1 ); do { // Clean up from previous pass. if (ExpandedString != NULL) { NetpMemoryFree( ExpandedString ); ExpandedString = NULL; } // Allocate the memory. NetpAssert( CharsRequired > 1 ); ExpandedString = NetpMemoryAllocate( CharsRequired * sizeof(TCHAR)); if (ExpandedString == NULL) { ApiStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } LastAllocationSize = CharsRequired * sizeof(TCHAR); IF_DEBUG( CONFIG ) { NetpKdPrint(( PREFIX_NETLIB "NetpExpandConfigString: expanding" " '" FORMAT_LPTSTR "' locally into " FORMAT_LPVOID ", alloc'ed size " FORMAT_DWORD ".\n", UnexpandedString, (LPVOID) ExpandedString, LastAllocationSize )); } // Expand string using local env vars. CharsRequired = ExpandEnvironmentStrings( UnexpandedString, // src ExpandedString, // dest LastAllocationSize / sizeof(TCHAR) ); // dest max char count if (CharsRequired == 0) { ApiStatus = (NET_API_STATUS) GetLastError(); NetpKdPrint(( PREFIX_NETLIB "NetpExpandConfigString: " "ExpandEnvironmentStringsW failed, API status=" FORMAT_API_STATUS ".\n", ApiStatus )); NetpAssert( ApiStatus != NO_ERROR ); goto Cleanup; } } while ((CharsRequired*sizeof(TCHAR)) > LastAllocationSize); IF_DEBUG( CONFIG ) { NetpKdPrint(( PREFIX_NETLIB "NetpExpandConfigString: expanded '" FORMAT_LPTSTR "' to '" FORMAT_LPTSTR "'.\n", UnexpandedString, ExpandedString )); } ApiStatus = NO_ERROR; // // Oh well, remote expansion required. // } else { DWORD DataType; LONG Error; UNICODE_STRING ExpandedUnicode; DWORD SizeRequired; UNICODE_STRING UnexpandedUnicode; // // Connect to remote registry. // Error = RegConnectRegistry( (LPTSTR) UncServerName, DEFAULT_ROOT_KEY, & RootKey ); // result key if (Error != ERROR_SUCCESS) { NetpKdPrint(( PREFIX_NETLIB "NetpExpandConfigString: RegConnectRegistry(machine '" FORMAT_LPTSTR "') ret error " FORMAT_LONG ".\n", UncServerName, Error )); ApiStatus = (NET_API_STATUS) Error; NetpAssert( ApiStatus != NO_ERROR ); goto Cleanup; } NetpAssert( RootKey != DEFAULT_ROOT_KEY ); // // Create a temporary environment, which we'll fill in and let RTL // routines do the expansion for us. // NtStatus = RtlCreateEnvironment( (BOOLEAN) FALSE, // don't clone current env &TemporaryEnvironment ); if ( !NT_SUCCESS( NtStatus ) ) { ApiStatus = NetpNtStatusToApiStatus( NtStatus ); NetpKdPrint(( PREFIX_NETLIB "NetpExpandConfigString: RtlCreateEnvironment failed, " "NT status=" FORMAT_NTSTATUS ", API status=" FORMAT_API_STATUS ".\n", NtStatus, ApiStatus )); NetpAssert( ApiStatus != NO_ERROR ); goto Cleanup; } NetpAssert( TemporaryEnvironment != NULL ); // // Start by populating the temporary environment with SystemRoot. // Error = RegOpenKeyEx( RootKey, REG_PATH_TO_SYSROOT, REG_OPTION_NON_VOLATILE, KEY_READ, // desired access & SectionKey ); if (Error == ERROR_FILE_NOT_FOUND) { ApiStatus = NERR_CfgCompNotFound; goto Cleanup; } else if (Error != ERROR_SUCCESS) { ApiStatus = (NET_API_STATUS) Error; NetpAssert( ApiStatus != NO_ERROR ); goto Cleanup; } NetpAssert( SectionKey != DEFAULT_ROOT_KEY ); ApiStatus = NetpGetWinRegConfigMaxSizes( SectionKey, NULL, // don't need keyword size &RandomValueSize ); // set max value size. if (ApiStatus != NO_ERROR) { goto Cleanup; } NetpAssert( RandomValueSize > 0 ); RandomValueW = NetpMemoryAllocate( RandomValueSize ); if (RandomValueW == NULL) { ApiStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } Error = RegQueryValueEx( SectionKey, (LPTSTR) ENV_KEYWORD_SYSTEMROOT, NULL, // reserved & DataType, (LPVOID) RandomValueW, // out: value string (TCHARs). & RandomValueSize ); if (Error != ERROR_SUCCESS) { ApiStatus = (NET_API_STATUS) Error; goto Cleanup; } if (DataType != REG_SZ) { NetpKdPrint(( PREFIX_NETLIB "NetpExpandConfigString: unexpected data type " FORMAT_DWORD ".\n", DataType )); ApiStatus = ERROR_INVALID_DATA; goto Cleanup; } if ( (RandomValueSize == 0) || ((RandomValueSize % sizeof(TCHAR)) != 0) ) { NetpKdPrint(( PREFIX_NETLIB "NetpExpandConfigString: unexpected data size " FORMAT_DWORD ".\n", RandomValueSize )); ApiStatus = ERROR_INVALID_DATA; goto Cleanup; } ApiStatus = NetpAddValueToTempEnvironment( TemporaryEnvironment, (LPTSTR) ENV_KEYWORD_SYSTEMROOT, RandomValueW ); if (ApiStatus != NO_ERROR) { goto Cleanup; } // // Loop until we have enough storage. // Expand the string. // SizeRequired = STRSIZE( UnexpandedString ); // First pass, try same size RtlInitUnicodeString( &UnexpandedUnicode, // dest (PCWSTR) UnexpandedString ); // src do { // Clean up from previous pass. if (ExpandedString != NULL) { NetpMemoryFree( ExpandedString ); ExpandedString = NULL; } // Allocate the memory. NetpAssert( SizeRequired > 0 ); ExpandedString = NetpMemoryAllocate( SizeRequired ); if (ExpandedString == NULL) { ApiStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } LastAllocationSize = SizeRequired; ExpandedUnicode.MaximumLength = (USHORT)SizeRequired; ExpandedUnicode.Buffer = ExpandedString; IF_DEBUG( CONFIG ) { NetpKdPrint(( PREFIX_NETLIB "NetpExpandConfigString: expanding" " '" FORMAT_LPTSTR "' remotely into " FORMAT_LPVOID ", alloc'ed size " FORMAT_DWORD ".\n", UnexpandedString, (LPVOID) ExpandedString, SizeRequired )); } NtStatus = RtlExpandEnvironmentStrings_U( TemporaryEnvironment, // env to use &UnexpandedUnicode, // source &ExpandedUnicode, // dest (PULONG) &SizeRequired ); // dest size needed next time. if ( NtStatus == STATUS_BUFFER_TOO_SMALL ) { NetpAssert( SizeRequired > LastAllocationSize ); continue; // try again with larger buffer. } else if ( !NT_SUCCESS( NtStatus ) ) { ApiStatus = NetpNtStatusToApiStatus( NtStatus ); NetpKdPrint(( PREFIX_NETLIB "NetpExpandConfigString: " "RtlExpandEnvironmentStrings_U failed, " "NT status=" FORMAT_NTSTATUS ", API status=" FORMAT_API_STATUS ".\n", NtStatus, ApiStatus )); NetpAssert( ApiStatus != NO_ERROR ); goto Cleanup; } else { NetpAssert( NT_SUCCESS( NtStatus ) ); break; // All done. } } while (SizeRequired > LastAllocationSize); ApiStatus = NO_ERROR; } Cleanup: IF_DEBUG( CONFIG ) { NetpKdPrint(( PREFIX_NETLIB "NetpExpandConfigString: returning, API status=" FORMAT_API_STATUS ".\n", ApiStatus )); } if (ApiStatus == NO_ERROR) { NetpAssert( ExpandedString != NULL ); *ValueBufferPtr = ExpandedString; } else { *ValueBufferPtr = NULL; if (ExpandedString != NULL) { NetpMemoryFree( ExpandedString ); } } if (RandomKeywordW != NULL) { NetpMemoryFree( RandomKeywordW ); } if (RandomValueW != NULL) { NetpMemoryFree( RandomValueW ); } if (RootKey != DEFAULT_ROOT_KEY) { (VOID) RegCloseKey( RootKey ); } if (SectionKey != DEFAULT_ROOT_KEY) { (VOID) RegCloseKey( SectionKey ); } if (TemporaryEnvironment != NULL) { (VOID) RtlDestroyEnvironment( TemporaryEnvironment ); } return (ApiStatus); } // NetpExpandConfigString