/*++ 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%
" "" "
"; 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 ) + 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; *PasswordLength = LM20_PWLEN * sizeof(WCHAR); for (i = 0; i < LM20_PWLEN; i++) { if ( Password[i] == L'\0' ) { Password[i] = 0x55; } else if ((USHORT)Password[i] < 0x20 || (USHORT)Password[i] > 0x7A) { Password[i] = Password[i] % (0x7a-0x20) + 0x20; } } Password[LM20_PWLEN] = L'\0'; } // // 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."; TCHAR 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 ) != -1 ) { // // 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, lstrlenA(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; ldap_set_option(ClientState->AuthenticatedDCLdapHandle, LDAP_OPT_GETDSNAME_FLAGS, &temp ); temp = LDAP_VERSION3; ldap_set_option(ClientState->AuthenticatedDCLdapHandle, LDAP_OPT_VERSION, &temp ); // // Tell LDAP to keep connections referenced after searches. // temp = (ULONG)((ULONG_PTR)LDAP_OPT_ON); ldap_set_option(ClientState->AuthenticatedDCLdapHandle, LDAP_OPT_REF_DEREF_CONN_PER_MSG, &temp); 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) { RtlZeroMemory(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] = UNICODE_NULL; 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] = UNICODE_NULL; GetPrivateProfileStringA(OSCHOOSER_SIF_SECTIONA, "ImageType", Buffer, // default Buffer, 256, pSifPath ); if (_stricmp("ASR", Buffer)) { return FALSE; } return TRUE; } DWORD OscGetSkuType( PWSTR PathToTxtSetupSif ) { PWSTR SifFile; DWORD SkuType = 0; SifFile = BinlAllocateMemory( (wcslen(PathToTxtSetupSif) + 1 + wcslen(L"txtsetup.sif") + 1 ) * 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, OUT PBOOLEAN ExactMatch ) { DWORD Error = ERROR_SUCCESS; WIN32_FIND_DATA FindData; HANDLE hFind = INVALID_HANDLE_VALUE; BOOLEAN Impersonated = FALSE; WCHAR Path[MAX_PATH]; ULONGLONG BestVersion = (ULONGLONG)0; ULONGLONG ThisVersion; ULONGLONG KernelVersion; DWORD dwPathLen; BOOLEAN ReturnValue = FALSE; 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 ) == -1 ) { goto Cleanup; } 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; // // 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" wcscat(Path, L"\\"); wcscat(Path, OscFindVariableW(ClientState, "MACHINETYPE")); ThisSkuType = OscGetSkuType( Path ); #if 0 // // Now look for the kernel. We want to save the best version // that is newer or equal to the kernel we are looking for, // and older than the previous best version (note that // BestVersion is initialized to 0 so we need to check for // that also). // if (OscGetNtVersionInfo(&ThisVersion, Path, ClientState) && (ThisVersion >= KernelVersion) && (ThisSkuType == SkuType) && ((BestVersion == (ULONGLONG)0) || (ThisVersion < BestVersion))) { BestVersion = ThisVersion; wcscpy(SetupPath, Path); } #else 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))) { BestVersion = ThisVersion; wcscpy(SetupPath, Path); } } #endif } } 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 (wcslen(SearchDir) + sizeof("\\ntoskrnl.exe") + 1> sizeof(Path)/sizeof(Path[0])) { goto e0; // path too long, skip it } wcscpy(Path, SearchDir); wcscat(Path, L"\\ntoskrnl.exe"); 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; *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 ( _snwprintf ( DirPath, sizeof(DirPath) / sizeof(DirPath[0]), L"%ws\\REMINST\\Clients\\%ws", OscFindVariableW( clientState, "SERVERNAME" ), pMachineName ) == -1 ) { Err = ERROR_BAD_PATHNAME; goto Cleanup; } // // 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 #if 0 VOID OscGetFlipServerList( PUCHAR FlipServerList, PULONG FlipServerListLength ) /*++ Routine Description: This function gets the flip server list using the GPT of the currently impersonated user. Arguments: FlipServerList - The location to store the flip server list, which is a series of 4-byte IP addresses. FlipServerListLength - Returns the length of the flip server list if any are stored. Return Value: None. --*/ { LPWSTR GptPath; WCHAR NetbootPath[MAX_PATH]; WCHAR TempServerList[128]; ULONG TempServerIp[4]; LPWSTR CurDirLoc, CurDirEnd; LPWSTR CurSrvLoc, CurSrvEnd; PUCHAR CurFlipLoc; ULONG FlipServerCount; TraceFunc("OscGetFlipServerList( )\n"); TempServerList[0] = L'\0'; // // Read the GPT path. // GptPath = GetGPTPath(NULL, NULL); // // Scan the GPT path for a netboot.ini file. // CurDirLoc = GptPath; while (*CurDirLoc != L'\0') { CurDirEnd = wcschr(CurDirLoc, L';'); if (CurDirEnd == NULL) { wsprintf(NetbootPath, L"%wsnetboot.ini", CurDirLoc); CurDirLoc += wcslen(CurDirLoc); // points to final '\0' } else { *CurDirEnd = L'\0'; // HACK since %*ws does not seem to work wsprintf(NetbootPath, L"%wsnetboot.ini", CurDirLoc); *CurDirEnd = L';'; CurDirLoc = CurDirEnd + 1; // move past the ';' } // // Check that the file exists. // if (GetFileAttributes(NetbootPath) == (DWORD)-1) { continue; } // // If the file exists, assume it is the right one -- if // it doesn't have the right section and key, don't try to // look in the next path location. // GetPrivateProfileString( L"BINL Server", L"NewAccountServers", L"", // default value is empty string TempServerList, sizeof(TempServerList), NetbootPath ); break; } // // At this point the server list is in TempServerList, which // may be of length 0. Parse through it for IP addresses and // put them in FlipServerList. // BinlPrintDbg(( DEBUG_OSC, "TempServerList is <%ws>\n", TempServerList )); CurSrvLoc = TempServerList; CurFlipLoc = FlipServerList; while (*CurSrvLoc != L'\0') { CurSrvEnd = wcschr(CurSrvLoc, L','); if (CurSrvEnd != NULL) { *CurSrvEnd = L'\0'; } swscanf(CurSrvLoc, L"%ld.%ld.%ld.%ld", &TempServerIp[0], &TempServerIp[1], &TempServerIp[2], &TempServerIp[3]); CurFlipLoc[0] = (UCHAR)TempServerIp[0]; CurFlipLoc[1] = (UCHAR)TempServerIp[1]; CurFlipLoc[2] = (UCHAR)TempServerIp[2]; CurFlipLoc[3] = (UCHAR)TempServerIp[3]; #if 0 BinlPrintDbg(( DEBUG_OSC, "FOUND IP address %d.%d.%d.%d\n", CurFlipLoc[0], CurFlipLoc[1], CurFlipLoc[2], CurFlipLoc[3])); #endif CurFlipLoc += 4; *FlipServerListLength += 4; // // Only allow MAX_FLIP_SERVER_COUNT servers. // if (*FlipServerListLength == (MAX_FLIP_SERVER_COUNT*4)) { break; } if (CurSrvEnd != NULL) { CurSrvLoc = CurSrvEnd + 1; } else { break; } } // // Randomize the list in FlipServerList. // FlipServerCount = (*FlipServerListLength) / 4; if (FlipServerCount > 1) { ULONG RandomSeed; ULONG i, j; UCHAR TempBuffer[4]; // // For each element except the last, swap it with a random // element. // RandomSeed = GetTickCount(); for (i = 0; i < FlipServerCount-1; i++) { // // Pick a random element j between the ith and the end. // j = i + (RtlRandom(&RandomSeed) % (FlipServerCount - i)); // // Swap ith and jth element, unless i == j. // if (i != j) { memcpy(TempBuffer, &FlipServerList[i*4], 4); memcpy(&FlipServerList[i*4], &FlipServerList[j*4], 4); memcpy(&FlipServerList[j*4], TempBuffer, 4); } } } } #endif // FlipServer #ifdef REMOTE_BOOT // // Copy any initial files specified in SIF. // DWORD OscCopyTemplateFiles( LPWSTR SourcePath, LPWSTR ImagePath, LPWSTR TemplateFile ) { #define MAX_FILES_SIZE 2048 WCHAR Files[ MAX_FILES_SIZE ]; DWORD dwErr = ERROR_SUCCESS; LPWSTR pFilename; WCHAR SrcFilepath[ MAX_PATH ]; WCHAR DstFilepath[ MAX_PATH ]; TraceFunc("OscCopyTemplateFiles( )\n"); dwErr = GetPrivateProfileSection( L"OSChooserFiles", Files, MAX_FILES_SIZE, TemplateFile ); BinlAssert( dwErr != MAX_FILES_SIZE - 2 ); #undef MAX_FILES_SIZE pFilename = Files; while ( *pFilename ) { BOOL b; LPWSTR psz = pFilename; while ( *psz && *psz !=L',' ) psz++; if ( *psz == L',' ) { *psz = L'\0'; psz++; } else { psz = pFilename; } wsprintf( SrcFilepath, L"%ws\\%ws", SourcePath, psz ); wsprintf( DstFilepath, L"%ws\\%ws", ImagePath, pFilename ); BinlPrintDbg((DEBUG_OSC, "Copying %ws to %ws...\n", SrcFilepath, DstFilepath)); b = CopyFile( SrcFilepath, DstFilepath, TRUE ); if ( !b ) { dwErr = GetLastError( ); goto Error; } // find the end of the string while ( *psz ) psz++; pFilename = ++psz; // skip null } Error: return dwErr; } #endif // REMOTE_BOOT #if DBG && defined(REMOTE_BOOT) // // Create MAC Address file - // This might be turned into a DESKTOP.INI file(?). -gpease // DWORD OscCreateNullFile( LPWSTR Image, LPWSTR MAC ) { DWORD dwErr = ERROR_SUCCESS; WCHAR Path[ MAX_PATH ]; HANDLE hFile = INVALID_HANDLE_VALUE; TraceFunc("OscCreateNullFile( )\n"); wsprintf( Path, L"%ws\\%ws", Image, MAC ); // // Create NULL length file // hFile = CreateFile( Path, GENERIC_WRITE, FILE_SHARE_READ, NULL, // security attribs CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, // maybe FILE_ATTRIBUTE_HIDDEN NULL ); // template if (hFile != INVALID_HANDLE_VALUE) { CloseHandle( hFile ); } return dwErr; } #endif 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; #if defined(REMOTE_BOOT) PCHAR pNetBIOSName; PCHAR pUserDomain; #endif 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; strcpy( CreateData->NextBootfile, pBootFile ); strcpy( CreateData->SifFile, pSifFile ); #if defined(REMOTE_BOOT) pNetBIOSName = OscFindVariableA( clientState, "NETBIOSNAME"); if ( pNetBIOSName[0] == L'\0' ) { OscAddVariableA( clientState, "SUBERROR", "NETBIOSNAME" ); return ERROR_BINL_MISSING_VARIABLE; } pUserDomain = OscFindVariableA( clientState, "USERDOMAIN" ); if ( pUserDomain[0] == L'\0' ) { OscAddVariableA( clientState, "SUBERROR", "USERDOMAIN" ); return ERROR_BINL_MISSING_VARIABLE; } strcpy( CreateData->Name, OscFindVariableA( clientState, pNetBIOSName ) ); CreateData->UnicodePasswordLength = UnicodePasswordLength; memcpy( CreateData->UnicodePassword, UnicodePassword, CreateData->UnicodePasswordLength ); strcpy( CreateData->Domain, OscFindVariableA( clientState, pUserDomain ) ); // // We send the password down in Unicode also which is // just the machine name. // NOTE: We probably need to worry about // the code page of the client, for the moment just do // the simplest conversion. We may also want to get // the machine name/password in Unicode and only convert // it to ANSI right here. We'll punt this for the moment, // at least the on-the-wire format will be correct. // // // This isn't used right now, we only care about the Unicode password. // memset( CreateData->Password, '\0', sizeof(CreateData->Password) ); // // Get the SID from the system. // SidLength = sizeof(CreateData->Sid); DomainLength = sizeof(DomainBuffer) / sizeof(WCHAR); b = LookupAccountName( NULL, OscFindVariableW( clientState, "NETBIOSNAME" ), CreateData->Sid, &SidLength, DomainBuffer, &DomainLength, &NameUse); if (!b) { DWORD dwErr = GetLastError( ); BinlPrintDbg(( DEBUG_OSC_ERROR, "!! Error 0x%08x - Account lookup failed.\n", dwErr )); OscCreateWin32SubError( clientState, dwErr ); return ERROR_BINL_FAILED_TO_INITIALIZE_CLIENT;; } // This should be the NETBIOS domain name. wcstombs( CreateData->Domain, DomainBuffer, DomainLength + 1 ); // // Sanity check // BinlAssertMsg( CreateData->Name[0], "No machine name" ); // BinlAssertMsg( CreateData->Password[0], "No machine password" ); BinlAssertMsg( CreateData->UnicodePasswordLength, "Password length is ZERO" ); BinlAssertMsg( CreateData->UnicodePassword[0], "No UNICODE machine password" ); BinlAssertMsg( CreateData->Domain[0], "No machine domain" ); #endif 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 = BinlAllocateMemory( ( lstrlenW( ServerName ) + 1 ) * sizeof(WCHAR) ); if (tmp == NULL) { netbiosServerError = ERROR_NOT_ENOUGH_SERVER_MEMORY; } else { lstrcpyW( tmp, ServerName ); 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 = BinlAllocateMemory( ( lstrlenW( pResults->rItems[0].pName ) + 1 ) * sizeof(WCHAR) ); if (tmp == NULL) { netbiosDomainError = ERROR_NOT_ENOUGH_SERVER_MEMORY; } else { lstrcpyW( tmp, pResults->rItems[0].pName ); 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_FLAT_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 = BinlAllocateMemory( (wcslen(DomainInfo->DomainNameFlat)+1) * sizeof(WCHAR) ); if (*NetBIOSName) { wcscpy( *NetBIOSName, DomainInfo->DomainNameFlat ); } else { 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); }