Windows-Server-2003/net/dhcp/server/binl/menu.c

669 lines
19 KiB
C

/*++
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 != _stricmp(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 (_stricmp(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" )
) < 0 ) {
Error = ERROR_BAD_PATHNAME;
goto Cleanup;
}
if (!BinlAnsiToUnicode(Path, UnicodePath, MAX_PATH*sizeof(WCHAR))) {
Error = ERROR_BAD_PATHNAME;
goto Cleanup;
}
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 IsWinPESif;
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
}
if (!BinlUnicodeToAnsi(FindData.cFileName, &Path[dwPathLen - 5], (USHORT)(dwFileNameLen+1) )) {
continue;
}
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);
IsWinPESif = OscSifIsWinPE(Path);
IsRecoveryOption = ( IsCmdConsSif || IsASRSif || IsWinPESif)
? 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),
"<OPTION VALUE=\"%s\" TIP=\"%s\"> %s\r\n",
Path,
HelpLines,
Description
) < 0 ) {
continue; // path too long, skip it
}
NewItems[sizeof(NewItems)-1] = '\0';
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
) < 0 ) {
Error = ERROR_BAD_PATHNAME;
goto Cleanup;
}
if (!BinlAnsiToUnicode(Path,UnicodePath,MAX_PATH*sizeof(WCHAR))) {
Error = ERROR_BAD_PATHNAME;
goto Cleanup;
}
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
}
if (!BinlUnicodeToAnsi(FindData.cFileName, &Path[dwPathLen - 1], (USHORT)(dwFileNameLen+1))) {
continue; // path too long, skip it
}
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 = strstr(CurLoc, "<OPTION ")) ||
!(OptionEnd = strchr(OptionStart+1, '<' )) ||
!(ValueStart = StrStrIA(OptionStart, "VALUE=\""))) {
break;
}
ValueStart += sizeof("VALUE=\"") - sizeof("");
if (!(ValueEnd = strchr(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"
if (!BinlUnicodeToAnsi(tmpGPO->lpFileSysPath,IniPath,MAX_PATH)) {
continue;
}
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 = strchr(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;
}