|
|
/*++
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);
}
|