You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2478 lines
68 KiB
2478 lines
68 KiB
/*++
|
|
|
|
Copyright (c) 1997-1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
utils.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
|
|
|
|
//
|
|
// When all else fails "Error screen".
|
|
//
|
|
CHAR ErrorScreenHeaders[] =
|
|
"<OSCML>"\
|
|
"<META KEY=F3 ACTION=\"REBOOT\">"
|
|
"<META KEY=ENTER ACTION=\"REBOOT\">"
|
|
"<TITLE> Client Installation Wizard Error "; // there is a %08x after this
|
|
CHAR ErrorScreenBody[] =
|
|
" </TITLE>"
|
|
"<FOOTER> Press F3 to reboot</FOOTER>"
|
|
"<BODY LEFT=3 RIGHT=76><BR><BR>"; // the error message is inserted here
|
|
CHAR ErrorScreenTrailer[] =
|
|
"An error occurred on the server. Please notify your administrator.<BR>"
|
|
"%SUBERROR%<BR>"
|
|
"</BODY>"
|
|
"</OSCML>";
|
|
|
|
#define RANDOM_SEED (98725757)
|
|
static PCWSTR gUsableChars = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
|
|
void
|
|
OscCreateWin32SubError(
|
|
PCLIENT_STATE clientState,
|
|
DWORD Error )
|
|
/*++
|
|
Routine Description:
|
|
|
|
Create a OSC Variable SUBERROR with the actual Win32 error code that
|
|
caused the BINL error.
|
|
|
|
Arguments:
|
|
|
|
clientState - client state to add the variable too.
|
|
|
|
Error - the Win32 error that occurred.
|
|
--*/
|
|
{
|
|
DWORD dwLen;
|
|
PWCHAR ErrorResponse = NULL;
|
|
PWCHAR ErrorMsg = NULL;
|
|
BOOL UsedFallback = FALSE;
|
|
PWCHAR pch;
|
|
DWORD ErrorLength;
|
|
|
|
const WCHAR UnknownErrorMsg[] = L"Unknown Error.";
|
|
const WCHAR ErrorString[] = L"Error: 0x%08x - %s";
|
|
|
|
TraceFunc( "OscCreateWin32SubError( )\n" );
|
|
|
|
// Retrieve the error message from system resources.
|
|
dwLen = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS |
|
|
FORMAT_MESSAGE_FROM_HMODULE |
|
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
Error,
|
|
0,
|
|
(LPWSTR) &ErrorMsg,
|
|
0,
|
|
NULL );
|
|
if ( dwLen == 0 )
|
|
goto Cleanup;
|
|
|
|
#if DBG
|
|
if ( ErrorMsg )
|
|
DebugMemoryAdd( ErrorMsg, __FILE__, __LINE__, "BINL", LPTR, wcslen(ErrorMsg), "ErrorMsg" );
|
|
#endif
|
|
|
|
// If all else fails, just print an error code out.
|
|
if ( ErrorMsg == NULL ) {
|
|
UsedFallback = TRUE;
|
|
ErrorMsg = (PWCHAR) UnknownErrorMsg;
|
|
dwLen = wcslen(ErrorMsg);
|
|
}
|
|
|
|
// The + 4 is the extra characters of the "%08x" of the generated error message.
|
|
ErrorLength = dwLen + (sizeof( ErrorString )/sizeof(ErrorString[0])) + 4;
|
|
ErrorResponse = (PWCHAR) BinlAllocateMemory( ErrorLength * sizeof(WCHAR) );
|
|
if ( ErrorResponse == NULL ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
wsprintf( ErrorResponse, ErrorString, Error, ErrorMsg );
|
|
|
|
// We need to go through the string and elminate any CRs or LFs that
|
|
// FormatMessageA() might have introduced.
|
|
pch = ErrorResponse;
|
|
while ( *pch )
|
|
{
|
|
if ( *pch == '\r' || * pch == '\n' )
|
|
*pch = 32; // change to space
|
|
|
|
pch++;
|
|
}
|
|
|
|
OscAddVariableW( clientState, "SUBERROR", ErrorResponse );
|
|
|
|
Cleanup:
|
|
if ( ErrorResponse ) {
|
|
BinlFreeMemory( ErrorResponse );
|
|
}
|
|
if ( ErrorMsg && !UsedFallback ) {
|
|
BinlFreeMemory( ErrorMsg );
|
|
}
|
|
}
|
|
|
|
void
|
|
OscCreateLDAPSubError(
|
|
PCLIENT_STATE clientState,
|
|
DWORD Error )
|
|
/*++
|
|
Routine Description:
|
|
|
|
Create a OSC Variable SUBERROR with the actual LDAP error code that
|
|
caused the BINL error.
|
|
|
|
Arguments:
|
|
|
|
clientState - client state to add the variable too.
|
|
|
|
Error - the LDAP error that occurred.
|
|
--*/
|
|
{
|
|
DWORD dwLen;
|
|
PWCHAR ErrorResponse = NULL;
|
|
DWORD ErrorLength;
|
|
|
|
const WCHAR LdapErrorMsg[] = L"LDAP Error: 0x%08x";
|
|
|
|
TraceFunc( "OscCreateLDAPSubError( )\n" );
|
|
|
|
// The + 13 is the "0x12345678 - " of the generated error message.
|
|
ErrorLength = wcslen(LdapErrorMsg) + 1 + 13;
|
|
ErrorResponse = (PWCHAR) BinlAllocateMemory( ErrorLength * sizeof(WCHAR) );
|
|
if ( ErrorResponse == NULL ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
wsprintf( ErrorResponse, LdapErrorMsg, Error );
|
|
|
|
OscAddVariableW( clientState, "SUBERROR", ErrorResponse );
|
|
|
|
Cleanup:
|
|
if ( ErrorResponse ) {
|
|
BinlFreeMemory( ErrorResponse );
|
|
}
|
|
}
|
|
|
|
//
|
|
// This routine was stolen from private\ntos\rtl\sertl.c\RtlRunEncodeUnicodeString().
|
|
//
|
|
|
|
VOID
|
|
OscGenerateSeed(
|
|
UCHAR Seed[1]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generates a one-byte seed for use in run encoding/decoding client
|
|
state variables such as passwords.
|
|
|
|
Arguments:
|
|
|
|
Seed - points to a single byte that holds the generated seed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER Time;
|
|
PUCHAR LocalSeed;
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
|
|
//
|
|
// Use the 2nd byte of current time as the seed.
|
|
// This byte seems to be sufficiently random (by observation).
|
|
//
|
|
|
|
Status = NtQuerySystemTime ( &Time );
|
|
BinlAssert(NT_SUCCESS(Status));
|
|
|
|
LocalSeed = (PUCHAR)((PVOID)&Time);
|
|
|
|
i = 1;
|
|
|
|
(*Seed) = LocalSeed[ i ];
|
|
|
|
//
|
|
// Occasionally, this byte could be zero. That would cause the
|
|
// string to become un-decodable, since 0 is the magic value that
|
|
// causes us to re-gen the seed. This loop makes sure that we
|
|
// never end up with a zero byte (unless time is zero, as well).
|
|
//
|
|
|
|
while ( ((*Seed) == 0) && ( i < sizeof( Time ) ) )
|
|
{
|
|
(*Seed) |= LocalSeed[ i++ ] ;
|
|
}
|
|
|
|
if ( (*Seed) == 0 )
|
|
{
|
|
(*Seed) = 1;
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
OscRunEncode(
|
|
IN PCLIENT_STATE ClientState,
|
|
IN LPSTR Data,
|
|
OUT LPSTR * EncodedData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calls RtlRunEncodeUnicodeString for the Data, using the client
|
|
state's random seed. Then convert each byte into a 2-byte
|
|
value so that there are no NULLs in the result.
|
|
|
|
Each byte is encoded into a 2-byte values as follows:
|
|
The first byte has the low 4 bits of the byte in its low 4 bits,
|
|
with 0xf in the high 4 bits
|
|
The second byte has the high 4 bits of the byte in its high 4 bits,
|
|
with 0xf in the low 4 bits
|
|
|
|
Arguments:
|
|
|
|
ClientState - the client state.
|
|
|
|
Data - The data which is to be encoded.
|
|
|
|
EncodedData - An allocated buffer which holds the encoded result.
|
|
|
|
Return Value:
|
|
|
|
The result of the operation.
|
|
|
|
--*/
|
|
{
|
|
STRING String;
|
|
ULONG i;
|
|
LPSTR p;
|
|
|
|
RtlInitAnsiString(&String, Data);
|
|
|
|
*EncodedData = BinlAllocateMemory((String.Length * 2) + 1);
|
|
if (*EncodedData == NULL) {
|
|
return ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
}
|
|
|
|
RtlRunEncodeUnicodeString(&ClientState->Seed, (PUNICODE_STRING)&String);
|
|
|
|
for (i = 0, p = *EncodedData; i < String.Length; i++) {
|
|
*(p++) = Data[i] | 0xf0;
|
|
*(p++) = Data[i] | 0x0f;
|
|
}
|
|
*p = '\0';
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
DWORD
|
|
OscRunDecode(
|
|
IN PCLIENT_STATE ClientState,
|
|
IN LPSTR EncodedData,
|
|
OUT LPSTR * Data
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert the encoded data (see OscRunEncode) into the real bytes,
|
|
then calls RtlRunDecodeUnicodeString on that, using the client
|
|
state's random seed.
|
|
|
|
Arguments:
|
|
|
|
ClientState - the client state.
|
|
|
|
EncodedData - the encoded data from OscRunEncode.
|
|
|
|
Data - An allocated buffer which holds the decoded result.
|
|
|
|
Return Value:
|
|
|
|
The result of the operation.
|
|
|
|
--*/
|
|
{
|
|
STRING String;
|
|
ULONG Count = strlen(EncodedData) / 2;
|
|
ULONG i, j;
|
|
LPSTR p;
|
|
|
|
*Data = BinlAllocateMemory(Count + 1);
|
|
if (*Data == NULL) {
|
|
return ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
}
|
|
|
|
for (i = 0, j = 0, p = *Data; i < Count; i++, j+=2) {
|
|
*(p++) = (EncodedData[j] & 0x0f) | (EncodedData[j+1] & 0xf0);
|
|
}
|
|
*p = '\0';
|
|
|
|
//
|
|
// Set up the string ourselves since there may be NULLs in
|
|
// the decoded data.
|
|
//
|
|
|
|
String.Buffer = *Data;
|
|
String.Length = (USHORT)Count;
|
|
String.MaximumLength = (USHORT)(Count+1);
|
|
|
|
RtlRunDecodeUnicodeString(ClientState->Seed, (PUNICODE_STRING)&String);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// This routine was stolen from net\svcdlls\logonsrv\server\ssiauth.c.
|
|
//
|
|
|
|
BOOLEAN
|
|
OscGenerateRandomBits(
|
|
PUCHAR Buffer,
|
|
ULONG BufferLen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generates random bits
|
|
|
|
Arguments:
|
|
|
|
pBuffer - Buffer to fill
|
|
|
|
cbBuffer - Number of bytes in buffer
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL Status = TRUE;
|
|
HCRYPTPROV CryptProvider = 0;
|
|
|
|
Status = CryptAcquireContext( &CryptProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT );
|
|
|
|
if ( Status ) {
|
|
|
|
Status = CryptGenRandom( CryptProvider, BufferLen, ( LPBYTE )Buffer );
|
|
|
|
CryptReleaseContext( CryptProvider, 0 );
|
|
|
|
} else {
|
|
|
|
BinlPrintDbg((DEBUG_ERRORS, "CryptAcquireContext failed with %lu\n", GetLastError() ));
|
|
|
|
}
|
|
|
|
return ( Status != 0);
|
|
}
|
|
|
|
VOID
|
|
OscGeneratePassword(
|
|
OUT PWCHAR Password,
|
|
OUT PULONG PasswordLength
|
|
)
|
|
{
|
|
ULONG i;
|
|
DWORD UsableCount = 0;
|
|
DWORD Seed = 0;
|
|
WCHAR GeneratedCharacter;
|
|
BOOLEAN Proceed = FALSE;
|
|
|
|
|
|
//
|
|
// Set return password length and initialize
|
|
// the generated password.
|
|
//
|
|
*PasswordLength = LM20_PWLEN * sizeof(WCHAR);
|
|
memset( Password, 0, *PasswordLength );
|
|
|
|
|
|
//
|
|
// Initialize for random password generation.
|
|
//
|
|
UsableCount = lstrlen(gUsableChars);
|
|
Seed = RANDOM_SEED ^ GetCurrentTime();
|
|
srand( Seed );
|
|
|
|
|
|
//
|
|
// Ensure we have at least one period in the password.
|
|
//
|
|
Password[rand() % LM20_PWLEN] = L'.';
|
|
|
|
|
|
//
|
|
// Ensure there's at least one digit in the password.
|
|
//
|
|
do {
|
|
i = rand() % LM20_PWLEN;
|
|
} while ( Password[i] != 0 );
|
|
Password[i] = (rand() % 10) + 0x30;
|
|
|
|
|
|
//
|
|
// Now fill in the rest of the password.
|
|
//
|
|
for (i = 0; i < LM20_PWLEN; i++) {
|
|
|
|
if ( Password[i] == 0 ) {
|
|
Password[i] = gUsableChars[rand() % UsableCount];
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// GenerateErrorScreen( )
|
|
//
|
|
DWORD
|
|
GenerateErrorScreen(
|
|
PCHAR *OutMessage,
|
|
PULONG OutMessageLength,
|
|
DWORD Error,
|
|
PCLIENT_STATE clientState
|
|
)
|
|
{
|
|
DWORD Err;
|
|
DWORD dwLen;
|
|
PCHAR ErrorMsg;
|
|
DWORD ErrorScreenLength = strlen(ErrorScreenHeaders) + strlen(ErrorScreenBody) + strlen(ErrorScreenTrailer);
|
|
PCHAR pch;
|
|
PCHAR RspMessage = NULL;
|
|
ULONG RspMessageLength = 0;
|
|
|
|
const CHAR UnknownErrorMsg[] = "Unknown Error.";
|
|
|
|
WCHAR ErrorMsgFilename[ MAX_PATH ];
|
|
HANDLE hfile;
|
|
|
|
LPSTR Messages[5];
|
|
|
|
Messages[0] = OscFindVariableA( clientState, "USERNAME" );
|
|
Messages[1] = OscFindVariableA( clientState, "USERDOMAIN" );
|
|
Messages[2] = OscFindVariableA( clientState, "MACHINENAME" );
|
|
Messages[3] = OscFindVariableA( clientState, "SUBERROR" );
|
|
Messages[4] = NULL; // paranoid
|
|
|
|
if ( _snwprintf( ErrorMsgFilename,
|
|
sizeof(ErrorMsgFilename) / sizeof(ErrorMsgFilename[0]),
|
|
L"%ws\\OSChooser\\%ws\\%08x.OSC",
|
|
IntelliMirrorPathW,
|
|
OscFindVariableW( clientState, "LANGUAGE" ),
|
|
Error ) > 0 ) {
|
|
|
|
ErrorMsgFilename[MAX_PATH-1] = L'\0'; // throw in terminating null just to be safe
|
|
//
|
|
// If we find the file, load it into memory.
|
|
//
|
|
hfile = CreateFile( ErrorMsgFilename, GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
if ( hfile != INVALID_HANDLE_VALUE ) {
|
|
DWORD FileSize;
|
|
//
|
|
// Find out how big this screen is, if bigger than 0xFFFFFFFF we won't
|
|
// display it.
|
|
//
|
|
FileSize = GetFileSize( hfile, NULL );
|
|
if ( FileSize != 0xFFFFffff )
|
|
{
|
|
DWORD dwRead = 0;
|
|
|
|
RspMessage = BinlAllocateMemory( FileSize + 3 );
|
|
if ( RspMessage == NULL )
|
|
{
|
|
//
|
|
// Ignore error and fall thru to generate an error screen
|
|
//
|
|
}
|
|
else
|
|
{
|
|
RspMessageLength = 0;
|
|
RspMessage[0] = '\0';
|
|
|
|
while ( dwRead != FileSize )
|
|
{
|
|
BOOL b;
|
|
DWORD dw;
|
|
b = ReadFile( hfile, &RspMessage[dwRead], FileSize - dwRead, &dw, NULL );
|
|
if (!b)
|
|
{
|
|
PWCHAR strings[2];
|
|
strings[0] = ErrorMsgFilename;
|
|
strings[1] = NULL;
|
|
Err = GetLastError( );
|
|
|
|
BinlPrint(( DEBUG_OSC_ERROR, "Error reading screen file: Seek=%u, Size=%u, File=%ws\n",
|
|
dwRead, FileSize - dwRead, ErrorMsgFilename ));
|
|
|
|
BinlReportEventW( EVENT_ERROR_READING_OSC_SCREEN,
|
|
EVENTLOG_ERROR_TYPE,
|
|
1,
|
|
sizeof(Err),
|
|
strings,
|
|
&Err
|
|
);
|
|
break;
|
|
}
|
|
dwRead += dw;
|
|
}
|
|
|
|
RspMessageLength = dwRead;
|
|
RspMessage[dwRead] = '\0'; // paranoid
|
|
|
|
CloseHandle( hfile );
|
|
|
|
Err = ERROR_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BinlPrintDbg((DEBUG_OSC_ERROR, "!!Error 0x%08x - Could not determine file size.\n", GetLastError( )));
|
|
//
|
|
// Ignore error and fall thru to generate an error screen
|
|
//
|
|
}
|
|
|
|
CloseHandle( hfile );
|
|
}
|
|
|
|
}
|
|
|
|
BinlPrintDbg((DEBUG_OSC_ERROR, "no friendly OSC error screen available.\n" ));
|
|
//
|
|
// See if this is a BINL error or a system error and
|
|
// get the text from the error tables.
|
|
//
|
|
dwLen = FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_HMODULE |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
GetModuleHandle(L"BINLSVC.DLL"),
|
|
Error,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
(LPSTR) &ErrorMsg,
|
|
0,
|
|
(va_list*) Messages );
|
|
if ( dwLen == 0 )
|
|
{
|
|
BinlAssert( ErrorMsg == NULL );
|
|
Err = GetLastError( );
|
|
BinlPrintDbg((DEBUG_OSC_ERROR, "!! Error 0x%08x - no BINLSVC specific message available.\n", Err ));
|
|
|
|
dwLen = FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS |
|
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
Error,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
(LPSTR) &ErrorMsg,
|
|
0,
|
|
NULL );
|
|
if ( dwLen == 0 )
|
|
{
|
|
BinlAssert( ErrorMsg == NULL );
|
|
Err = GetLastError( );
|
|
BinlPrintDbg((DEBUG_OSC_ERROR, "!! Error 0x%08x - no SYSTEM specific message available.\n", Err ));
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
if ( ErrorMsg )
|
|
DebugMemoryAdd( ErrorMsg, __FILE__, __LINE__, "BINL", LPTR, strlen(ErrorMsg), "ErrorMsg" );
|
|
#endif
|
|
|
|
//
|
|
// If all else fails, just print an error code.
|
|
//
|
|
if ( ErrorMsg == NULL ) {
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "sending using generic error message.\n" ));
|
|
ErrorMsg = (PCHAR) UnknownErrorMsg;
|
|
dwLen = strlen(ErrorMsg);
|
|
}
|
|
|
|
#define ERRORITEM "%s%08x%s<BR><BOLD>%s</BOLD><BR><BR>%s"
|
|
|
|
//
|
|
// The + 13 is the "0x12345678 - " of the generated error message.
|
|
//
|
|
RspMessageLength = ErrorScreenLength + strlen(ERRORITEM) + dwLen + 1 + 13;
|
|
RspMessage = (PCHAR) BinlAllocateMemory( RspMessageLength );
|
|
if ( RspMessage == NULL )
|
|
{
|
|
Err = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
wsprintfA( RspMessage, ERRORITEM, ErrorScreenHeaders, Error, ErrorScreenBody, ErrorMsg, ErrorScreenTrailer );
|
|
|
|
Err = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
if ( Err == ERROR_SUCCESS )
|
|
{
|
|
// BinlPrint(( DEBUG_OSC, "Generated Error Response:\n%s\n", RspMessage ));
|
|
*OutMessage = RspMessage;
|
|
*OutMessageLength = RspMessageLength;
|
|
|
|
BinlReportEventA( EVENT_ERROR_SERVER_SIDE_ERROR,
|
|
EVENTLOG_ERROR_TYPE,
|
|
4,
|
|
sizeof(Error),
|
|
Messages,
|
|
&Error
|
|
);
|
|
}
|
|
else
|
|
{
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "!! Error 0x%08x - Couldn't generate error screen.\n", Err ));
|
|
|
|
BinlReportEventA( EVENT_ERROR_GENERATING_SERVER_SIDE_ERROR,
|
|
EVENTLOG_ERROR_TYPE,
|
|
4,
|
|
sizeof(Err),
|
|
Messages,
|
|
&Err
|
|
);
|
|
|
|
*OutMessage = NULL;
|
|
*OutMessageLength = 0;
|
|
|
|
if ( RspMessage )
|
|
{
|
|
BinlFreeMemory( RspMessage );
|
|
}
|
|
}
|
|
|
|
return Err;
|
|
}
|
|
|
|
//
|
|
// Returns a pointer point to the next 'ch' or NULL character.
|
|
//
|
|
PCHAR
|
|
FindNext(
|
|
PCHAR Start,
|
|
CHAR ch,
|
|
PCHAR End
|
|
)
|
|
{
|
|
TraceFunc("FindNext( )\n");
|
|
|
|
while( Start != End && *Start && *Start !=ch ) {
|
|
Start++;
|
|
}
|
|
|
|
if ( Start != End && *Start ) {
|
|
return Start;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finds the screen name.
|
|
//
|
|
PCHAR
|
|
FindScreenName(
|
|
PCHAR Screen
|
|
)
|
|
{
|
|
PCHAR Name;
|
|
TraceFunc("FindScreenName( )\n");
|
|
|
|
Name = strstr( Screen, "NAME" );
|
|
|
|
if ( Name == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
Name += 5; // "Name" plus space
|
|
|
|
return Name;
|
|
}
|
|
|
|
DWORD
|
|
OscImpersonate(
|
|
IN PCLIENT_STATE ClientState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Makes the current thread impersonate the client. It is assumed
|
|
that the client has already sent up a login screen. If this call
|
|
succeeds, ClientState->AuthenticatedDCLdapHandle is valid.
|
|
|
|
Arguments:
|
|
|
|
ClientState - The client state.
|
|
|
|
Return Value:
|
|
|
|
Windows Error.
|
|
|
|
--*/
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
LPSTR pUserName;
|
|
LPSTR pUserDomain;
|
|
LPSTR pUserPassword;
|
|
LPSTR pDecodedPassword = NULL;
|
|
LPSTR tempptr;
|
|
ULONG temp;
|
|
ULONG LdapError = 0;
|
|
SEC_WINNT_AUTH_IDENTITY_A authIdentity;
|
|
BOOL bResult;
|
|
BOOL Impersonating = FALSE;
|
|
LPWSTR pCrossDsDc;
|
|
|
|
TraceFunc( "OscImpersonate( ... )\n" );
|
|
|
|
pCrossDsDc = OscFindVariableW( ClientState, "DCNAME" );
|
|
if (*pCrossDsDc == L'\0') {
|
|
|
|
//
|
|
// Clean up any old client state.
|
|
//
|
|
|
|
if (ClientState->AuthenticatedDCLdapHandle &&
|
|
ClientState->UserToken) {
|
|
|
|
bResult = ImpersonateLoggedOnUser(ClientState->UserToken);
|
|
if (bResult) {
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (ClientState->AuthenticatedDCLdapHandle) {
|
|
// Reconnecting again. Use new credentials.
|
|
ldap_unbind(ClientState->AuthenticatedDCLdapHandle);
|
|
ClientState->AuthenticatedDCLdapHandle = NULL;
|
|
}
|
|
if (ClientState->UserToken) {
|
|
CloseHandle(ClientState->UserToken);
|
|
ClientState->UserToken = NULL;
|
|
}
|
|
|
|
//
|
|
// Get the login variables from the client state.
|
|
//
|
|
|
|
pUserName = OscFindVariableA( ClientState, "USERNAME" );
|
|
pUserDomain = OscFindVariableA( ClientState, "USERDOMAIN" );
|
|
pUserPassword = OscFindVariableA( ClientState, "*PASSWORD" );
|
|
|
|
if (pUserName[0] == '\0') {
|
|
OscAddVariableA( ClientState, "SUBERROR", "USERNAME" );
|
|
Error = ERROR_BINL_MISSING_VARIABLE;
|
|
goto ImpersonateFailed;
|
|
}
|
|
|
|
//
|
|
// Decode the password.
|
|
//
|
|
|
|
Error = OscRunDecode(ClientState, pUserPassword, &pDecodedPassword);
|
|
if (Error != ERROR_SUCCESS) {
|
|
goto ImpersonateFailed;
|
|
}
|
|
|
|
|
|
//
|
|
// if the user didn't enter a domain name, use the server's
|
|
//
|
|
|
|
if (pUserDomain == NULL || pUserDomain[0] == '\0') {
|
|
|
|
OscAddVariableW( ClientState, "USERDOMAIN", BinlGlobalOurDomainName );
|
|
|
|
pUserDomain = OscFindVariableA( ClientState, "USERDOMAIN" );
|
|
|
|
}
|
|
|
|
//
|
|
// Do a LogonUser with the credentials, since we
|
|
// need that to change the machine password (even the
|
|
// authenticated LDAP handle won't do that if we don't
|
|
// have 128-bit SSL setup on this machine).
|
|
//
|
|
|
|
bResult = LogonUserA(
|
|
pUserName,
|
|
pUserDomain,
|
|
pDecodedPassword,
|
|
LOGON32_LOGON_NETWORK_CLEARTEXT,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&ClientState->UserToken);
|
|
|
|
if (!bResult) {
|
|
Error = GetLastError();
|
|
BinlPrintDbg(( DEBUG_ERRORS, "LogonUser failed %lx\n", Error));
|
|
ClientState->UserToken = NULL; // this may be set even on failure
|
|
goto ImpersonateFailed;
|
|
}
|
|
|
|
//
|
|
// if the user didn't enter a domain name, grab it out of the user token.
|
|
//
|
|
|
|
if (pUserDomain == NULL || pUserDomain[0] == '\0') {
|
|
|
|
PTOKEN_USER userToken;
|
|
DWORD tokenSize = 4096;
|
|
|
|
userToken = (PTOKEN_USER) BinlAllocateMemory( tokenSize );
|
|
|
|
if (userToken != NULL) {
|
|
|
|
DWORD returnLength;
|
|
BOOL bRC;
|
|
|
|
bRC = GetTokenInformation( ClientState->UserToken,
|
|
TokenUser,
|
|
(LPVOID) userToken,
|
|
tokenSize,
|
|
&returnLength
|
|
);
|
|
|
|
if (bRC) {
|
|
|
|
WCHAR uUser[128];
|
|
DWORD cUser = 128;
|
|
WCHAR uDomain[128];
|
|
DWORD cDomain = 128;
|
|
SID_NAME_USE peType;
|
|
|
|
uDomain[0] = L'\0';
|
|
uUser[0] = L'\0';
|
|
|
|
bRC = LookupAccountSidW( NULL, // system name
|
|
userToken->User.Sid,
|
|
uUser, // user name
|
|
&cUser, // user name count
|
|
uDomain, // domain name
|
|
&cDomain, // domain name count
|
|
&peType
|
|
);
|
|
|
|
if (bRC && uDomain[0] != L'\0') {
|
|
|
|
OscAddVariableW( ClientState, "USERDOMAIN", &uDomain[0] );
|
|
}
|
|
}
|
|
|
|
BinlFreeMemory( userToken );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now impersonate the user.
|
|
//
|
|
|
|
bResult = ImpersonateLoggedOnUser(ClientState->UserToken);
|
|
if (!bResult) {
|
|
BinlPrintDbg(( DEBUG_ERRORS,
|
|
"ImpersonateLoggedOnUser failed %x\n", GetLastError()));
|
|
Error = GetLastError();
|
|
goto ImpersonateFailed;
|
|
}
|
|
|
|
Impersonating = TRUE;
|
|
|
|
//
|
|
// Create authenticated DC connection for use in machine object creation
|
|
// or modification.
|
|
//
|
|
BinlPrintDbg(( DEBUG_OSC,
|
|
"ldap_init %S or %S\n", pCrossDsDc, BinlGlobalDefaultDS ));
|
|
|
|
ClientState->AuthenticatedDCLdapHandle = ldap_init(
|
|
(*pCrossDsDc != L'\0')
|
|
? pCrossDsDc
|
|
: BinlGlobalDefaultDS,
|
|
LDAP_PORT);
|
|
|
|
BinlPrintDbg(( DEBUG_OSC,
|
|
"ldap_init handle %x\n", ClientState->AuthenticatedDCLdapHandle ));
|
|
|
|
|
|
temp = DS_DIRECTORY_SERVICE_REQUIRED | DS_IP_REQUIRED;
|
|
Error = ldap_set_option(ClientState->AuthenticatedDCLdapHandle, LDAP_OPT_GETDSNAME_FLAGS, &temp );
|
|
if (Error != ERROR_SUCCESS) {
|
|
BinlPrintDbg(( DEBUG_ERRORS, "OscImpersonate: Error %x when setting ldap options\n" ));
|
|
goto ImpersonateFailed;
|
|
}
|
|
|
|
temp = LDAP_VERSION3;
|
|
Error = ldap_set_option(ClientState->AuthenticatedDCLdapHandle, LDAP_OPT_VERSION, &temp );
|
|
if (Error != ERROR_SUCCESS) {
|
|
BinlPrintDbg(( DEBUG_ERRORS, "OscImpersonate: Error %x when setting ldap options\n" ));
|
|
goto ImpersonateFailed;
|
|
}
|
|
|
|
//
|
|
// Tell LDAP to keep connections referenced after searches.
|
|
//
|
|
|
|
temp = (ULONG)((ULONG_PTR)LDAP_OPT_ON);
|
|
Error = ldap_set_option(ClientState->AuthenticatedDCLdapHandle, LDAP_OPT_REF_DEREF_CONN_PER_MSG, &temp);
|
|
if (Error != ERROR_SUCCESS) {
|
|
BinlPrintDbg(( DEBUG_ERRORS, "OscImpersonate: Error %x when setting ldap options\n" ));
|
|
goto ImpersonateFailed;
|
|
}
|
|
|
|
LdapError = ldap_connect(ClientState->AuthenticatedDCLdapHandle,0);
|
|
|
|
if (LdapError != LDAP_SUCCESS) {
|
|
BinlPrintDbg(( DEBUG_ERRORS,
|
|
"this ldap_connect() failed %x\n", LdapError));
|
|
goto ImpersonateFailed;
|
|
}
|
|
|
|
//
|
|
// LDAP_AUTH_NEGOTIATE tells it to use the credentials of the user
|
|
// we are impersonating.
|
|
//
|
|
|
|
LdapError = ldap_bind_sA(ClientState->AuthenticatedDCLdapHandle,
|
|
NULL,
|
|
NULL,
|
|
LDAP_AUTH_NEGOTIATE);
|
|
|
|
if (LdapError != LDAP_SUCCESS) {
|
|
BinlPrintDbg(( DEBUG_ERRORS,
|
|
"ldap_bind_s() failed %x\n", LdapError));
|
|
goto ImpersonateFailed;
|
|
}
|
|
|
|
ImpersonateFailed:
|
|
|
|
//
|
|
// If we decoded the password, then erase and free it.
|
|
//
|
|
|
|
if (pDecodedPassword != NULL) {
|
|
RtlSecureZeroMemory(pDecodedPassword, strlen(pDecodedPassword));
|
|
BinlFreeMemory(pDecodedPassword);
|
|
}
|
|
|
|
if (LdapError != LDAP_SUCCESS) {
|
|
Error = LdapMapErrorToWin32(LdapError);
|
|
}
|
|
|
|
if (Error) {
|
|
PWCHAR strings[3];
|
|
strings[0] = OscFindVariableW( ClientState, "USERNAME" );
|
|
strings[1] = OscFindVariableW( ClientState, "USERDOMAIN" );
|
|
strings[2] = NULL;
|
|
|
|
BinlReportEventW( ERROR_BINL_ERR_USER_LOGIN_FAILED,
|
|
EVENTLOG_WARNING_TYPE,
|
|
2,
|
|
sizeof(ULONG),
|
|
strings,
|
|
&Error
|
|
);
|
|
|
|
if (ClientState->AuthenticatedDCLdapHandle) {
|
|
ldap_unbind(ClientState->AuthenticatedDCLdapHandle);
|
|
ClientState->AuthenticatedDCLdapHandle = NULL;
|
|
}
|
|
if (ClientState->UserToken) {
|
|
CloseHandle(ClientState->UserToken);
|
|
ClientState->UserToken = NULL;
|
|
}
|
|
if (Impersonating) {
|
|
RevertToSelf();
|
|
}
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
OscRevert(
|
|
IN PCLIENT_STATE ClientState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stops the current thread impersonating.
|
|
|
|
Arguments:
|
|
|
|
ClientState - The client state.
|
|
|
|
Return Value:
|
|
|
|
Windows Error.
|
|
|
|
--*/
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
BOOL bResult;
|
|
|
|
TraceFunc( "OscRevert( ... )\n" );
|
|
|
|
//
|
|
// We are done impersonating for the moment.
|
|
//
|
|
|
|
bResult = RevertToSelf();
|
|
if (!bResult) {
|
|
BinlPrintDbg(( DEBUG_ERRORS,
|
|
"RevertToSelf failed %x\n", GetLastError()));
|
|
Error = GetLastError();
|
|
}
|
|
|
|
// keep the ldap handle around in case we need it again.
|
|
|
|
// if (ClientState->AuthenticatedDCLdapHandle) {
|
|
// ldap_unbind(ClientState->AuthenticatedDCLdapHandle);
|
|
// ClientState->AuthenticatedDCLdapHandle = NULL;
|
|
// }
|
|
// if (ClientState->UserToken) {
|
|
// CloseHandle(ClientState->UserToken);
|
|
// ClientState->UserToken = NULL;
|
|
// }
|
|
|
|
return Error;
|
|
|
|
}
|
|
|
|
//
|
|
// OscGuidToBytes( )
|
|
//
|
|
// Change CHAR Guid to bytes
|
|
//
|
|
DWORD
|
|
OscGuidToBytes(
|
|
LPSTR pszGuid,
|
|
LPBYTE Guid )
|
|
{
|
|
PCHAR psz;
|
|
ULONG len;
|
|
ULONG i;
|
|
|
|
TraceFunc( "OscGuidToBytes( ... )\n" );
|
|
|
|
len = strlen(pszGuid);
|
|
BinlAssert( len == 32 );
|
|
if ( len != 32 ) {
|
|
return ERROR_BINL_INVALID_GUID;
|
|
}
|
|
|
|
psz = pszGuid;
|
|
i = 0;
|
|
while ( i * 2 < 32 )
|
|
{
|
|
//
|
|
// Upper 4-bits
|
|
//
|
|
CHAR c = *psz;
|
|
psz++;
|
|
Guid[i] = ( c > 59 ? (toupper(c) - 55) << 4 : (c - 48) << 4);
|
|
|
|
//
|
|
// Lower 4-bits
|
|
//
|
|
c = *psz;
|
|
psz++;
|
|
Guid[i] += ( c > 59 ? (toupper(c) - 55) : (c - 48) );
|
|
|
|
//
|
|
// Next byte
|
|
//
|
|
i++;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
OscSifIsSysPrep(
|
|
LPWSTR pSysPrepSifPath
|
|
)
|
|
{
|
|
DWORD dwErr;
|
|
WCHAR Buffer[256];
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
TraceFunc("OscSifIsSysPrep( )\n");
|
|
|
|
Buffer[0] = UNICODE_NULL;
|
|
GetPrivateProfileString(OSCHOOSER_SIF_SECTIONW,
|
|
L"ImageType",
|
|
Buffer, // default
|
|
Buffer,
|
|
256,
|
|
pSysPrepSifPath
|
|
);
|
|
|
|
RtlInitUnicodeString(&UnicodeString, Buffer);
|
|
RtlUpcaseUnicodeString(&UnicodeString, &UnicodeString, FALSE);
|
|
|
|
if (_wcsicmp(L"SYSPREP", Buffer)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
OscSifIsCmdConsA(
|
|
PCHAR pSifPath
|
|
)
|
|
{
|
|
DWORD dwErr;
|
|
CHAR Buffer[256];
|
|
|
|
TraceFunc("OscSifIsCmdCons( )\n");
|
|
|
|
Buffer[0] = '\0';
|
|
GetPrivateProfileStringA(OSCHOOSER_SIF_SECTIONA,
|
|
"ImageType",
|
|
Buffer, // default
|
|
Buffer,
|
|
256,
|
|
pSifPath
|
|
);
|
|
|
|
if (_stricmp("CMDCONS", Buffer)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
OscSifIsASR(
|
|
PCHAR pSifPath
|
|
)
|
|
{
|
|
DWORD dwErr;
|
|
CHAR Buffer[256];
|
|
|
|
TraceFunc("OscSifIsASR( )\n");
|
|
|
|
Buffer[0] = '\0';
|
|
GetPrivateProfileStringA(OSCHOOSER_SIF_SECTIONA,
|
|
"ImageType",
|
|
Buffer, // default
|
|
Buffer,
|
|
256,
|
|
pSifPath
|
|
);
|
|
|
|
if (_stricmp("ASR", Buffer)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
OscSifIsWinPE(
|
|
PCHAR pSifPath
|
|
)
|
|
{
|
|
DWORD dwErr;
|
|
CHAR Buffer[256];
|
|
|
|
TraceFunc("OscSifIsWinPE( )\n");
|
|
|
|
Buffer[0] = '\0';
|
|
GetPrivateProfileStringA(OSCHOOSER_SIF_SECTIONA,
|
|
"ImageType",
|
|
Buffer, // default
|
|
Buffer,
|
|
256,
|
|
pSifPath
|
|
);
|
|
|
|
if (_stricmp("WinPE", Buffer)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
OscSifIsWinPEW(
|
|
PWCHAR pSifPath
|
|
)
|
|
{
|
|
DWORD dwErr;
|
|
WCHAR Buffer[256];
|
|
|
|
TraceFunc("OscSifIsWinPE( )\n");
|
|
|
|
Buffer[0] = UNICODE_NULL;
|
|
GetPrivateProfileString(OSCHOOSER_SIF_SECTIONW,
|
|
L"ImageType",
|
|
Buffer, // default
|
|
Buffer,
|
|
256,
|
|
pSifPath
|
|
);
|
|
|
|
if (_wcsicmp(L"WinPE", Buffer)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
OscGetSkuType(
|
|
PWSTR PathToTxtSetupSif
|
|
)
|
|
{
|
|
PWSTR SifFile;
|
|
DWORD SkuType = 0;
|
|
|
|
SifFile = BinlAllocateMemory(
|
|
(wcslen(PathToTxtSetupSif) +
|
|
sizeof(L"\\txtsetup.sif")/sizeof(WCHAR) ) * sizeof(WCHAR));
|
|
|
|
if (!SifFile) {
|
|
return 0; //default to professional on failure
|
|
}
|
|
|
|
wcscpy(SifFile, PathToTxtSetupSif);
|
|
if (SifFile[wcslen(SifFile)-1] == L'\\') {
|
|
wcscat( SifFile, L"txtsetup.sif" );
|
|
} else {
|
|
wcscat( SifFile, L"\\txtsetup.sif" );
|
|
}
|
|
|
|
SkuType = GetPrivateProfileInt(
|
|
L"SetupData",
|
|
L"ProductType",
|
|
0,
|
|
SifFile );
|
|
|
|
BinlFreeMemory( SifFile );
|
|
|
|
return (SkuType);
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
OscGetClosestNt(
|
|
IN LPWSTR PathToKernel,
|
|
IN DWORD SkuType,
|
|
IN PCLIENT_STATE ClientState,
|
|
OUT LPWSTR SetupPath,
|
|
IN DWORD SetupPathSize,
|
|
OUT PBOOLEAN ExactMatch
|
|
)
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
WIN32_FIND_DATA FindData,TemplateData;
|
|
HANDLE hFind = INVALID_HANDLE_VALUE,hTemplate = INVALID_HANDLE_VALUE;
|
|
BOOLEAN Impersonated = FALSE;
|
|
WCHAR Path[MAX_PATH],TemplatesPath[MAX_PATH];
|
|
PWSTR p;
|
|
ULONGLONG BestVersion = (ULONGLONG)0;
|
|
ULONGLONG ThisVersion;
|
|
ULONGLONG KernelVersion;
|
|
DWORD dwPathLen;
|
|
BOOLEAN ReturnValue = FALSE;
|
|
BOOLEAN FoundWinPE;
|
|
|
|
TraceFunc("OscGetClosestNt( )\n");
|
|
|
|
Error = ImpersonateSecurityContext(&ClientState->ServerContextHandle);
|
|
if (Error != STATUS_SUCCESS) {
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "ImpersonateSecurityContext: 0x%08x\n", Error ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Impersonated = TRUE;
|
|
|
|
//
|
|
// Get the version info of the kernel passed in
|
|
//
|
|
if (!OscGetNtVersionInfo(&KernelVersion, PathToKernel, ClientState)) {
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "OscGetNtVersionInfo failed\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Resulting string should be something like:
|
|
// "D:\RemoteInstall\Setup\English\Images\*"
|
|
if ( _snwprintf( Path,
|
|
sizeof(Path) / sizeof(Path[0]),
|
|
L"%ws\\Setup\\%ws\\%ws\\*",
|
|
IntelliMirrorPathW,
|
|
OscFindVariableW(ClientState, "LANGUAGE"),
|
|
REMOTE_INSTALL_IMAGE_DIR_W
|
|
) < 0 ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
Path[MAX_PATH-1] = L'\0'; // throw in terminating null just to be safe
|
|
hFind = FindFirstFile(Path, (LPVOID) &FindData);
|
|
if (hFind == INVALID_HANDLE_VALUE) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwPathLen = wcslen(Path);
|
|
|
|
//
|
|
// Loop enumerating each subdirectory
|
|
//
|
|
do {
|
|
//
|
|
// Ignore directories "." and ".."
|
|
//
|
|
if (wcscmp(FindData.cFileName, L".") &&
|
|
wcscmp(FindData.cFileName, L"..") &&
|
|
(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
DWORD ThisSkuType;
|
|
DWORD dwFileNameLen;
|
|
DWORD dwTemplatesPathLen;
|
|
|
|
//
|
|
// Add the sub-directory to the path
|
|
//
|
|
dwFileNameLen = wcslen(FindData.cFileName);
|
|
if (dwPathLen + dwFileNameLen + MAX_ARCHITECTURE_LENGTH + 1 > sizeof(Path)/sizeof(Path[0])) {
|
|
continue; // path too long, skip it
|
|
}
|
|
wcscpy(&Path[dwPathLen - 1], FindData.cFileName );
|
|
|
|
BinlPrintDbg(( DEBUG_OSC, "Found OS Directory: %ws\n", Path ));
|
|
|
|
// Resulting string should be something like:
|
|
// "D:\RemoteInstall\Setup\English\Images\nt50.wks\i386"
|
|
p = OscFindVariableW(ClientState, "MACHINETYPE");
|
|
if (!p) {
|
|
continue;
|
|
}
|
|
|
|
if (wcslen(Path) + (sizeof(L"\\")/sizeof(WCHAR)) + wcslen(p) > sizeof(Path)/sizeof(Path[0])) {
|
|
continue;
|
|
}
|
|
|
|
wcscat(Path, L"\\");
|
|
wcscat(Path, p);
|
|
|
|
//
|
|
// get a path to the templates folder and make sure we dont' have
|
|
// a winpe image. if we do then we're in trouble because we can't
|
|
// treat that as a normal image.
|
|
//
|
|
FoundWinPE = FALSE;
|
|
if (0 > _snwprintf(
|
|
TemplatesPath,
|
|
MAX_PATH,
|
|
L"%ws\\templates\\*.sif",
|
|
Path)) {
|
|
continue;
|
|
}
|
|
TemplatesPath[MAX_PATH-1] = L'\0'; // throw in terminating null just to be safe
|
|
|
|
dwTemplatesPathLen = wcslen(TemplatesPath);
|
|
p = wcsrchr(TemplatesPath,L'*');
|
|
BinlAssert( p != NULL );
|
|
|
|
dwTemplatesPathLen = (p - TemplatesPath)/sizeof(WCHAR);
|
|
|
|
|
|
hTemplate = FindFirstFile(TemplatesPath, (LPVOID) &TemplateData);
|
|
if (hTemplate != INVALID_HANDLE_VALUE) {
|
|
do {
|
|
if (wcscmp(TemplateData.cFileName, L".") &&
|
|
wcscmp(TemplateData.cFileName, L"..") &&
|
|
(TemplateData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
|
// make sure there is enough room.
|
|
if (dwTemplatesPathLen + wcslen(TemplateData.cFileName) + 1 > sizeof(TemplatesPath)/sizeof(WCHAR)) {
|
|
continue;
|
|
}
|
|
|
|
wcscpy(&TemplatesPath[dwTemplatesPathLen],TemplateData.cFileName);
|
|
|
|
if (OscSifIsWinPEW(TemplatesPath)) {
|
|
BinlPrintDbg(( DEBUG_OSC, "OS Directory %ws is really a WinPE image, skipping it.\n", Path ));
|
|
FoundWinPE = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
} while (FindNextFile(hTemplate, (LPVOID) &TemplateData));
|
|
|
|
FindClose(hTemplate);
|
|
hTemplate = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (FoundWinPE) {
|
|
continue;
|
|
}
|
|
|
|
|
|
ThisSkuType = OscGetSkuType( Path );
|
|
|
|
if (OscGetNtVersionInfo(&ThisVersion, Path, ClientState)) {
|
|
//
|
|
// if the sku we're looking for is ads and we've found srv,
|
|
// then lie and say it's really
|
|
// ads. This gets around a problem where txtsetup.sif didn't
|
|
// specify the SKU type correctly in 2195.
|
|
//
|
|
if (ThisSkuType == 1 && SkuType == 2) {
|
|
ThisSkuType = 2;
|
|
}
|
|
|
|
if ((ThisVersion >= KernelVersion) &&
|
|
(ThisSkuType == SkuType) &&
|
|
((BestVersion == (ULONGLONG)0) || (ThisVersion < BestVersion))) {
|
|
if (SetupPathSize >= wcslen(Path)) {
|
|
wcscpy(SetupPath, Path);
|
|
BestVersion = ThisVersion;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} while (FindNextFile(hFind, (LPVOID) &FindData));
|
|
|
|
if (BestVersion != 0) {
|
|
ReturnValue = TRUE;
|
|
*ExactMatch = (BOOLEAN)(BestVersion == KernelVersion);
|
|
} else {
|
|
ReturnValue = FALSE;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (hFind != INVALID_HANDLE_VALUE) {
|
|
FindClose(hFind);
|
|
}
|
|
|
|
if (Impersonated) {
|
|
Error = RevertSecurityContext(&ClientState->ServerContextHandle);
|
|
if (Error != STATUS_SUCCESS) {
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "RevertSecurityContext: 0x%08x\n", Error ));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
BOOLEAN
|
|
OscGetNtVersionInfo(
|
|
PULONGLONG Version,
|
|
PWCHAR SearchDir,
|
|
PCLIENT_STATE ClientState
|
|
)
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
DWORD FileVersionInfoSize;
|
|
DWORD VersionHandle;
|
|
ULARGE_INTEGER TmpVersion;
|
|
PVOID VersionInfo;
|
|
VS_FIXEDFILEINFO * FixedFileInfo;
|
|
UINT FixedFileInfoLength;
|
|
WCHAR Path[MAX_PATH];
|
|
BOOLEAN fResult = FALSE;
|
|
|
|
TraceFunc("OscGetNtVersionInfo( )\n");
|
|
|
|
if (!SearchDir) {
|
|
goto e0;
|
|
}
|
|
|
|
//
|
|
// Resulting string should be something like:
|
|
// "D:\RemoteInstall\Setup\English\Images\nt50.wks\i386\ntoskrnl.exe"
|
|
//
|
|
if (0 > _snwprintf(
|
|
Path,
|
|
MAX_PATH,
|
|
L"%ws\\ntoskrnl.exe",
|
|
SearchDir)) {
|
|
goto e0; // path too long, skip it
|
|
}
|
|
Path[MAX_PATH-1] = L'\0'; // throw in terminating null just to be safe
|
|
|
|
BinlPrintDbg((DEBUG_OSC, "Checking version: %ws\n", Path));
|
|
|
|
FileVersionInfoSize = GetFileVersionInfoSize(Path, &VersionHandle);
|
|
if (FileVersionInfoSize == 0) {
|
|
goto e0;
|
|
}
|
|
|
|
VersionInfo = BinlAllocateMemory(FileVersionInfoSize);
|
|
if (VersionInfo == NULL) {
|
|
goto e0;
|
|
}
|
|
|
|
if (!GetFileVersionInfo(
|
|
Path,
|
|
VersionHandle,
|
|
FileVersionInfoSize,
|
|
VersionInfo)) {
|
|
goto e1;
|
|
}
|
|
|
|
if (!VerQueryValue(
|
|
VersionInfo,
|
|
L"\\",
|
|
&FixedFileInfo,
|
|
&FixedFileInfoLength)) {
|
|
goto e1;
|
|
}
|
|
|
|
TmpVersion.HighPart = FixedFileInfo->dwFileVersionMS;
|
|
TmpVersion.LowPart = FixedFileInfo->dwFileVersionLS;
|
|
|
|
//
|
|
// We need to whack the low 16 bits of the .LowPart so that
|
|
// we ignore the service pack value. For example, WindowsXP has a
|
|
// version number of 5.1.2600.0. XP-ServicePack1 has a version
|
|
// number of 5.1.2600.1038. We'd like those to match, so just whack
|
|
// the servicepack number portion.
|
|
//
|
|
TmpVersion.LowPart &= 0xFFFF0000;
|
|
|
|
|
|
*Version = TmpVersion.QuadPart;
|
|
|
|
fResult = TRUE;
|
|
|
|
e1:
|
|
BinlFreeMemory(VersionInfo);
|
|
e0:
|
|
return fResult;
|
|
}
|
|
|
|
//
|
|
// Send a message on our socket. If the message is too long, then it
|
|
// splits it into fragments of MAXIMUM_FRAGMENT_LENGTH bytes.
|
|
//
|
|
|
|
#define MAXIMUM_FRAGMENT_LENGTH 1400
|
|
|
|
DWORD
|
|
SendUdpMessage(
|
|
LPBINL_REQUEST_CONTEXT RequestContext,
|
|
PCLIENT_STATE clientState,
|
|
BOOL bFragment,
|
|
BOOL bResend
|
|
)
|
|
{
|
|
DWORD error;
|
|
FRAGMENT_PACKET FragmentHeader;
|
|
USHORT FragmentNumber;
|
|
USHORT FragmentTotal;
|
|
ULONG MessageLengthWithoutHeader;
|
|
ULONG BytesSent;
|
|
ULONG BytesThisSend;
|
|
UCHAR TempMessage[1500];
|
|
FRAGMENT_PACKET UNALIGNED * SendFragmentPacket =
|
|
(FRAGMENT_PACKET UNALIGNED *)TempMessage;
|
|
|
|
TraceFunc("SendUdpMessage( )\n");
|
|
|
|
//
|
|
// The message starts with a signature, a length, a sequence number (all
|
|
// four bytes), then two ushorts for fragment count and total. If
|
|
// we have to split it we preserve this header in each packet, with
|
|
// fragment count modified for each one.
|
|
//
|
|
|
|
MessageLengthWithoutHeader =
|
|
clientState->LastResponseLength - FRAGMENT_PACKET_DATA_OFFSET;
|
|
|
|
if (!bFragment ||
|
|
((FragmentTotal = (USHORT)((MessageLengthWithoutHeader + MAXIMUM_FRAGMENT_LENGTH - 1) / MAXIMUM_FRAGMENT_LENGTH)) <= 1))
|
|
{
|
|
#ifdef _TRACE_FUNC_
|
|
SendFragmentPacket = (FRAGMENT_PACKET UNALIGNED *)clientState->LastResponse;
|
|
TraceFunc("Sending packet with ");
|
|
BinlPrintDbg(( DEBUG_OSC, " SequenceNumber = %u )\n", SendFragmentPacket->SequenceNumber ));
|
|
#endif
|
|
|
|
error = sendto(
|
|
RequestContext->ActiveEndpoint->Socket,
|
|
clientState->LastResponse,
|
|
clientState->LastResponseLength,
|
|
0,
|
|
&RequestContext->SourceName,
|
|
RequestContext->SourceNameLength
|
|
);
|
|
|
|
} else {
|
|
|
|
FragmentHeader = *((FRAGMENT_PACKET UNALIGNED *)clientState->LastResponse); // struct copy -- save the header
|
|
BytesSent = 0;
|
|
|
|
for (FragmentNumber = 0; FragmentNumber < FragmentTotal; FragmentNumber++) {
|
|
|
|
if (FragmentNumber == (FragmentTotal - 1)) {
|
|
BytesThisSend = MessageLengthWithoutHeader - BytesSent;
|
|
} else {
|
|
BytesThisSend = MAXIMUM_FRAGMENT_LENGTH;
|
|
}
|
|
|
|
memcpy(
|
|
TempMessage + FRAGMENT_PACKET_DATA_OFFSET,
|
|
clientState->LastResponse + FRAGMENT_PACKET_DATA_OFFSET + (FragmentNumber * MAXIMUM_FRAGMENT_LENGTH),
|
|
BytesThisSend);
|
|
|
|
memcpy(SendFragmentPacket, &FragmentHeader, FRAGMENT_PACKET_DATA_OFFSET);
|
|
SendFragmentPacket->Length = BytesThisSend + FRAGMENT_PACKET_EMPTY_LENGTH;
|
|
SendFragmentPacket->FragmentNumber = FragmentNumber + 1;
|
|
SendFragmentPacket->FragmentTotal = FragmentTotal;
|
|
|
|
#ifdef TEST_FAILURE
|
|
if (FailFirstFragment) {
|
|
FailFirstFragment = FALSE;
|
|
BinlPrintDbg((DEBUG_OSC, "NOT sending first fragment, %ld bytes\n", BytesThisSend + FRAGMENT_PACKET_DATA_OFFSET));
|
|
error = ERROR_SUCCESS;
|
|
} else
|
|
#endif
|
|
|
|
//
|
|
// On resends, wait between fragments in case the resend is
|
|
// because the card can't handle quick bursts of packets.
|
|
//
|
|
if (bResend && (FragmentNumber != 0)) {
|
|
Sleep(10); // wait 10 milliseconds
|
|
}
|
|
|
|
error = sendto(
|
|
RequestContext->ActiveEndpoint->Socket,
|
|
TempMessage,
|
|
BytesThisSend + FRAGMENT_PACKET_DATA_OFFSET,
|
|
0,
|
|
&RequestContext->SourceName,
|
|
RequestContext->SourceNameLength
|
|
);
|
|
|
|
if (error == SOCKET_ERROR) {
|
|
break;
|
|
}
|
|
|
|
BytesSent += BytesThisSend;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( error == SOCKET_ERROR ) {
|
|
error = WSAGetLastError();
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "Sendto() failed, error = %ld\n", error ));
|
|
} else {
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
|
|
return( error );
|
|
}
|
|
|
|
//
|
|
// Verifies the packets signature is authentic
|
|
//
|
|
DWORD
|
|
OscVerifySignature(
|
|
PCLIENT_STATE clientState,
|
|
SIGNED_PACKET UNALIGNED * signedMessage
|
|
)
|
|
{
|
|
SECURITY_STATUS SecStatus;
|
|
SecBuffer SigBuffers[2];
|
|
ULONG MessageLength, SignLength;
|
|
SecBufferDesc SignMessage;
|
|
|
|
TraceFunc("OscVerifySignature( )\n");
|
|
|
|
MessageLength = signedMessage->Length;
|
|
SignLength = signedMessage->SignLength;
|
|
|
|
//
|
|
// Verify the signature
|
|
//
|
|
SigBuffers[0].pvBuffer = signedMessage->Data;
|
|
SigBuffers[0].cbBuffer = MessageLength - SIGNED_PACKET_EMPTY_LENGTH;
|
|
SigBuffers[0].BufferType = SECBUFFER_DATA;
|
|
|
|
SigBuffers[1].pvBuffer = signedMessage->Sign;
|
|
SigBuffers[1].cbBuffer = SignLength;
|
|
SigBuffers[1].BufferType = SECBUFFER_TOKEN;
|
|
|
|
SignMessage.pBuffers = SigBuffers;
|
|
SignMessage.cBuffers = 2;
|
|
SignMessage.ulVersion = 0;
|
|
|
|
#ifndef ONLY_SIGN_MESSAGES
|
|
SecStatus = UnsealMessage(
|
|
&clientState->ServerContextHandle,
|
|
&SignMessage,
|
|
0,
|
|
0 );
|
|
#else
|
|
SecStatus = VerifySignature(
|
|
&clientState->ServerContextHandle,
|
|
&SignMessage,
|
|
0,
|
|
0 );
|
|
#endif
|
|
|
|
if (SecStatus != STATUS_SUCCESS)
|
|
{
|
|
DWORD Error;
|
|
SIGNED_PACKET UNALIGNED * SendSignedMessage;
|
|
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "Sending ERR packet from Verify/Unseal!!\n"));
|
|
|
|
clientState->LastResponseLength = SIGNED_PACKET_ERROR_LENGTH;
|
|
Error = OscVerifyLastResponseSize(clientState);
|
|
if (Error != ERROR_SUCCESS) {
|
|
return SecStatus; // we can't send anything back
|
|
}
|
|
|
|
SendSignedMessage = (SIGNED_PACKET UNALIGNED *)(clientState->LastResponse);
|
|
|
|
memcpy(SendSignedMessage->Signature, ErrorSignedSignature, 4);
|
|
SendSignedMessage->Length = 4;
|
|
SendSignedMessage->SequenceNumber = clientState->LastSequenceNumber;
|
|
}
|
|
|
|
return SecStatus;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
DWORD
|
|
OscSendSignedMessage(
|
|
LPBINL_REQUEST_CONTEXT RequestContext,
|
|
PCLIENT_STATE clientState,
|
|
PCHAR Message,
|
|
ULONG MessageLength
|
|
)
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
SIGNED_PACKET UNALIGNED * SendSignedMessage;
|
|
SecBuffer SigBuffers[2];
|
|
SecBufferDesc SignMessage;
|
|
SECURITY_STATUS SecStatus;
|
|
|
|
#ifdef _TRACE_FUNC_
|
|
TraceFunc("OscSendSignedMessage( ");
|
|
BinlPrintDbg(( DEBUG_OSC, "SequenceNumber = %u )\n", clientState->LastSequenceNumber ));
|
|
#endif
|
|
|
|
//
|
|
// Make sure we have space for the message
|
|
//
|
|
clientState->LastResponseLength = MessageLength + SIGNED_PACKET_DATA_OFFSET;
|
|
Error = OscVerifyLastResponseSize(clientState);
|
|
if (Error != ERROR_SUCCESS)
|
|
return Error;
|
|
|
|
//
|
|
// copy the message
|
|
//
|
|
SendSignedMessage = (SIGNED_PACKET UNALIGNED *) clientState->LastResponse;
|
|
memcpy(SendSignedMessage->Data, Message, MessageLength);
|
|
|
|
//
|
|
// sign the message
|
|
//
|
|
memcpy(SendSignedMessage->Signature, ResponseSignedSignature, 4);
|
|
SendSignedMessage->Length = MessageLength + SIGNED_PACKET_EMPTY_LENGTH;
|
|
SendSignedMessage->SequenceNumber = clientState->LastSequenceNumber;
|
|
SendSignedMessage->FragmentNumber = 1; // fragment count
|
|
SendSignedMessage->FragmentTotal = 1; // fragment total
|
|
|
|
SendSignedMessage->SignLength = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
|
|
|
|
#if 0
|
|
//
|
|
// Send out an unsealed copy to a different port.
|
|
//
|
|
|
|
{
|
|
USHORT TmpPort;
|
|
PCHAR TmpSignature[4];
|
|
|
|
TmpPort = ((struct sockaddr_in *)&RequestContext->SourceName)->sin_port;
|
|
memcpy(TmpSignature, SendSignedMessage->Signature, 4);
|
|
|
|
((struct sockaddr_in *)&RequestContext->SourceName)->sin_port = 0xabcd;
|
|
memcpy(SendSignedMessage->Signature, "FAKE", 4);
|
|
|
|
Error = SendUdpMessage(RequestContext, clientState, TRUE, FALSE);
|
|
|
|
((struct sockaddr_in *)&RequestContext->SourceName)->sin_port = TmpPort;
|
|
memcpy(SendSignedMessage->Signature, TmpSignature, 4);
|
|
}
|
|
#endif
|
|
|
|
SigBuffers[0].pvBuffer = SendSignedMessage->Data;
|
|
SigBuffers[0].cbBuffer = MessageLength;
|
|
SigBuffers[0].BufferType = SECBUFFER_DATA;
|
|
|
|
SigBuffers[1].pvBuffer = SendSignedMessage->Sign;
|
|
SigBuffers[1].cbBuffer = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
|
|
SigBuffers[1].BufferType = SECBUFFER_TOKEN;
|
|
|
|
SignMessage.pBuffers = SigBuffers;
|
|
SignMessage.cBuffers = 2;
|
|
SignMessage.ulVersion = 0;
|
|
|
|
#ifndef ONLY_SIGN_MESSAGES
|
|
SecStatus = SealMessage(
|
|
&clientState->ServerContextHandle,
|
|
0,
|
|
&SignMessage,
|
|
0 );
|
|
#else
|
|
SecStatus = MakeSignature(
|
|
&clientState->ServerContextHandle,
|
|
0,
|
|
&SignMessage,
|
|
0 );
|
|
#endif
|
|
|
|
//
|
|
// Make sure the signature worked. If not, send error packet.
|
|
//
|
|
if (SecStatus != STATUS_SUCCESS)
|
|
{
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "Sending ERR packet from Make/Seal!!\n"));
|
|
|
|
clientState->LastResponseLength = SIGNED_PACKET_ERROR_LENGTH;
|
|
Error = OscVerifyLastResponseSize(clientState);
|
|
if (Error != ERROR_SUCCESS)
|
|
return Error;
|
|
|
|
memcpy(SendSignedMessage->Signature, ErrorSignedSignature, 4);
|
|
SendSignedMessage->Length = 4;
|
|
}
|
|
else
|
|
{
|
|
BinlPrintDbg(( DEBUG_OSC, "Sending RSPS, %d bytes\n", clientState->LastResponseLength));
|
|
}
|
|
|
|
#ifdef TEST_FAILURE
|
|
if (FailFirstResponse)
|
|
{
|
|
BinlPrintDbg(( DEBUG_OSC, "NOT Sending RSP, %d bytes\n", clientState->LastResponseLength));
|
|
FailFirstResponse = FALSE;
|
|
Error = ERROR_SUCCESS;
|
|
} else
|
|
#endif
|
|
|
|
Error = SendUdpMessage(RequestContext, clientState, TRUE, FALSE);
|
|
|
|
if (Error != ERROR_SUCCESS)
|
|
{
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not send RSP message %d\n", Error ));
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
DWORD
|
|
OscSendUnsignedMessage(
|
|
LPBINL_REQUEST_CONTEXT RequestContext,
|
|
PCLIENT_STATE clientState,
|
|
PCHAR Message,
|
|
ULONG MessageLength
|
|
)
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
SIGNED_PACKET UNALIGNED * SendSignedMessage;
|
|
SecBuffer SigBuffers[2];
|
|
SecBufferDesc SignMessage;
|
|
SECURITY_STATUS SecStatus;
|
|
|
|
#ifdef _TRACE_FUNC_
|
|
TraceFunc("OscSendUnsignedMessage( ");
|
|
BinlPrintDbg(( DEBUG_OSC, "SequenceNumber = %u )\n", clientState->LastSequenceNumber ));
|
|
#endif
|
|
|
|
//
|
|
// Make sure we have space for the message
|
|
//
|
|
clientState->LastResponseLength = MessageLength + SIGNED_PACKET_DATA_OFFSET;
|
|
Error = OscVerifyLastResponseSize(clientState);
|
|
if (Error != ERROR_SUCCESS) {
|
|
return Error;
|
|
}
|
|
|
|
//
|
|
// copy the message
|
|
//
|
|
SendSignedMessage = (SIGNED_PACKET UNALIGNED *) clientState->LastResponse;
|
|
memcpy(SendSignedMessage->Data, Message, MessageLength);
|
|
|
|
//
|
|
// sign the message
|
|
//
|
|
memcpy(SendSignedMessage->Signature, ResponseUnsignedSignature, 4);
|
|
SendSignedMessage->Length = MessageLength + SIGNED_PACKET_EMPTY_LENGTH;
|
|
SendSignedMessage->SequenceNumber = clientState->LastSequenceNumber;
|
|
SendSignedMessage->FragmentNumber = 1; // fragment count
|
|
SendSignedMessage->FragmentTotal = 1; // fragment total
|
|
SendSignedMessage->SignLength = 0;
|
|
|
|
Error = SendUdpMessage(RequestContext, clientState, TRUE, FALSE);
|
|
|
|
if (Error != ERROR_SUCCESS)
|
|
{
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not send RSU message %d\n", Error ));
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
OscSendSetupMessage(
|
|
LPBINL_REQUEST_CONTEXT RequestContext,
|
|
PCLIENT_STATE clientState,
|
|
ULONG RequestType,
|
|
PCHAR Message,
|
|
ULONG MessageLength
|
|
)
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
SPUDP_PACKET UNALIGNED * SendMessage;
|
|
|
|
#ifdef _TRACE_FUNC_
|
|
TraceFunc("OscSendSetupMessage( ");
|
|
BinlPrintDbg(( DEBUG_OSC, "SequenceNumber = %u )\n", clientState->LastSequenceNumber ));
|
|
#endif
|
|
|
|
//
|
|
// Make sure we have space for the message
|
|
//
|
|
clientState->LastResponseLength = MessageLength + SPUDP_PACKET_DATA_OFFSET;
|
|
Error = OscVerifyLastResponseSize(clientState);
|
|
if (Error != ERROR_SUCCESS) {
|
|
return Error;
|
|
}
|
|
|
|
//
|
|
// copy the message
|
|
//
|
|
SendMessage = (SPUDP_PACKET UNALIGNED *) clientState->LastResponse;
|
|
memcpy(SendMessage->Data, Message, MessageLength);
|
|
|
|
//
|
|
// fill in the message stuff
|
|
//
|
|
memcpy(SendMessage->Signature, SetupResponseSignature, 4);
|
|
SendMessage->Length = MessageLength + SPUDP_PACKET_EMPTY_LENGTH;
|
|
SendMessage->Status = STATUS_SUCCESS;
|
|
SendMessage->SequenceNumber = clientState->LastSequenceNumber;
|
|
SendMessage->RequestType = RequestType;
|
|
SendMessage->FragmentNumber = 1; // fragment count
|
|
SendMessage->FragmentTotal = 1; // fragment total
|
|
|
|
Error = SendUdpMessage(RequestContext, clientState, TRUE, FALSE);
|
|
|
|
if (Error != ERROR_SUCCESS) {
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not send SPR message %d\n", Error ));
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
#ifdef SET_ACLS_ON_CLIENT_DIRS
|
|
//
|
|
//
|
|
//
|
|
DWORD
|
|
OscSetClientDirectoryPermissions(
|
|
PCLIENT_STATE clientState )
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
WCHAR DirPath[ MAX_PATH ];
|
|
WCHAR Domain[ 80 ];
|
|
DWORD dwDomainSize = 80;
|
|
PSECURITY_DESCRIPTOR pSD;
|
|
PACL pDACL;
|
|
PSID pSID;
|
|
BOOL bOwnerDefault;
|
|
DWORD dwLengthRequired;
|
|
SID_NAME_USE snu;
|
|
PWCHAR pMachineName = OscFindVariableW( clientState, "MACHINENAME" );
|
|
|
|
if ( !pMachineName || _snwprintf (
|
|
DirPath,
|
|
sizeof(DirPath) / sizeof(DirPath[0]),
|
|
L"%ws\\REMINST\\Clients\\%ws",
|
|
OscFindVariableW( clientState, "SERVERNAME" ),
|
|
pMachineName ) < 0 ) {
|
|
Err = ERROR_BAD_PATHNAME;
|
|
goto Cleanup;
|
|
}
|
|
DirPath[MAX_PATH-1] = L'\0'; // throw in terminating null just to be safe
|
|
|
|
//
|
|
// Figure out how big the machine account's SID is
|
|
//
|
|
LookupAccountName( NULL,
|
|
pMachineName,
|
|
pSID,
|
|
&dwLengthRequired,
|
|
Domain,
|
|
&dwDomainSize,
|
|
&snu );
|
|
|
|
//
|
|
// make space
|
|
//
|
|
pSID = (PSID) BinlAllocateMemory( dwLengthRequired );
|
|
if ( pSID == NULL ) {
|
|
goto OutOfMemory;
|
|
}
|
|
|
|
//
|
|
// get the machine account's SID
|
|
//
|
|
if (!LookupAccountName( NULL, pMachineName, pSID, &dwLengthRequired, Domain, &dwDomainSize, &snu ) ) {
|
|
goto Error;
|
|
}
|
|
|
|
dwLengthRequired += sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid ( pSID );
|
|
pDACL = (PACL) BinlAllocateMemory( dwLengthRequired );
|
|
if ( pDACL == NULL ) {
|
|
goto OutOfMemory;
|
|
}
|
|
|
|
pSD = ( PSECURITY_DESCRIPTOR) BinlAllocateMemory( SECURITY_DESCRIPTOR_MIN_LENGTH + dwLengthRequired );
|
|
if ( pSD == NULL ) {
|
|
goto OutOfMemory;
|
|
}
|
|
|
|
if ( !InitializeSecurityDescriptor ( pSD, SECURITY_DESCRIPTOR_REVISION) ) {
|
|
goto Error;
|
|
}
|
|
|
|
if ( !InitializeAcl( pDACL, dwLengthRequired, ACL_REVISION ) ) {
|
|
goto Error;
|
|
}
|
|
|
|
if ( !AddAccessAllowedAce( pDACL, ACL_REVISION, FILE_ALL_ACCESS, pSID ) ) {
|
|
goto Error;
|
|
}
|
|
|
|
if ( !IsValidAcl( pDACL ) ) {
|
|
goto Error;
|
|
}
|
|
|
|
if ( !SetSecurityDescriptorDacl( pSD, TRUE, pDACL, FALSE ) ) {
|
|
goto Error;
|
|
}
|
|
|
|
if ( !SetSecurityDescriptorOwner( pSD, pSID, FALSE ) ) {
|
|
goto Error;
|
|
}
|
|
|
|
if ( ! IsValidSecurityDescriptor ( pSD ) ) {
|
|
goto Error;
|
|
}
|
|
|
|
if ( !SetFileSecurity( DirPath,
|
|
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
|
|
pSD ) ) {
|
|
goto Error;
|
|
}
|
|
|
|
goto Cleanup;
|
|
|
|
OutOfMemory:
|
|
Err = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
goto Cleanup;
|
|
|
|
Error:
|
|
Err = GetLastError( );
|
|
|
|
Cleanup:
|
|
if ( pSID )
|
|
BinlFreeMemory( pSID );
|
|
|
|
if ( pSD )
|
|
BinlFreeMemory( pSD );
|
|
|
|
return Err;
|
|
}
|
|
#endif // SET_ACLS_ON_CLIENT_DIRS
|
|
|
|
|
|
DWORD
|
|
OscConstructSecret(
|
|
PCLIENT_STATE clientState,
|
|
PWCHAR UnicodePassword,
|
|
ULONG UnicodePasswordLength,
|
|
PCREATE_DATA CreateData
|
|
)
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
UINT i;
|
|
WCHAR DomainBuffer[64];
|
|
DWORD SidLength, DomainLength;
|
|
SID_NAME_USE NameUse;
|
|
BOOL b;
|
|
PCHAR pBootFile;
|
|
PCHAR pSifFile;
|
|
|
|
TraceFunc( "OscConstructSecret( )\n" );
|
|
|
|
RtlZeroMemory(CreateData, sizeof(CREATE_DATA));
|
|
|
|
//
|
|
// Copy the machine data into the response packet
|
|
//
|
|
// The following fields aren't necessary unless we're supporting remote boot.
|
|
// UCHAR Sid[28];
|
|
// UCHAR Domain[32];
|
|
// UCHAR Name[32];
|
|
// UCHAR Password[32];
|
|
// ULONG UnicodePasswordLength; // in bytes
|
|
// WCHAR UnicodePassword[32];
|
|
// UCHAR Installation[32];
|
|
// UCHAR MachineType[6]; // 'i386\0' or 'Alpha\0'
|
|
//
|
|
|
|
pBootFile = OscFindVariableA( clientState, "BOOTFILE" );
|
|
if ( pBootFile[0] == L'\0' ) {
|
|
OscAddVariableA( clientState, "SUBERROR", "BOOTFILE" );
|
|
return ERROR_BINL_MISSING_VARIABLE;
|
|
}
|
|
|
|
pSifFile = OscFindVariableA( clientState, "SIFFILE" );
|
|
if ( pSifFile[0] == L'\0' ) {
|
|
OscAddVariableA( clientState, "SUBERROR", "SIFFILE" );
|
|
return ERROR_BINL_MISSING_VARIABLE;
|
|
}
|
|
|
|
memcpy( CreateData->Id, "ACCT", 4);
|
|
CreateData->VersionNumber = OSC_CREATE_DATA_VERSION;
|
|
strncpy( CreateData->NextBootfile, pBootFile, 128 );
|
|
CreateData->NextBootfile[128-1]='\0';
|
|
strncpy( CreateData->SifFile, pSifFile, 128 );
|
|
CreateData->SifFile[128-1]='\0';
|
|
|
|
BinlAssertMsg( CreateData->NextBootfile[0], "No boot file" );
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
GetOurServerInfo (
|
|
VOID
|
|
)
|
|
//
|
|
// This routine gets several global names that we need to handle client
|
|
// requests. We store them in globals because they change very infrequently
|
|
// and they're relatively expense to retrieve.
|
|
//
|
|
{
|
|
PWCHAR fqdn = NULL;
|
|
DWORD uSize;
|
|
DWORD dnsError = ERROR_SUCCESS;
|
|
DWORD fqdnError = ERROR_SUCCESS;
|
|
DWORD netbiosServerError = ERROR_SUCCESS;
|
|
DWORD netbiosDomainError = ERROR_SUCCESS;
|
|
PWCHAR ourDNSName = NULL;
|
|
PWCHAR tmp;
|
|
PWCHAR pDomain;
|
|
WCHAR ServerName[32] = { 0 };
|
|
DWORD ServerSize = sizeof(ServerName) / sizeof(WCHAR);
|
|
ULONG Error;
|
|
|
|
// first grab the netbios name of our server
|
|
|
|
if ( !GetComputerNameEx( ComputerNameNetBIOS, ServerName, &ServerSize ) ) {
|
|
netbiosServerError = GetLastError();
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "!! Error 0x%08x - GetComputerNameEx failed.\n", netbiosServerError ));
|
|
} else {
|
|
|
|
tmp = BinlStrDup( ServerName );
|
|
|
|
if (tmp == NULL) {
|
|
netbiosServerError = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
} else {
|
|
EnterCriticalSection( &gcsParameters );
|
|
|
|
if (BinlGlobalOurServerName) {
|
|
BinlFreeMemory( BinlGlobalOurServerName );
|
|
}
|
|
|
|
BinlGlobalOurServerName = tmp;
|
|
|
|
LeaveCriticalSection( &gcsParameters );
|
|
}
|
|
}
|
|
|
|
// Next grab the fully qualified domain name of our server
|
|
uSize = 0;
|
|
if ( !GetComputerObjectName( NameFullyQualifiedDN, NULL, &uSize ) ) {
|
|
fqdnError = GetLastError( );
|
|
if ( fqdnError != ERROR_MORE_DATA ) {
|
|
|
|
BinlPrint((DEBUG_OSC_ERROR, "!! Error 0x%08x - GetComputerObjectName failed.\n", fqdnError ));
|
|
goto GetDNS;
|
|
}
|
|
fqdnError = ERROR_SUCCESS;
|
|
}
|
|
fqdn = BinlAllocateMemory( uSize * sizeof(WCHAR) );
|
|
if ( fqdn ) {
|
|
if ( !GetComputerObjectName( NameFullyQualifiedDN, fqdn, &uSize ) ) {
|
|
|
|
fqdnError = GetLastError( );
|
|
BinlPrint((DEBUG_OSC_ERROR, "!! Error 0x%08x - GetComputerObjectName failed.\n", fqdnError ));
|
|
|
|
} else {
|
|
|
|
EnterCriticalSection( &gcsParameters );
|
|
|
|
tmp = BinlGlobalOurFQDNName;
|
|
BinlGlobalOurFQDNName = fqdn;
|
|
|
|
fqdn = tmp; // we'll free it below
|
|
|
|
// next setup the netbios domain name
|
|
|
|
pDomain = StrStrIW( BinlGlobalOurFQDNName, L"DC=" );
|
|
if ( pDomain ) {
|
|
|
|
PDS_NAME_RESULTW pResults;
|
|
|
|
BinlPrintDbg(( DEBUG_OSC, "Converting %ws to a NetBIOS domain name...\n", pDomain ));
|
|
|
|
netbiosDomainError = DsCrackNames( INVALID_HANDLE_VALUE,
|
|
DS_NAME_FLAG_SYNTACTICAL_ONLY,
|
|
DS_FQDN_1779_NAME,
|
|
DS_CANONICAL_NAME,
|
|
1,
|
|
&pDomain,
|
|
&pResults );
|
|
if (netbiosDomainError != ERROR_SUCCESS) {
|
|
|
|
BinlPrint(( DEBUG_ERRORS, "GetOurServerInfo error in DsCrackNames %u\n", netbiosDomainError ));
|
|
}
|
|
|
|
if ( netbiosDomainError == ERROR_SUCCESS ) {
|
|
if ( pResults->cItems == 1
|
|
&& pResults->rItems[0].status == DS_NAME_NO_ERROR
|
|
&& pResults->rItems[0].pName ) { // paranoid
|
|
|
|
pResults->rItems[0].pName[wcslen(pResults->rItems[0].pName)-1] = L'\0';
|
|
|
|
tmp = BinlStrDup( pResults->rItems[0].pName );
|
|
|
|
if (tmp == NULL) {
|
|
|
|
netbiosDomainError = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
|
|
} else {
|
|
|
|
if (BinlGlobalOurDomainName) {
|
|
BinlFreeMemory( BinlGlobalOurDomainName );
|
|
}
|
|
BinlGlobalOurDomainName = tmp;
|
|
}
|
|
}
|
|
DsFreeNameResult( pResults );
|
|
}
|
|
}
|
|
LeaveCriticalSection( &gcsParameters );
|
|
}
|
|
} else {
|
|
|
|
fqdnError = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
}
|
|
|
|
GetDNS:
|
|
// Retrieve the FQDNS name of the server
|
|
uSize = 0;
|
|
if ( !GetComputerNameEx( ComputerNameDnsFullyQualified, NULL, &uSize ) ) {
|
|
dnsError = GetLastError( );
|
|
if ( dnsError != ERROR_MORE_DATA ) {
|
|
BinlPrint((DEBUG_OSC_ERROR, "!! Error 0x%08x - GetComputerNameEx failed.\n", dnsError ));
|
|
goto returnError;
|
|
}
|
|
dnsError = ERROR_SUCCESS;
|
|
}
|
|
ourDNSName = (PWCHAR) BinlAllocateMemory( uSize * sizeof(WCHAR) );
|
|
if ( ourDNSName ) {
|
|
if ( !GetComputerNameEx( ComputerNameDnsFullyQualified, ourDNSName, &uSize ) ) {
|
|
|
|
dnsError = GetLastError( );
|
|
BinlPrint((DEBUG_OSC_ERROR, "!! Error 0x%08x - GetComputerNameEx failed.\n", dnsError ));
|
|
|
|
} else {
|
|
|
|
EnterCriticalSection( &gcsParameters );
|
|
|
|
tmp = BinlGlobalOurDnsName;
|
|
BinlGlobalOurDnsName = ourDNSName;
|
|
|
|
LeaveCriticalSection( &gcsParameters );
|
|
|
|
ourDNSName = tmp; // we'll free it below
|
|
}
|
|
} else {
|
|
dnsError = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
}
|
|
|
|
returnError:
|
|
|
|
if (ourDNSName) {
|
|
BinlFreeMemory( ourDNSName );
|
|
}
|
|
if (fqdn) {
|
|
BinlFreeMemory( fqdn );
|
|
}
|
|
if (fqdnError != ERROR_SUCCESS) {
|
|
Error = fqdnError;
|
|
} else if (dnsError != ERROR_SUCCESS) {
|
|
Error = dnsError;
|
|
} else if (netbiosServerError != ERROR_SUCCESS) {
|
|
Error = netbiosServerError;
|
|
} else {
|
|
Error = netbiosDomainError;
|
|
}
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
GetDomainNetBIOSName(
|
|
IN PCWSTR DomainNameInAnyFormat,
|
|
OUT PWSTR *NetBIOSName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves the netbios name for a domain given an input name. The input
|
|
name may be in DNS form or netbios form, it doesn't really matter.
|
|
|
|
Arguments:
|
|
|
|
DomainNameInAnyFormat - string representing the name of the domain to query
|
|
|
|
NetBIOSName - receives string that represents the domain netbios name.
|
|
The string must be freed via BinlFreeMemory.
|
|
|
|
Return Value:
|
|
|
|
win32 error code indicating outcome.
|
|
|
|
--*/
|
|
{
|
|
PDOMAIN_CONTROLLER_INFO DomainControllerInfo = NULL;
|
|
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC DomainInfo = NULL;
|
|
DWORD Error;
|
|
|
|
Error = DsGetDcName(
|
|
NULL,
|
|
DomainNameInAnyFormat,
|
|
NULL,
|
|
NULL,
|
|
DS_RETURN_DNS_NAME,
|
|
&DomainControllerInfo );
|
|
|
|
if (Error != ERROR_SUCCESS) {
|
|
BinlPrintDbg((
|
|
DEBUG_ERRORS,
|
|
"DsGetDcName (%ws) failed, ec = %d.\r\n",
|
|
DomainNameInAnyFormat,
|
|
Error ));
|
|
goto exit;
|
|
}
|
|
|
|
Error = DsRoleGetPrimaryDomainInformation(
|
|
DomainControllerInfo->DomainControllerName,
|
|
DsRolePrimaryDomainInfoBasic,
|
|
(PBYTE *) &DomainInfo);
|
|
|
|
if (Error != ERROR_SUCCESS) {
|
|
BinlPrintDbg((
|
|
DEBUG_ERRORS,
|
|
"DsRoleGetPrimaryDomainInformation (%ws) failed, ec = %d.\r\n",
|
|
DomainControllerInfo->DomainControllerName,
|
|
Error ));
|
|
goto exit;
|
|
}
|
|
|
|
*NetBIOSName = BinlStrDup( DomainInfo->DomainNameFlat );
|
|
|
|
if (!*NetBIOSName) {
|
|
BinlPrintDbg((
|
|
DEBUG_ERRORS,
|
|
"GetDomainNetBIOSName: failed to allocate memory (%d bytes) .\r\n",
|
|
(wcslen(DomainInfo->DomainNameFlat)+1) * sizeof(WCHAR) ));
|
|
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
}
|
|
|
|
exit:
|
|
if (DomainInfo) {
|
|
DsRoleFreeMemory( DomainInfo );
|
|
}
|
|
|
|
if (DomainControllerInfo) {
|
|
NetApiBufferFree( DomainControllerInfo );
|
|
}
|
|
|
|
return(Error);
|
|
|
|
}
|