/*++ Copyright (c) 1997-1998 Microsoft Corporation Module Name: menu.c Abstract: This module contains the code to process OS Chooser message for the BINL server. Author: Adam Barr (adamba) 9-Jul-1997 Geoff Pease (gpease) 10-Nov-1997 Environment: User Mode - Win32 Revision History: --*/ #include "binl.h" #pragma hdrstop BOOL IsIncompatibleRiprepSIF( PCHAR Path, PCLIENT_STATE clientState ) { CHAR HalName[32]; CHAR ImageType[32]; PCHAR DetectedHalName; BOOL RetVal; ImageType[0] = '\0'; HalName[0] = '\0'; // // if it's not an RIPREP image, then just bail out. // GetPrivateProfileStringA( OSCHOOSER_SIF_SECTIONA, "ImageType", "", ImageType, sizeof(ImageType)/sizeof(ImageType[0]), Path ); if (0 != StrCmpIA(ImageType,"SYSPREP")) { RetVal = FALSE; goto exit; } // // retrieve the hal name from the SIF file // GetPrivateProfileStringA( OSCHOOSER_SIF_SECTIONA, "HalName", "", HalName, sizeof(HalName)/sizeof(HalName[0]), Path ); // // if the hal name isn't present, assume it's an old SIF that // doesn't have the hal type in it, and so we just return success // if (*HalName == '\0') { RetVal = FALSE; goto exit; } // // retrieve the detected HAL type from earlier // DetectedHalName = OscFindVariableA( clientState, "HALTYPE" ); if (StrCmpIA(HalName,DetectedHalName)==0) { RetVal = FALSE; goto exit; } // // if we got this far, the SIF file is incompatible // RetVal = TRUE; exit: return(RetVal); } DWORD OscAppendTemplatesMenus( PCHAR *GeneratedScreen, PDWORD dwGeneratedSize, PCHAR DirToEnum, PCLIENT_STATE clientState, BOOLEAN RecoveryOptionsOnly ) { DWORD Error = ERROR_SUCCESS; WIN32_FIND_DATA FindData; HANDLE hFind; int x = 1; CHAR Path[MAX_PATH]; WCHAR UnicodePath[MAX_PATH]; DWORD dwGeneratedCurrentLength; TraceFunc("OscAppendTemplatesMenus( )\n"); BinlAssert( *GeneratedScreen != NULL ); // // The incoming size is the current length of the buffer // dwGeneratedCurrentLength = *dwGeneratedSize; // Resulting string should be something like: // "D:\RemoteInstall\English\Images\nt50.wks\i386\Templates\*.sif" if ( _snprintf( Path, sizeof(Path) / sizeof(Path[0]), "%s\\%s\\Templates\\*.sif", DirToEnum, OscFindVariableA( clientState, "MACHINETYPE" ) ) == -1 ) { Error = ERROR_BAD_PATHNAME; goto Cleanup; } mbstowcs( UnicodePath, Path, strlen(Path) + 1 ); BinlPrintDbg(( DEBUG_OSC, "Enumerating: %s\n", Path )); hFind = FindFirstFile( UnicodePath, (LPVOID) &FindData ); if ( hFind != INVALID_HANDLE_VALUE ) { DWORD dwPathLen; dwPathLen = strlen( Path ); do { // // If it is not a directory, try to open it // if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { CHAR Description[DESCRIPTION_SIZE]; CHAR HelpLines[HELPLINES_SIZE]; PCHAR NewScreen; // temporary points to newly generated screen DWORD dwErr; DWORD dwFileNameLen; CHAR NewItems[ MAX_PATH * 2 + 512 ]; // arbitrary size DWORD dwNewItemsLength; BOOLEAN IsCmdConsSif; BOOLEAN IsASRSif; BOOLEAN IsRecoveryOption; // // Resulting string should be something like: // "D:\RemoteInstall\English\Images\nt50.wks\i386\Templates\Winnt.Sif" dwFileNameLen = wcslen(FindData.cFileName); if (dwPathLen + dwFileNameLen - 4 > sizeof(Path) / sizeof(Path[0])) { continue; // path too long, skip it } wcstombs( &Path[dwPathLen - 5], FindData.cFileName, dwFileNameLen + 1 ); BinlPrintDbg(( DEBUG_OSC, "Found SIF File: %s\n", Path )); // // Check that the image is the type we are looking for // IsCmdConsSif = OscSifIsCmdConsA(Path); IsASRSif = OscSifIsASR(Path); IsRecoveryOption = ( IsCmdConsSif || IsASRSif ) ? TRUE : FALSE; if ((RecoveryOptionsOnly && !IsRecoveryOption) || (!RecoveryOptionsOnly && IsRecoveryOption)) { continue; // not readable, skip it } if (IsIncompatibleRiprepSIF(Path,clientState)) { // // skip it // BinlPrintDbg(( DEBUG_OSC, "Skipping %s because it's an incompatible RIPREP SIF\n", Path )); continue; } // // Retrieve the description // dwErr = GetPrivateProfileStringA(OSCHOOSER_SIF_SECTIONA, "Description", "", Description, DESCRIPTION_SIZE, Path ); if ( dwErr == 0 || Description[0] == L'\0' ) continue; // not readible, skip it // // Retrieve the help lines // dwErr = GetPrivateProfileStringA(OSCHOOSER_SIF_SECTIONA, "Help", "", HelpLines, HELPLINES_SIZE, Path ); // // Create the new item that look like this: // <OPTION VALUE="sif_filename.ext" TIP="Help_Lines"> Description\r\n // if ( _snprintf( NewItems, sizeof(NewItems) / sizeof(NewItems[0]), "<OPTION VALUE=\"%s\" TIP=\"%s\"> %s\r\n", Path, HelpLines, Description ) == -1 ) { continue; // path too long, skip it } dwNewItemsLength = strlen( NewItems ); // // Check to see if we have to grow the buffer... // if ( dwNewItemsLength + dwGeneratedCurrentLength >= *dwGeneratedSize ) { // // Grow the buffer (add in some slop too)... // NewScreen = BinlAllocateMemory( dwNewItemsLength + dwGeneratedCurrentLength + GENERATED_SCREEN_GROW_SIZE ); if( NewScreen == NULL ) { return ERROR_NOT_ENOUGH_SERVER_MEMORY; } memcpy( NewScreen, *GeneratedScreen, *dwGeneratedSize ); BinlFreeMemory(*GeneratedScreen); *GeneratedScreen = NewScreen; *dwGeneratedSize = dwNewItemsLength + dwGeneratedCurrentLength + GENERATED_SCREEN_GROW_SIZE; } // // Add the new items to the screen // strcat( *GeneratedScreen, NewItems ); dwGeneratedCurrentLength += dwNewItemsLength; x++; // move to next line } } while (FindNextFile( hFind, (LPVOID) &FindData )); FindClose( hFind ); } else { OscCreateWin32SubError( clientState, GetLastError( ) ); Error = ERROR_BINL_FAILED_TO_GENERATE_SCREEN; } // // We do this so that we only transmitted what is needed // // *dwGeneratedSize = dwGeneratedCurrentLength + 1; // plus 1 for the NULL character Cleanup: return Error; } // // SearchAndGenerateOSMenu() // DWORD SearchAndGenerateOSMenu( PCHAR *GeneratedScreen, PDWORD dwGeneratedSize, PCHAR DirToEnum, PCLIENT_STATE clientState ) { DWORD Error = ERROR_SUCCESS; DWORD err; // not a return value WIN32_FIND_DATA FindData; HANDLE hFind; int x = 1; CHAR Path[MAX_PATH]; WCHAR UnicodePath[MAX_PATH]; BOOLEAN SearchingCmdCons; TraceFunc("SearchAndGenerateOSMenu( )\n"); BinlAssert( *GeneratedScreen != NULL ); Error = ImpersonateSecurityContext( &clientState->ServerContextHandle ); if ( Error != STATUS_SUCCESS ) { BinlPrintDbg(( DEBUG_OSC_ERROR, "ImpersonateSecurityContext: 0x%08x\n", Error )); if ( !NT_SUCCESS(Error)) { return Error; } } // // Resulting string should be something like: // "D:\RemoteInstall\Setup\English\Images\*" // // We special case the CMDCONS directive to search in the Images directory. // SearchingCmdCons = (BOOLEAN)(!_stricmp(DirToEnum, "CMDCONS")); if ( _snprintf( Path, sizeof(Path) / sizeof(Path[0]), "%s\\Setup\\%s\\%s\\*", IntelliMirrorPathA, OscFindVariableA( clientState, "LANGUAGE" ), SearchingCmdCons ? REMOTE_INSTALL_IMAGE_DIR_A : DirToEnum ) == -1 ) { Error = ERROR_BAD_PATHNAME; goto Cleanup; } mbstowcs( UnicodePath, Path, strlen(Path) + 1 ); hFind = FindFirstFile( UnicodePath, (LPVOID) &FindData ); if ( hFind != INVALID_HANDLE_VALUE ) { DWORD dwPathLen = strlen( Path ); // // Loop enumerating each subdirectory's MachineType\Templates for // SIF files. // do { // // Ignore current and parent directories, but search other // directories. // if (wcscmp(FindData.cFileName, L".") && wcscmp(FindData.cFileName, L"..") && (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )) { DWORD dwFileNameLen; // // Add the sub-directory to the path // dwFileNameLen = wcslen( FindData.cFileName ); if (dwPathLen + dwFileNameLen > sizeof(Path)/sizeof(Path[0])) { continue; // path too long, skip it } wcstombs( &Path[dwPathLen - 1] , FindData.cFileName, dwFileNameLen + 1); BinlPrintDbg(( DEBUG_OSC, "Found OS Directory: %s\n", Path )); // // Then enumerate the templates and add them to the menu screen // OscAppendTemplatesMenus( GeneratedScreen, dwGeneratedSize, Path, clientState, SearchingCmdCons ); } } while (FindNextFile( hFind, (LPVOID) &FindData )); FindClose( hFind ); } else { OscCreateWin32SubError( clientState, GetLastError( ) ); Error = ERROR_BINL_FAILED_TO_GENERATE_SCREEN; } Cleanup: err = RevertSecurityContext( &clientState->ServerContextHandle ); if ( err != STATUS_SUCCESS ) { BinlPrintDbg(( DEBUG_OSC_ERROR, "RevertSecurityContext: 0x%08x\n", Error )); OscCreateWin32SubError( clientState, err ); Error = ERROR_BINL_FAILED_TO_GENERATE_SCREEN; } return Error; } // // FilterFormOptions() - for every option in this form, scan the GPO // list for oscfilter.ini, in each one see if there is an entry in // section [SectionName] that indicates if each option should be // filtered out. // #define MAX_INI_SECTION_SIZE 512 typedef struct _FORM_OPTION { ULONG Result; PCHAR ValueName; PCHAR TagStart; ULONG TagLength; struct _FORM_OPTION * Next; } FORM_OPTION, *PFORM_OPTION; DWORD FilterFormOptions( PCHAR OutMessage, PCHAR FilterStart, PULONG OutMessageLength, PCHAR SectionName, PCLIENT_STATE ClientState ) { PCHAR OptionStart, OptionEnd, ValueStart, ValueEnd, CurLoc; PCHAR ValueName, EqualSign; PFORM_OPTION Options = NULL, TmpOption; PCHAR IniSection = NULL; ULONG ValueLen; BOOLEAN Impersonating = FALSE; CHAR IniPath[MAX_PATH]; PGROUP_POLICY_OBJECT pGPOList = NULL, tmpGPO; DWORD Error, BytesRead, i; DWORD OptionCount = 0; // // First scan the form and find all the OPTION tags. For each one, // we save a point to the value name, the location and length of the // tag, and a place to store the current result for that tag (if // the result is 1, then the tag stays, otherwise it is deleted). // CurLoc = FilterStart; while (TRUE) { // // Find the next option/end-of-option/value/end-of-value // if (!(OptionStart = StrStrIA(CurLoc, "<OPTION ")) || !(OptionEnd = StrChrA(OptionStart+1, '<' )) || !(ValueStart = StrStrIA(OptionStart, "VALUE=\""))) { break; } ValueStart += sizeof("VALUE=\"") - sizeof(""); if (!(ValueEnd = StrChrA(ValueStart, '\"'))) { break; } ValueLen = (ULONG)(ValueEnd - ValueStart); // // Allocate and fill in a FORM_OPTION for this option. // TmpOption = BinlAllocateMemory(sizeof(FORM_OPTION)); if (!TmpOption) { break; } TmpOption->ValueName = BinlAllocateMemory(ValueLen + 1); if (!TmpOption->ValueName) { BinlFreeMemory(TmpOption); break; } TmpOption->Result = 1; strncpy(TmpOption->ValueName, ValueStart, ValueLen); TmpOption->ValueName[ValueLen] = '\0'; TmpOption->TagStart = OptionStart; TmpOption->TagLength = (ULONG)(OptionEnd - OptionStart); ++OptionCount; // // Now link it at the head of Options. // TmpOption->Next = Options; Options = TmpOption; // // Continue looking for options. // CurLoc = OptionEnd; } if (!Options) { goto Cleanup; // didn't find any, so don't bother filtering } // // Now scan the GPO list. // Error = OscImpersonate(ClientState); if (Error != ERROR_SUCCESS) { BinlPrintDbg((DEBUG_ERRORS, "FilterFormOptions: OscImpersonate failed %lx\n", Error)); goto Cleanup; } Impersonating = TRUE; if (!GetGPOList(ClientState->UserToken, NULL, NULL, NULL, 0, &pGPOList)) { BinlPrintDbg((DEBUG_ERRORS, "FilterFormOptions: GetGPOList failed %lx\n", GetLastError())); goto Cleanup; } IniSection = BinlAllocateMemory(MAX_INI_SECTION_SIZE); if (!IniSection) { BinlPrintDbg((DEBUG_ERRORS, "FilterFormOptions: Allocate %d failed\n", MAX_INI_SECTION_SIZE)); goto Cleanup; } for (tmpGPO = pGPOList; tmpGPO != NULL; tmpGPO = tmpGPO->pNext) { // // Try to open our .ini file. We read the whole section so // that we only go over the network once. // #define OSCFILTER_INI_PATH "\\Microsoft\\RemoteInstall\\oscfilter.ini" wcstombs(IniPath, tmpGPO->lpFileSysPath, wcslen(tmpGPO->lpFileSysPath) + 1); if (strlen(IniPath) + sizeof(OSCFILTER_INI_PATH) > sizeof(IniPath)/sizeof(IniPath[0])) { continue; // path too long, skip it } strcat(IniPath, OSCFILTER_INI_PATH); memset( IniSection, '\0', MAX_INI_SECTION_SIZE ); BytesRead = GetPrivateProfileSectionA( SectionName, IniSection, MAX_INI_SECTION_SIZE, IniPath); if (BytesRead == 0) { BinlPrintDbg((DEBUG_POLICY, "FilterFormOptions: Could not read [%s] section in %s\n", SectionName, IniPath)); continue; } BinlPrintDbg((DEBUG_POLICY, "FilterFormOptions: Found [%s] section in %s\n", SectionName, IniPath)); // // GetPrivateProfileSectionA puts a NULL character after every // option, but in fact we don't want that since we use StrStrIA // below. // for (i = 0; i < BytesRead; i++) { if (IniSection[i] == '\0') { IniSection[i] = ' '; } } // // We have the section, now walk the list of options seeing if this // section has something for that value name. // for (TmpOption = Options; TmpOption != NULL; TmpOption = TmpOption->Next) { if ((ValueName = StrStrIA(IniSection, TmpOption->ValueName)) && (EqualSign = StrChrA(ValueName, '='))) { TmpOption->Result = strtol(EqualSign+1, NULL, 10); BinlPrintDbg((DEBUG_POLICY, "FilterFormOptions: Found %s = %d\n", TmpOption->ValueName, TmpOption->Result)); } } } // // Now we have figured out the results for all the options in the // form, clean up the file if needed. // // NOTE: We rely on the fact that the option list is sorted from // last option to first, so that when we remove an option and // slide the rest of the file up, we don't affect any of the // TmpOption->TagStart values that we have not yet processed. // for (TmpOption = Options; TmpOption != NULL; TmpOption = TmpOption->Next) { if (TmpOption->Result == 0) { *OutMessageLength -= TmpOption->TagLength; memmove( TmpOption->TagStart, TmpOption->TagStart + TmpOption->TagLength, *OutMessageLength - (size_t)(TmpOption->TagStart - OutMessage)); --OptionCount; } } Cleanup: if (pGPOList) { FreeGPOList(pGPOList); } if (IniSection) { BinlFreeMemory(IniSection); } // // Free the options chain. // while (Options) { TmpOption = Options->Next; BinlFreeMemory(Options->ValueName); BinlFreeMemory(Options); Options = TmpOption; } if (Impersonating) { OscRevert(ClientState); } return OptionCount; }