/*++ 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[] = ""\ "" "" " Client Installation Wizard Error "; // there is a %08x after this CHAR ErrorScreenBody[] = " " "" "

"; // the error message is inserted here CHAR ErrorScreenTrailer[] = "An error occurred on the server. Please notify your administrator.
" "%SUBERROR%
" "" "
"; #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
%s

%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); }