windows-nt/Source/XPSP1/NT/net/dhcp/server/binl/osc.c

4816 lines
153 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997-1998 Microsoft Corporation
Module Name:
osc.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
#include <dns.h>
#include <aclapi.h>
#ifdef TEST_FAILURE
BOOL FailFirstChallenge = TRUE;
BOOL FailFirstResult = TRUE;
BOOL FailFirstResponse = TRUE;
BOOL FailFirstFragment = TRUE;
#endif
//
// Flag indicating whether OscInitialize has been called.
//
BOOLEAN OscInitialized = FALSE;
//
// The list of clients.
//
LIST_ENTRY ClientsQueue;
//
// This guards access to ClientsQueue.
//
CRITICAL_SECTION ClientsCriticalSection;
//
// This is a temporary hack to serialize all calls to the
// NetUserSetInfo/NetUserModalsGet pair. See discussion in
// bug 319962.
//
CRITICAL_SECTION HackWorkaroundCriticalSection;
//
// CurrentClientCount-er access guard
//
CRITICAL_SECTION g_CurrentClientCountCritSect;
//
// Guards creation of the \remoteinstall\tmp directory.
//
CRITICAL_SECTION g_TmpDirectoryCriticalSection;
//
// Credential handle from SSPI
//
CredHandle CredentialHandle;
//
// Info on the NTLMSSP security package.
//
PSecPkgInfo PackageInfo = NULL;
#if DBG
CHAR OscWatchVariable[32] = "";
#endif
DWORD
OscCheckTmpDirectory(
VOID
)
/*++
Routine Description:
This function verifies that the \remoteinstall\tmp directory
is there, if not it creates it.
Arguments:
None.
Return Value:
Windows Error.
--*/
{
DWORD Error = ERROR_SUCCESS;
WCHAR TmpPath[ MAX_PATH ];
DWORD FileAttributes;
BOOL InCriticalSection = FALSE;
PSID pEveryoneSID = NULL;
PACL pACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
BOOL b;
if ( _snwprintf( TmpPath,
sizeof(TmpPath) / sizeof(TmpPath[0]),
L"%ws\\%ws",
IntelliMirrorPathW,
TEMP_DIRECTORY
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
goto Cleanup;
}
FileAttributes = GetFileAttributes(TmpPath);
if (FileAttributes == 0xFFFFFFFF) {
EXPLICIT_ACCESS ea;
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
SECURITY_ATTRIBUTES sa;
//
// Get the critical section if we are going to try to create it.
//
InCriticalSection = TRUE;
EnterCriticalSection(&g_TmpDirectoryCriticalSection);
//
// Make sure it still needs to be created.
//
FileAttributes = GetFileAttributes(TmpPath);
if (FileAttributes == 0xFFFFFFFF) {
// Create a well-known SID for the Everyone group.
if(! AllocateAndInitializeSid( &SIDAuthWorld, 1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&pEveryoneSID) ) {
Error = GetLastError();
BinlPrintDbg(( DEBUG_INIT, "AllocateAndInitializeSid failed: %lx\n", Error ));
goto Cleanup;
}
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow Everyone all access to the directory.
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
ea.grfAccessMode = SET_ACCESS;
ea.grfInheritance= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea.Trustee.ptstrName = (LPTSTR) pEveryoneSID;
// Create a new ACL that contains the new ACE.
Error = SetEntriesInAcl(1, &ea, NULL, &pACL);
if (Error != ERROR_SUCCESS) {
BinlPrintDbg(( DEBUG_INIT, "SetEntriesInAcl failed lx\n", Error ));
goto Cleanup;
}
// Initialize a security descriptor.
pSD = (PSECURITY_DESCRIPTOR) BinlAllocateMemory(SECURITY_DESCRIPTOR_MIN_LENGTH);
if (pSD == NULL) {
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
BinlPrintDbg(( DEBUG_INIT, "Allocate SECURITY_DESCRIPTOR failed\n"));
goto Cleanup;
}
if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {
Error = GetLastError();
BinlPrintDbg(( DEBUG_INIT, "InitializeSecurityDescriptor failed: %lx\n", Error ));
goto Cleanup;
}
// Add the ACL to the security descriptor.
if (!SetSecurityDescriptorDacl(pSD,
TRUE, // fDaclPresent flag
pACL,
FALSE)) // not a default DACL
{
Error = GetLastError();
BinlPrintDbg(( DEBUG_INIT, "SetSecurityDescriptorDacl failed: %lx\n", Error ));
goto Cleanup;
}
// Initialize a security attributes structure.
sa.nLength = sizeof (SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
b = CreateDirectory(TmpPath, &sa);
if (!b) {
Error = GetLastError();
BinlPrintDbg(( DEBUG_INIT, "CreateDirectory failed: %lx\n", Error ));
goto Cleanup;
}
}
} else if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
Error = ERROR_FILE_EXISTS;
goto Cleanup;
}
Cleanup:
if (InCriticalSection) {
LeaveCriticalSection(&g_TmpDirectoryCriticalSection);
}
if (pEveryoneSID) {
FreeSid(pEveryoneSID);
}
if (pACL) {
BinlFreeMemory(pACL);
}
if (pSD) {
BinlFreeMemory(pSD);
}
return Error;
}
DWORD
OscInitialize(
VOID
)
/*++
Routine Description:
This function initializes the OSChooser server.
Arguments:
None.
Return Value:
Windows Error.
--*/
{
DWORD Error = ERROR_SUCCESS;
SECURITY_STATUS SecStatus;
ULONG PackageCount;
TimeStamp Lifetime;
LPSHARE_INFO_2 psi;
NET_API_STATUS netStatus;
TraceFunc("OscInitialize( )\n");
if ( !OscInitialized ) {
InitializeListHead(&ClientsQueue);
InitializeCriticalSection(&ClientsCriticalSection);
InitializeCriticalSection(&HackWorkaroundCriticalSection);
InitializeCriticalSection(&g_CurrentClientCountCritSect);
InitializeCriticalSection(&g_TmpDirectoryCriticalSection);
CredentialHandle.dwLower = 0;
CredentialHandle.dwUpper = 0;
OscInitialized = TRUE;
}
//
// Retrieve the path to the remote install directory
//
netStatus = NetShareGetInfo(NULL, L"REMINST", 2, (LPBYTE *)&psi);
if ( netStatus == ERROR_SUCCESS )
{
wcscpy( IntelliMirrorPathW, psi->shi2_path );
NetApiBufferFree(psi);
}
else
{
BinlPrintDbg(( DEBUG_MISC, "NetShareGetInfo( ) returned 0x%08x\n", netStatus));
BinlServerEventLog(
EVENT_SERVER_OSC_NO_DEFAULT_SHARE_FOUND,
EVENTLOG_ERROR_TYPE,
netStatus );
Error = ERROR_BINL_SHARE_NOT_FOUND;
goto Cleanup;
}
//
// Translate this to ANSI once, use many...
//
wcstombs( IntelliMirrorPathA, IntelliMirrorPathW, wcslen(IntelliMirrorPathW) +1 );
//
// Make sure there is a tmp directory below it.
//
Error = OscCheckTmpDirectory();
if (Error != ERROR_SUCCESS) {
BinlPrintDbg(( DEBUG_INIT, "OscCheckTempDirectory failed lx\n", Error ));
goto Cleanup;
}
//
// Get info about the security packages.
//
SecStatus = EnumerateSecurityPackages( &PackageCount, &PackageInfo );
if ( SecStatus != STATUS_SUCCESS ) {
BinlPrintDbg(( DEBUG_INIT, "EnumerateSecurityPackages failed: %lx\n", SecStatus ));
Error = ERROR_NO_SUCH_PACKAGE;
goto Cleanup;
}
//
// Get info about the security packages.
//
SecStatus = QuerySecurityPackageInfo( NTLMSP_NAME, &PackageInfo );
if ( SecStatus != STATUS_SUCCESS ) {
BinlPrintDbg(( DEBUG_INIT, "QuerySecurityPackageInfo failed: %lx\n", SecStatus ));
Error = ERROR_NO_SUCH_PACKAGE;
goto Cleanup;
}
//
// Acquire a credential handle for the server side
//
SecStatus = AcquireCredentialsHandle(
NULL, // New principal
NTLMSP_NAME, // Package Name
SECPKG_CRED_INBOUND,
NULL,
NULL,
NULL,
NULL,
&CredentialHandle,
&Lifetime );
if ( SecStatus != STATUS_SUCCESS ) {
BinlPrintDbg(( DEBUG_INIT, "AcquireCredentialsHandle failed: %lx\n", SecStatus ));
Error = SecStatus;
}
Cleanup:
return Error;
}
VOID
OscUninitialize(
VOID
)
/*++
Routine Description:
This function uninitializes the OSChooser server.
Arguments:
None.
Return Value:
None.
--*/
{
SECURITY_STATUS SecStatus;
TraceFunc("OscUninitialize( )\n");
if ( OscInitialized ) {
OscFreeClients();
DeleteCriticalSection(&ClientsCriticalSection);
DeleteCriticalSection(&HackWorkaroundCriticalSection);
DeleteCriticalSection(&g_CurrentClientCountCritSect);
DeleteCriticalSection(&g_TmpDirectoryCriticalSection);
SecStatus = FreeCredentialsHandle( &CredentialHandle );
if ( SecStatus != STATUS_SUCCESS ) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "FreeCredentialsHandle failed: %lx\n", SecStatus ));
}
if ( BinlOscClientDSHandle != NULL ) {
DsUnBind( &BinlOscClientDSHandle );
BinlOscClientDSHandle = NULL;
}
OscInitialized = FALSE;
}
}
DWORD
OscProcessMessage(
LPBINL_REQUEST_CONTEXT RequestContext
)
/*++
Routine Description:
This function dispatches the processing of a received OS chooser message.
The handler functions will send response messages if necessary.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
Return Value:
Windows Error.
--*/
{
SIGNED_PACKET UNALIGNED * loginMessage = (SIGNED_PACKET UNALIGNED *)RequestContext->ReceiveBuffer;
DWORD Error;
PCLIENT_STATE clientState = NULL;
ULONG RemoteIp;
BOOL FreeClientState;
BOOL IsLogoffMessage;
BOOL IsNetcardRequestMessage;
BOOL IsHalRequestMessage;
BOOL IsNegotiateMessage;
BOOL IsAuthenicateMessage;
TraceFunc("OscProcessMessage( )\n");
BinlPrintDbg(( DEBUG_OSC, "Received message, length %d, data %.3s\n",
RequestContext->ReceiveMessageSize,
((PUCHAR)loginMessage)+1));
//
// Extract the IP address of the client.
//
RemoteIp = ((struct sockaddr_in *)&RequestContext->SourceName)->sin_addr.s_addr;
//
// IsLogoffMessage will be TRUE if the signature is equal to LogoffSignature.
//
IsLogoffMessage = (BOOL)(memcmp(loginMessage->Signature, LogoffSignature, 4) == 0);
//
// IsNetcardRequestMessage will be TRUE if the signature is equal to NetcardRequestSignature.
//
IsNetcardRequestMessage = (BOOL)(memcmp(loginMessage->Signature, NetcardRequestSignature, 4) == 0);
//
// IsHalRequestMessage will be TRUE if the signature is equal to HalRequestSignature.
//
IsHalRequestMessage = (BOOL)(memcmp(loginMessage->Signature, HalRequestSignature, 4) == 0);
//
// IsNegotiateMessage will be TRUE if the signature is equal to a NegotiateSignature.
//
IsNegotiateMessage = (BOOL)(memcmp(loginMessage->Signature, NegotiateSignature, 4) == 0);
//
// IsAuthenicateMessage will be TRUE if the signature is equal to an AuthenticateSignature
// or AuthenticateFlippedSignature.
//
IsAuthenicateMessage = (BOOL)((memcmp(loginMessage->Signature, AuthenticateSignature, 4) == 0) ||
(memcmp(loginMessage->Signature, AuthenticateFlippedSignature, 4) == 0));
//
// All messages except netcard queries need to use a CLIENT_STATE.
//
if (!IsNetcardRequestMessage)
{
//
// If IsLogoffMessage is FALSE, this finds an old CLIENT_STATE or creates
// a new one. If IsLogoffMessage is TRUE, this removes CLIENT_STATE from
// the database if it finds it.
// In both cases, if successful, it adds one to the PositiveRefCount.
//
Error = OscFindClient(RemoteIp, IsLogoffMessage, &clientState);
if (Error == ERROR_NOT_ENOUGH_SERVER_MEMORY)
{
CLIENT_STATE TempClientState;
SIGNED_PACKET TempLoginPacket;
BinlPrint(( DEBUG_OSC_ERROR, "Could not get client state for %s\n", inet_ntoa(*(struct in_addr *)&RemoteIp) ));
//
// Send a NAK back to the client. We use a local CLIENT_STATE
// since this did not allocate one.
//
TempClientState.LastResponse = (PUCHAR)&TempLoginPacket;
TempClientState.LastResponseLength = SIGNED_PACKET_DATA_OFFSET;
memcpy(TempLoginPacket.Signature, NegativeAckSignature, 4);
TempLoginPacket.Length = 0;
Error = SendUdpMessage(RequestContext, &TempClientState, FALSE, FALSE);
if (Error != ERROR_SUCCESS)
{
BinlPrint(( DEBUG_OSC_ERROR, "Could not send NAK message %d\n", Error));
}
return ERROR_NOT_ENOUGH_SERVER_MEMORY;
}
else if (Error == ERROR_BUSY) {
//
// If it is likely that another thread is processing a request
// for this client, then just exit quietly.
//
BinlPrintDbg((DEBUG_OSC, "clientState = 0x%08x busy, exiting\n", clientState ));
return ERROR_SUCCESS;
} if ( clientState == NULL ) {
BinlPrintDbg((DEBUG_OSC, "clientState not found, exiting\n" ));
return ERROR_SUCCESS;
}
EnterCriticalSection(&clientState->CriticalSection);
clientState->CriticalSectionHeld = TRUE;
BinlPrintDbg((DEBUG_OSC, "Entering CS for clientState = 0x%08x\n", clientState ));
}
if (IsNegotiateMessage)
{
//
// This is an initial negotiate request.
//
Error = OscProcessNegotiate( RequestContext, clientState );
}
else if (IsAuthenicateMessage)
{
//
// This has the authenticate message.
//
Error = OscProcessAuthenticate( RequestContext, clientState );
}
else if (memcmp(loginMessage->Signature, RequestUnsignedSignature, 4) == 0)
{
//
// This is an unsigned request.
//
// Format is:
//
// "RQU"
// length (not including "RQU" and this)
// sequence number
// fragment count/total
// sign length
// sign
// data
//
Error = OscProcessRequestUnsigned( RequestContext, clientState );
}
else if (memcmp(loginMessage->Signature, RequestSignedSignature, 4) == 0)
{
//
// This is a signed request.
//
// Format is:
//
// "REQ"
// length (not including "REQ" and this)
// sequence number
// fragment count/total
// sign length
// sign
// data
//
Error = OscProcessRequestSigned( RequestContext, clientState );
}
else if (memcmp(loginMessage->Signature, SetupRequestSignature, 4) == 0)
{
//
// This is a request by a textmode setup.
//
// Format is defined in SPUDP_PACKET in oskpkt.h
//
Error = OscProcessSetupRequest( RequestContext, clientState );
}
else if (IsLogoffMessage)
{
//
// This is a logoff request. clientState has
// already been removed from the database.
//
Error = OscProcessLogoff( RequestContext, clientState );
}
else if (IsNetcardRequestMessage)
{
//
// This is a netcard request, which needs no client state.
//
Error = OscProcessNetcardRequest( RequestContext );
}
else if (IsHalRequestMessage)
{
//
// This is a hal request
//
Error = OscProcessHalRequest( RequestContext, clientState );
}
else
{
BinlPrintDbg(( DEBUG_OSC_ERROR, "Received unknown message!\n" ));
Error = ERROR_INVALID_FUNCTION;
}
if ( clientState ) {
clientState->LastUpdate = GetTickCount();
if (!IsNetcardRequestMessage) {
//
// We processed a packet, so if we get a first request for this
// client state (meaning the client has rebooted), we need to
// reinitialize it.
//
clientState->InitializeOnFirstRequest = TRUE;
++clientState->NegativeRefCount;
//
// FreeClientState will be TRUE if the two refcounts are equal.
//
FreeClientState = (BOOL)(clientState->PositiveRefCount == clientState->NegativeRefCount);
clientState->CriticalSectionHeld = FALSE;
LeaveCriticalSection(&clientState->CriticalSection);
BinlPrintDbg((DEBUG_OSC, "Leaving CS for clientState = 0x%08x\n", clientState ));
if (FreeClientState)
{
FreeClient(clientState);
}
}
}
return Error;
}
DWORD
OscVerifyLastResponseSize(
PCLIENT_STATE clientState
)
/*++
Routine Description:
This function checks that the LastResponse buffer is big enough,
if not it reallocates it. The algorithm used is to keep increasing
the size of the buffer, but not to try to shrink it.
Arguments:
clientState - The client state for the remote. clientState->
LastResponseLength should be set to the needed size.
Return Value:
Windows Error.
--*/
{
DWORD Error = ERROR_SUCCESS;
TraceFunc("OscVerifyLastResponseSize( )\n");
if (clientState->LastResponseAllocated < clientState->LastResponseLength) {
if (clientState->LastResponse) {
BinlFreeMemory(clientState->LastResponse);
}
clientState->LastResponse = BinlAllocateMemory(clientState->LastResponseLength);
if (clientState->LastResponse == NULL) {
clientState->LastResponseAllocated = 0;
BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not grow LastResponse to %ld bytes\n", clientState->LastResponseLength ));
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
} else {
clientState->LastResponseAllocated = clientState->LastResponseLength;
}
}
return Error;
}
DWORD
OscProcessNegotiate(
LPBINL_REQUEST_CONTEXT RequestContext,
PCLIENT_STATE clientState
)
/*++
Routine Description:
This function processes a negotiate message.
IT IS CALLED WITH clientState->CriticalSection HELD.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
clientState - The client state for the remote.
Return Value:
Windows Error.
--*/
{
DWORD Error;
SECURITY_STATUS SecStatus;
SecBufferDesc NegotiateDesc;
SecBuffer NegotiateBuffer;
SecBufferDesc ChallengeDesc;
SecBuffer ChallengeBuffer;
TimeStamp Lifetime;
LOGIN_PACKET UNALIGNED * loginMessage = (LOGIN_PACKET UNALIGNED *)RequestContext->ReceiveBuffer;
PUCHAR TempChallengeBuffer;
LOGIN_PACKET UNALIGNED * SendLoginMessage;
TraceFunc("OscProcessNegotiate( )\n");
//
// First free anything we have allocated for this client. We
// assume that each negotiate is a new request since the client
// may have rebooted, so we don't resend the last response.
//
if (clientState->AuthenticatedDCLdapHandle) {
// Reconnecting again. Use new credentials.
ldap_unbind(clientState->AuthenticatedDCLdapHandle);
clientState->AuthenticatedDCLdapHandle = NULL;
}
if (clientState->UserToken) {
CloseHandle(clientState->UserToken);
clientState->UserToken = NULL;
}
if (clientState->NegotiateProcessed) {
BinlPrintDbg(( DEBUG_OSC, "Got negotiate from client, reinitializing negotiate\n" ));
SecStatus = DeleteSecurityContext( &clientState->ServerContextHandle );
if ( SecStatus != STATUS_SUCCESS ) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "DeleteSecurityContext failed: %lx\n", SecStatus ));
// This seems to fail if a previous logon failed, so ignore errors
// return SecStatus;
}
clientState->NegotiateProcessed = FALSE;
}
if (clientState->AuthenticateProcessed) {
BinlPrintDbg(( DEBUG_OSC, "Got negotiate from client, reinitializing authenticate\n"));
clientState->AuthenticateProcessed = FALSE;
}
//
// Once the client has logged in we need to worry about resending screens
// if the client issues the request again. 0 is an invalid sequence
// number, so set this to 0 to ensure that any stale LastResponse is
// not resent.
//
clientState->LastSequenceNumber = 0;
//
// Get the ChallengeMessage (ServerSide)
//
ChallengeDesc.ulVersion = 0;
ChallengeDesc.cBuffers = 1;
ChallengeDesc.pBuffers = &ChallengeBuffer;
TempChallengeBuffer = (PUCHAR)BinlAllocateMemory(PackageInfo->cbMaxToken);
if (TempChallengeBuffer == NULL) {
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
BinlPrintDbg(( DEBUG_OSC, "Allocate TempChallengeBuffer failed\n"));
return Error;
}
ChallengeBuffer.pvBuffer = TempChallengeBuffer;
ChallengeBuffer.cbBuffer = PackageInfo->cbMaxToken;
ChallengeBuffer.BufferType = SECBUFFER_TOKEN;
NegotiateDesc.ulVersion = 0;
NegotiateDesc.cBuffers = 1;
NegotiateDesc.pBuffers = &NegotiateBuffer;
NegotiateBuffer.cbBuffer = loginMessage->Length;
NegotiateBuffer.BufferType = SECBUFFER_TOKEN | SECBUFFER_READONLY;
NegotiateBuffer.pvBuffer = loginMessage->Data;
SecStatus = AcceptSecurityContext(
&CredentialHandle,
NULL, // No Server context yet
&NegotiateDesc,
ISC_REQ_SEQUENCE_DETECT | ASC_REQ_ALLOW_NON_USER_LOGONS,
SECURITY_NATIVE_DREP,
&clientState->ServerContextHandle,
&ChallengeDesc,
&clientState->ContextAttributes,
&Lifetime );
if ( SecStatus != STATUS_SUCCESS ) {
if ( !NT_SUCCESS(SecStatus) ) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "AcceptSecurityContext (Challenge): %lx", SecStatus ));
BinlFreeMemory(TempChallengeBuffer);
return SecStatus;
}
}
//
// Send the challenge message back to the client.
//
clientState->LastResponseLength = ChallengeBuffer.cbBuffer + LOGIN_PACKET_DATA_OFFSET;
Error = OscVerifyLastResponseSize(clientState);
if (Error != ERROR_SUCCESS) {
BinlFreeMemory(TempChallengeBuffer);
return Error;
}
SendLoginMessage = (LOGIN_PACKET UNALIGNED *)(clientState->LastResponse);
memcpy(SendLoginMessage->Signature, ChallengeSignature, 4);
SendLoginMessage->Length = ChallengeBuffer.cbBuffer;
memcpy(SendLoginMessage->Data, ChallengeBuffer.pvBuffer, ChallengeBuffer.cbBuffer);
BinlFreeMemory(TempChallengeBuffer);
#ifdef TEST_FAILURE
if (FailFirstChallenge) {
BinlPrintDbg(( DEBUG_OSC, "NOT Sending CHL, %d bytes\n", clientState->LastResponseLength));
FailFirstChallenge = FALSE;
Error = ERROR_SUCCESS;
} else
#endif
Error = SendUdpMessage(RequestContext, clientState, FALSE, FALSE);
if (Error != ERROR_SUCCESS) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not send CHAL message %d\n", Error));
}
clientState->NegotiateProcessed = TRUE;
return Error;
}
DWORD
OscProcessAuthenticate(
LPBINL_REQUEST_CONTEXT RequestContext,
PCLIENT_STATE clientState
)
/*++
Routine Description:
This function processes an authenticate message.
IT IS CALLED WITH clientState->CriticalSection HELD.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
clientState - The client state for the remote.
Return Value:
Windows Error.
--*/
{
DWORD Error;
SECURITY_STATUS SecStatus;
SecBufferDesc AuthenticateDesc;
SecBuffer AuthenticateBuffer;
TimeStamp Lifetime;
LOGIN_PACKET UNALIGNED * loginMessage = (LOGIN_PACKET UNALIGNED *)RequestContext->ReceiveBuffer;
ULONG FlipServerListLength;
LOGIN_PACKET UNALIGNED * SendLoginMessage;
UCHAR TempServerList[MAX_FLIP_SERVER_COUNT*4];
TraceFunc("OscProcessAuthenticate( )\n");
//
// Make sure we have gotten a negotiate.
//
if (!clientState->NegotiateProcessed) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "Received AUTH without NEG?\n"));
return ERROR_INVALID_DATA;
}
//
// If we have already responsed to this, just resend with the
// same status.
//
if (clientState->AuthenticateProcessed) {
SecStatus = clientState->AuthenticateStatus;
BinlPrintDbg(( DEBUG_OSC, "Got authenticate from client, resending\n"));
} else {
//
// Finally authenticate the user (ServerSide)
//
AuthenticateDesc.ulVersion = 0;
AuthenticateDesc.cBuffers = 1;
AuthenticateDesc.pBuffers = &AuthenticateBuffer;
AuthenticateBuffer.cbBuffer = loginMessage->Length;
AuthenticateBuffer.BufferType = SECBUFFER_TOKEN | SECBUFFER_READONLY;
AuthenticateBuffer.pvBuffer = loginMessage->Data;
SecStatus = AcceptSecurityContext(
NULL,
&clientState->ServerContextHandle,
&AuthenticateDesc,
ASC_REQ_ALLOW_NON_USER_LOGONS,
SECURITY_NATIVE_DREP,
&clientState->ServerContextHandle,
NULL,
&clientState->ContextAttributes,
&Lifetime );
FlipServerListLength = 0;
if ( SecStatus != STATUS_SUCCESS ) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "AcceptSecurityContext (Challenge): %lx\n", SecStatus ));
} else {
#if 0
//
// Impersonate the client.
//
SecStatus = ImpersonateSecurityContext( &clientState->ServerContextHandle );
if ( SecStatus != STATUS_SUCCESS ) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "ImpersonateSecurityContext: %lx\n", SecStatus ));
if ( !NT_SUCCESS(SecStatus) ) {
return SecStatus;
}
}
#endif
#if 0
//
// If the client is not indicating that he has been flipped, then
// look for a flip list. The client indicates that he has been
// flipped by using a signature of "AU2" instead of "AUT".
//
if (memcmp(loginMessage->Signature, AuthenticateFlippedSignature, 4) != 0) {
OscGetFlipServerList(
TempServerList,
&FlipServerListLength
);
}
#endif
#if 0
//
// Revert to ourselves.
//
SecStatus = RevertSecurityContext( &clientState->ServerContextHandle );
if ( SecStatus != STATUS_SUCCESS ) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "RevertSecurityContext: %lx\n", SecStatus ));
if ( !NT_SUCCESS(SecStatus) ) {
return SecStatus;
}
}
#endif
}
//
// Send the result back to the client. If there is a flip server
// list, it has already been stored after g_LoginMessage->Status and
// FlipServerListLength reflects its length.
//
clientState->LastResponseLength = LOGIN_PACKET_DATA_OFFSET + sizeof(ULONG) + FlipServerListLength;
Error = OscVerifyLastResponseSize(clientState);
if (Error != ERROR_SUCCESS) {
return Error;
}
SendLoginMessage = (LOGIN_PACKET UNALIGNED *)(clientState->LastResponse);
memcpy(SendLoginMessage->Signature, ResultSignature, 4);
SendLoginMessage->Length = 4 + FlipServerListLength;
SendLoginMessage->Status = SecStatus;
memcpy((PUCHAR)(&SendLoginMessage->Status) + sizeof(ULONG), TempServerList, FlipServerListLength);
clientState->AuthenticateProcessed = TRUE;
clientState->AuthenticateStatus = SecStatus;
}
#ifdef TEST_FAILURE
if (FailFirstResult) {
BinlPrintDbg(( DEBUG_OSC, "NOT Sending OK, %d bytes\n", clientState->LastResponseLength));
FailFirstResult = FALSE;
Error = ERROR_SUCCESS;
} else
#endif
Error = SendUdpMessage(RequestContext, clientState, FALSE, FALSE);
if (Error != ERROR_SUCCESS) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not send OK message %d\n", Error));
}
return Error;
}
//
//
//
DWORD
OscProcessScreenArguments(
LPBINL_REQUEST_CONTEXT RequestContext,
PCLIENT_STATE clientState,
PUCHAR *NameLoc
)
{
SIGNED_PACKET UNALIGNED * signedMessage = (SIGNED_PACKET UNALIGNED *)RequestContext->ReceiveBuffer;
PCHAR packetEnd = signedMessage->Data + signedMessage->Length - SIGNED_PACKET_EMPTY_LENGTH;
DWORD Error = ERROR_SUCCESS;
PCHAR Args;
BOOLEAN GuidSent = FALSE;
PDS_NAME_RESULTA pResults = NULL;
TraceFunc("OscProcessScreenArguments( )\n");
//
// Find out the screen name and how long it is
//
*NameLoc = signedMessage->Data;
Args = FindNext( *NameLoc, '\n', packetEnd );
if ( Args == NULL )
{
BinlPrintDbg((DEBUG_OSC, "Could not determine the screen name."));
*NameLoc = NULL;
Error = ERROR_INVALID_DATA;
}
else
{
*Args = '\0'; // terminate
//
// The name can't have a ".." part in it.
//
if ((memcmp(*NameLoc, "..\\", 3) == 0) ||
(strstr(*NameLoc, "\\..\\") != NULL)) {
BinlPrintDbg((DEBUG_OSC, "Name <%s> has .. in it.", *NameLoc));
*NameLoc = NULL;
Error = ERROR_INVALID_DATA;
} else {
Args++; // skip the NULL char
//
// Munge any variables that might have been passed back with it.
// They come in like this:
// NextScreenName\nvariable=response\n....\nvariable=response\n\0
//
// If no arguments, it comes like:
// NextScreenName\n\0
//
while ( *Args )
{
PCHAR NextArg;
PCHAR Response;
PCHAR EncodedResponse;
//
// Find the variable name
//
Response = FindNext( Args, '=', packetEnd );
if ( Response == NULL )
{
BinlPrintDbg((DEBUG_OSC, "Could not find <variable>.\n" ));
Error = ERROR_INVALID_DATA;
break;
}
//
// Find the variable response value
//
NextArg = FindNext( Response, '\n', packetEnd );
if ( NextArg == NULL )
{
BinlPrintDbg((DEBUG_OSC, "Could not find <response>.\n" ));
Error = ERROR_INVALID_DATA;
break;
}
//
// Terminate strings
//
*NextArg = '\0';
*Response = '\0';
//
// Point to response
//
Response++;
NextArg++;
//
//
// Add them to the Variable table.
// If the variable starts with a '*', encode it first.
//
if (Args[0] == '*') {
Error = OscRunEncode(clientState, Response, &EncodedResponse);
if (Error == ERROR_SUCCESS) {
Error = OscAddVariableA( clientState, Args, EncodedResponse );
BinlFreeMemory(EncodedResponse);
}
} else {
//
// Check whether this is the GUID variable. If it is, we'll
// need to do some special processing later. See below.
//
if ( Response[0] != '\0'
&& StrCmpIA( Args, "GUID" ) == 0 )
{
GuidSent = TRUE;
}
// HACKHACK: Special case "MACHINEOU" for translation from canonical form
// to 1779 so we can use it later.
if ( Response[0] != '\0'
&& StrCmpIA( Args, "MACHINEOU" ) == 0 )
{
BOOL FirstTime = TRUE;
invalid_ds_handle:
// Make sure the handle is initialized
if ( BinlOscClientDSHandle == NULL )
{
HANDLE hTemp;
Error = DsBind(NULL, NULL, &hTemp);
InterlockedCompareExchangePointer( (void**)&BinlOscClientDSHandle, hTemp, NULL );
if ( BinlOscClientDSHandle != hTemp )
{
DsUnBind( &hTemp );
}
}
if ( Error == ERROR_SUCCESS )
{
Error = DsCrackNamesA( BinlOscClientDSHandle,
DS_NAME_NO_FLAGS,
DS_UNKNOWN_NAME,
DS_FQDN_1779_NAME,
1,
&Response,
&pResults );
BinlAssertMsg( Error == ERROR_SUCCESS, "Error in DsCrackNames\n" );
if ( Error == ERROR_SUCCESS ) {
if ( pResults->cItems == 1
&& pResults->rItems[0].status == DS_NAME_NO_ERROR
&& pResults->rItems[0].pName ) { // paranoid
Response = pResults->rItems[0].pName;
} else {
//
// check if we have an "external trust"
// problem. if that's the case, then we
// need to bind to a DC in the domain we
// care about and retrieve the information
// from there.
//
if (pResults->cItems == 1 && pResults->rItems[0].status == DS_NAME_ERROR_DOMAIN_ONLY) {
HANDLE hDC;
Error = MyGetDcHandle(clientState, pResults->rItems[0].pDomain,&hDC);
if (Error == ERROR_SUCCESS) {
DsFreeNameResultA( pResults );
pResults = NULL;
Error = DsCrackNamesA(
hDC,
DS_NAME_NO_FLAGS,
DS_UNKNOWN_NAME,
DS_FQDN_1779_NAME,
1,
&Response,
&pResults );
DsUnBindA(&hDC);
if (Error != ERROR_SUCCESS) {
BinlPrintDbg((
DEBUG_OSC_ERROR,
"DsCrackNames failed, ec = %d.\n",
Error ));
}
if (Error == ERROR_SUCCESS ) {
if ( pResults->cItems == 1
&& pResults->rItems[0].status == DS_NAME_NO_ERROR
&& pResults->rItems[0].pName ) { // paranoid
Response = pResults->rItems[0].pName;
} else {
BinlPrintDbg((
DEBUG_OSC,
"pResults->rItems[0].status = %u\n",
pResults->rItems[0].status ));
Error = pResults->rItems[0].status;
}
}
}
}
}
}
}
// DsCrackNamesA seems to return either ERROR_INVALID_HANDLE or RPC_S_CALL_FAILED
// if the server is unavailable. If we get either of these, assume the handle is
// stale and needs to be refreshed before we try again.
if ( (Error == ERROR_INVALID_HANDLE) ||
(Error == RPC_S_CALL_FAILED) ) {
HANDLE hTemp;
BinlPrintDbg((
DEBUG_ERRORS,
"DsCrackNames returned a semi-expected error code. Need to refresh handle.\n"
));
hTemp = InterlockedExchangePointer( (void**)&BinlOscClientDSHandle, NULL);
DsUnBind( &hTemp );
if (FirstTime) {
FirstTime = FALSE;
goto invalid_ds_handle;
}
}
if ( Error != ERROR_SUCCESS ) {
OscAddVariableA( clientState, "SUBERROR", Response );
return ERROR_BINL_UNABLE_TO_CONVERT_TO_DN;
}
}
Error = OscAddVariableA( clientState, Args, Response );
}
if ( Error != ERROR_SUCCESS ) {
BinlPrintDbg(( DEBUG_OSC_ERROR,
"!!Error 0x%08x - Could not add argument '%s' = '%s'\n",
Error, Args, Response));
break;
}
BinlPrintDbg(( DEBUG_OSC, "Got argument '%s' = '%s'\n", Args, Response));
if (pResults) {
DsFreeNameResultA( pResults );
pResults = NULL;
}
Args = NextArg;
}
}
}
//
// If the GUID was sent in this message, check to see if it's all zeroes
// or all FFs. If it is, substitute a MAC address-based GUID. This is the
// same thing that we do when we receive DHCP packets with bogus GUIDs.
// We need to do this here, otherwise we end up adding the client to the
// DS with a bogus GUID. It seems that PXE 2.0 clients always send the
// GUID option even when they have no real machine GUID.
//
// Note that we can't do this substitution in the loop above because we
// might not have process the MAC address variable yet. (Currently
// OSChooser always puts the MAC address ahead of the GUID in the packet,
// but we don't want to depend on that.)
//
if ( GuidSent ) {
//
// A GUID was sent. Retrieve it. It should be there, but if it
// isn't, just bail.
//
LPSTR guid = OscFindVariableA( clientState, "GUID" );
DWORD length;
if ( (guid != NULL) && ((length = strlen(guid)) != 0) ) {
//
// Check the GUID for all zeroes or all FFs.
//
if ( (strspn( guid, "0" ) == length) ||
(strspn( guid, "F" ) == length) ) {
//
// The GUID is bogus. Substitute a MAC address GUID.
// We should have the MAC address at this point, but
// if we don't, just bail.
//
LPSTR mac = OscFindVariableA( clientState, "MAC" );
if ( mac != NULL ) {
//
// The generated GUID is zeroes followed by the
// MAC address. (In other words, the MAC address
// is right-justified in a 32-character string.)
//
UCHAR guidString[(BINL_GUID_LENGTH * 2) + 1];
length = strlen(mac);
if ( length > BINL_GUID_LENGTH * 2 ) {
//
// We have an unusually long MAC address.
// Use the last 32 characters.
//
mac = mac + length - (BINL_GUID_LENGTH * 2);
length = BINL_GUID_LENGTH * 2;
}
if ( length < BINL_GUID_LENGTH * 2 ) {
//
// Write zeroes in front of the MAC address.
//
memset( guidString, '0', (BINL_GUID_LENGTH * 2) - length );
}
//
// Copy the MAC address into the GUID (including the
// null terminator).
//
strcpy( guidString + (BINL_GUID_LENGTH * 2) - length, mac );
//
// Set the new GUID.
//
OscAddVariableA( clientState, "GUID", guidString );
}
}
}
}
return Error;
}
//
//
//
DWORD
OscProcessRequestUnsigned(
LPBINL_REQUEST_CONTEXT RequestContext,
PCLIENT_STATE clientState
)
/*++
Routine Description:
This function processes a request message.
IT IS CALLED WITH clientState->CriticalSection HELD.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
clientState - The client state for the remote.
Return Value:
Windows Error.
--*/
{
DWORD Error = ERROR_SUCCESS;
SIGNED_PACKET UNALIGNED * signedMessage = (SIGNED_PACKET UNALIGNED *)RequestContext->ReceiveBuffer;
PCHAR RspMessage = NULL;
ULONG RspMessageLength = 0;
ULONG k;
WCHAR FilePath[ MAX_PATH ];
HANDLE hfile;
PCHAR NameLoc;
TraceFunc("OscProcessRequestUnsigned( )\n");
//
// All clients start with at least one unsigned request. When we get an
// unsigned request, the client may have rebooted and be asking for a
// different screen with the same sequence number. To avoid having
// to check for this, we don't bother resending unsigned screens. We
// do save the incoming sequence number since we use that for sending
// the response.
//
clientState->LastSequenceNumber = signedMessage->SequenceNumber;
//
// We have a valid request for a screen, process incoming arguments.
//
Error = OscProcessScreenArguments( RequestContext, clientState, &NameLoc );
if ( Error != ERROR_SUCCESS ) {
goto SendScreen;
} else {
//
// If NULL message, then send down the welcome screen.
//
if ( NameLoc == NULL || *NameLoc == '\0' )
{
if ( _snwprintf( FilePath,
sizeof(FilePath) / sizeof(FilePath[0]),
L"%ws\\OSChooser\\%s",
IntelliMirrorPathW,
DEFAULT_SCREEN_NAME
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
goto SendScreen;
}
BinlPrint(( DEBUG_OSC, "NULL screen name so we are retrieving the Welcome Screen.\n"));
//
// This is the first request a client makes, which is a good
// time to clean up the client state, unless we don't need to
// (because the client state is new).
//
if (clientState->InitializeOnFirstRequest) {
if (!OscInitializeClientVariables(clientState)) {
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
goto SendScreen;
}
}
}
else
{
WCHAR NameLocW[ MAX_PATH ];
ULONG NameLocLength;
//
// Create path to possible OSC file. Should look something like:
// "D:\RemoteInstall\OSChooser\English\NameLoc.OSC"
BinlAssert( NameLoc );
NameLocLength = strlen(NameLoc) + 1;
if (NameLocLength > MAX_PATH) {
NameLocLength = MAX_PATH-1;
NameLocW[ MAX_PATH-1 ] = L'\0';
}
mbstowcs( NameLocW, NameLoc, NameLocLength );
if ( _snwprintf( FilePath,
sizeof(FilePath) / sizeof(FilePath[0]),
L"%ws\\OSChooser\\%ws\\%ws.OSC",
IntelliMirrorPathW,
OscFindVariableW( clientState, "LANGUAGE" ),
NameLocW
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
goto SendScreen;
}
}
BinlPrint(( DEBUG_OSC, "Retrieving screen file: '%ws'\n", FilePath));
}
//
// If we find the file, load it into memory.
//
hfile = CreateFile( FilePath, 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 )
{
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
}
else
{
RspMessageLength = 0;
RspMessage[0] = '\0';
while ( dwRead != FileSize )
{
BOOL b;
DWORD dw;
b = ReadFile( hfile, &RspMessage[dwRead], FileSize - dwRead, &dw, NULL );
if (!b)
{
BinlPrintDbg(( DEBUG_OSC_ERROR, "Error reading screen file: Seek=%u, Size=%u, File=%ws\n",
dwRead, FileSize - dwRead, FilePath ));
Error = GetLastError( );
break;
}
dwRead += dw;
}
RspMessageLength = dwRead;
}
}
else
{
BinlPrintDbg((DEBUG_OSC, "!!Error - Could not determine file size.\n"));
Error = GetLastError();
}
CloseHandle( hfile );
}
else
{
BinlPrintDbg((DEBUG_OSC, "!!Error - Did not find screen file: '%ws'\n", FilePath));
Error = GetLastError();
OscAddVariableW( clientState, "SUBERROR", FilePath );
}
SendScreen:
//
// If there were any errors, switch to the error screen.
//
if ( Error != ERROR_SUCCESS )
{
//
// Send down message about the failure.
//
if ( RspMessage )
{
BinlFreeMemory( RspMessage );
RspMessage = NULL; // paranoid
}
BinlPrintDbg((DEBUG_OSC, "!!Error - Sending error screen back to client. Server error=0x%08x\n", Error));
Error = GenerateErrorScreen( &RspMessage,
&RspMessageLength,
Error,
clientState );
BinlAssert( Error == ERROR_SUCCESS );
if ( Error != ERROR_SUCCESS ) {
goto Cleanup;
}
//
// Require them to re-login.
//
// clientState->AuthenticateProcessed = FALSE;
}
if (RspMessage) {
// Do replacements for dynamic screens or error screens
SearchAndReplace( clientState->Variables,
&RspMessage,
clientState->nVariables,
RspMessageLength,
0);
RspMessageLength = strlen( RspMessage ) + 1;
RspMessage[RspMessageLength-1] = '\0'; // paranoid
}
//
// Send out a signed response
//
BinlAssert( RspMessage );
// BinlPrint((DEBUG_OSC, "Sending Unsigned:\n%s\n", RspMessage));
OscSendUnsignedMessage( RequestContext, clientState, RspMessage, RspMessageLength );
Cleanup:
//
// Free any memory the we allocated for the screen.
//
if ( RspMessage ) {
BinlFreeMemory( RspMessage );
}
return Error;
}
//
//
//
OscInstallClient(
LPBINL_REQUEST_CONTEXT RequestContext,
PCLIENT_STATE clientState,
PCREATE_DATA createData )
{
DWORD Error;
//
// Client wants to create a machine account and run setup.
//
Error = OscSetupClient( clientState, TRUE );
if ( Error == ERROR_SUCCESS )
{
//
// Only create the account if the setup went OK.
//
RetryCreateAccount:
Error = OscCreateAccount( clientState, createData );
switch ( Error )
{
case ERROR_BINL_DUPLICATE_MACHINE_NAME_FOUND:
if ( clientState->fAutomaticMachineName ) {
// Try generating another name
Error = GenerateMachineName( clientState );
if ( Error == ERROR_SUCCESS ) {
goto RetryCreateAccount;
}
BinlPrint(( DEBUG_OSC_ERROR, "!!Error 0x%08x - Failed to generate machine name\n" ));
}
break;
default:
break;
}
}
#ifdef SET_ACLS_ON_CLIENT_DIRS
if ( Error == ERROR_SUCCESS )
{
//
// Change the ACL permission of the client machine's root directory
//
Error = OscSetClientDirectoryPermissions( clientState );
}
#endif // SET_ACLS_ON_CLIENT_DIRS
if ( Error != ERROR_SUCCESS )
{
OscUndoSetupClient( clientState );
BinlPrint((DEBUG_OSC,
"!!Error 0x%08x - Error setting up the client for Setup.\n",
Error ));
}
return Error;
}
//
// OscGetCreateData( )
//
// Queries the DS to get the information required to create
// the CreateData secret and then builds one.
//
DWORD
OscGetCreateData(
LPBINL_REQUEST_CONTEXT RequestContext,
PCLIENT_STATE clientState,
PCREATE_DATA CreateData)
{
DWORD Error = ERROR_BINL_INVALID_BINL_CLIENT;
PCHAR pGuid;
PWCHAR pGuidW;
UCHAR Guid[ BINL_GUID_LENGTH ];
WCHAR BootFilePath[MAX_PATH];
PMACHINE_INFO pMachineInfo = NULL;
WCHAR MachinePassword[LM20_PWLEN + 1];
WCHAR SifPath[MAX_PATH];
WCHAR SifFile[(BINL_GUID_LENGTH*2)+(sizeof(TEMP_DIRECTORY)/sizeof(WCHAR))+6];
DWORD FileAttributes;
ULONG MachinePasswordLength;
DWORD dwRequestedInfo = MI_NAME | MI_BOOTFILENAME | MI_MACHINEDN | MI_SAMNAME;
PWCHAR pszOU;
USHORT SystemArchitecture;
DWORD OldFlags;
ULONG lenIntelliMirror = wcslen(IntelliMirrorPathW) + 1;
pGuid = OscFindVariableA( clientState, "GUID" );
if ( pGuid[0] == '\0' ) {
BinlPrintDbg((DEBUG_OSC_ERROR, "OscGetCreateData: could not find GUID" ));
OscAddVariableA( clientState, "SUBERROR", "GUID" );
Error = ERROR_BINL_MISSING_VARIABLE;
goto e0;
}
pGuidW = OscFindVariableW( clientState, "GUID" );
if ( pGuidW[0] == L'\0' ) {
BinlPrintDbg((DEBUG_OSC_ERROR, "OscGetCreateData: could not find GUID (unicode)" ));
OscAddVariableA( clientState, "SUBERROR", "GUID" );
Error = ERROR_BINL_MISSING_VARIABLE;
goto e0;
}
Error = OscGuidToBytes( pGuid, Guid );
if ( Error != ERROR_SUCCESS ) {
BinlPrintDbg((DEBUG_OSC_ERROR, "OscGetCreateData: OscGuidToBytes failed" ));
goto e0;
}
SystemArchitecture = OscPlatformToArchitecture(clientState);
Error = GetBootParameters( Guid,
&pMachineInfo,
dwRequestedInfo,
SystemArchitecture,
FALSE );
if ( Error != ERROR_SUCCESS ) {
BinlPrintDbg((DEBUG_OSC_ERROR, "OscGetCreateData: GetBootParameters failed %lx", Error ));
goto e0;
}
BinlAssertMsg(( pMachineInfo->dwFlags & dwRequestedInfo )== dwRequestedInfo, "Missing info." );
//
// The SIF file is named GUID.sif, and it must exist (the SIF
// files go in a tmp directory so an admin may have cleaned it
// out).
//
wsprintf( SifFile, L"%ws\\%ws.sif", TEMP_DIRECTORY, pGuidW );
if ( _snwprintf( SifPath,
sizeof(SifPath) / sizeof(SifPath[0]),
L"%ws\\%ws",
IntelliMirrorPathW,
SifFile
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
goto e0;
}
FileAttributes = GetFileAttributes(SifPath);
if (FileAttributes == 0xFFFFFFFF) {
BinlPrintDbg((DEBUG_OSC_ERROR, "OscGetCreateData: SifFile not found" ));
OscAddVariableW( clientState, "SUBERROR", SifPath );
Error = ERROR_BINL_SIFFILE_NOT_FOUND;
goto e0;
}
//
// The bootfile stored in pMachineInfo->BootFileName will point to
// oschooser, so we parse the SIF to find the setupldr boot file.
//
BootFilePath[0] = L'\0';
GetPrivateProfileString( OSCHOOSER_SIF_SECTIONW,
L"LaunchFile",
BootFilePath, // default
BootFilePath,
MAX_PATH,
SifPath );
//
// This might fail to add if it is too long due to bogus data in the .sif.
//
Error = OscAddVariableW( clientState, "BOOTFILE", BootFilePath );
if ( Error != ERROR_SUCCESS ) {
goto e0;
}
//
// This might fail to add if it is too long due to bogus data in the DS.
//
if (pMachineInfo->dwFlags & MI_SIFFILENAME_ALLOC) {
Error = OscAddVariableW( clientState, "FORCESIFFILE", pMachineInfo->ForcedSifFileName );
if ( Error != ERROR_SUCCESS ) {
goto e0;
}
}
//
// These next two shouldn't fail unless someone has modified the DS by
// hand, since they are checked when the accounts are generated, but
// best to be safe.
//
Error = OscAddVariableW( clientState, "NETBIOSNAME", pMachineInfo->SamName );
if ( Error != ERROR_SUCCESS) {
goto e0;
}
Error = OscAddVariableW( clientState, "MACHINENAME", pMachineInfo->Name );
if ( Error != ERROR_SUCCESS) {
goto e0;
}
//
// Shouldn't fail unless the IntelliMirrorPathW is too long.
//
Error = OscAddVariableW( clientState, "SIFFILE", &SifPath[lenIntelliMirror] );
if ( Error != ERROR_SUCCESS) {
goto e0;
}
pszOU = wcschr( pMachineInfo->MachineDN, L',' );
if (pszOU)
{
pszOU++;
Error = OscAddVariableW( clientState, "MACHINEOU", pszOU );
if ( Error != ERROR_SUCCESS ) {
goto e0;
}
}
Error = OscSetupMachinePassword( clientState, SifPath );
if (Error != ERROR_SUCCESS) {
goto e0;
}
//
// save off the old flags so we can update the machine account information
//
OldFlags = pMachineInfo->dwFlags;
pMachineInfo->dwFlags = MI_PASSWORD | MI_MACHINEDN;
pMachineInfo->Password = clientState->MachineAccountPassword;
pMachineInfo->PasswordLength = clientState->MachineAccountPasswordLength;
Error = UpdateAccount( clientState, pMachineInfo, FALSE );
if ( Error != LDAP_SUCCESS ) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "!! LdapError 0x%08x - UpdateAccount( ) failed.\n", Error ));
goto e0;
}
pMachineInfo->dwFlags |= OldFlags; // add them back
Error = OscConstructSecret(
clientState,
clientState->MachineAccountPassword,
clientState->MachineAccountPasswordLength,
CreateData );
if ( Error != ERROR_SUCCESS ) {
BinlPrintDbg((DEBUG_OSC_ERROR, "OscGetCreateData: OscConstructSecret failed %lx", Error ));
goto e0;
}
e0:
if (pMachineInfo) {
BinlDoneWithCacheEntry( pMachineInfo, FALSE );
}
return Error;
}
//
//
//
DWORD
OscProcessRequestSigned(
LPBINL_REQUEST_CONTEXT RequestContext,
PCLIENT_STATE clientState
)
/*++
Routine Description:
This function processes a request message.
IT IS CALLED WITH clientState->CriticalSection HELD.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
clientState - The client state for the remote.
Return Value:
Windows Error.
--*/
{
DWORD Error = ERROR_SUCCESS;
SIGNED_PACKET UNALIGNED * signedMessage = (SIGNED_PACKET UNALIGNED *)RequestContext->ReceiveBuffer;
PCHAR RspMessage = NULL;
ULONG RspMessageLength = 0;
PCHAR RspBinaryData = NULL;
ULONG RspBinaryDataLength = 0;
ULONG k;
DWORD dwErr;
WCHAR FilePath[ MAX_PATH ];
CHAR TmpName[ 16 ];
HANDLE hfile;
PCHAR NameLoc;
LPSTR psz;
TraceFunc("OscProcessRequestSigned( )\n");
if ( clientState->AuthenticateProcessed == FALSE )
{
SIGNED_PACKET UNALIGNED * SendSignedMessage;
//
// This may happen if we reboot the server -- send an ERRS
// and the client should reconnect OK.
//
BinlPrintDbg(( DEBUG_OSC_ERROR, "Got REQ but not authenticated, sending UNR\n" ));
clientState->LastResponseLength = SIGNED_PACKET_ERROR_LENGTH;
Error = OscVerifyLastResponseSize(clientState);
if (Error != ERROR_SUCCESS) {
return Error;
}
SendSignedMessage =
(SIGNED_PACKET UNALIGNED *)(clientState->LastResponse);
memcpy(SendSignedMessage->Signature, UnrecognizedClientSignature, 4);
SendSignedMessage->Length = 4;
SendSignedMessage->SequenceNumber = signedMessage->SequenceNumber;
Error = SendUdpMessage( RequestContext, clientState, FALSE, FALSE );
return Error;
}
if ( signedMessage->SequenceNumber == clientState->LastSequenceNumber )
{
//
// Is the signature the same as the last one we sent out? If so,
// then just resend (we'll do that after we leave this if
// statement).
//
if ( clientState->LastResponse )
{
BinlPrintDbg(( DEBUG_OSC, "Resending last message\n" ));
}
else
{
//
// We were unable to save the last response -- we are hosed!
//
BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not resend last message\n" ));
return ERROR_NOT_ENOUGH_SERVER_MEMORY;
}
//
// Resend last response
//
return SendUdpMessage( RequestContext,
clientState,
TRUE,
TRUE );
}
else if ( signedMessage->SequenceNumber != ((clientState->LastSequenceNumber % 0x2000) + 1))
{
//
// It's not the next message - ignore it.
//
BinlPrintDbg(( DEBUG_OSC, "got bogus sequence number: Got %u. Expected: %u\n",
signedMessage->SequenceNumber , ((clientState->LastSequenceNumber % 0x2000) + 1)));
return ERROR_INVALID_DATA;
}
//
// Advance the clientState sequence counter
//
clientState->LastSequenceNumber = signedMessage->SequenceNumber;
//
// Verify packet signature
//
Error = OscVerifySignature( clientState, signedMessage );
if ( Error != STATUS_SUCCESS )
return Error;
//
// We have a valid request for a screen.
//
if (signedMessage->Length == (SIGNED_PACKET_EMPTY_LENGTH))
{
//
// An empty packet indicates to us to send down the Welcome screen.
//
if ( _snwprintf( FilePath,
sizeof(FilePath) / sizeof(FilePath[0]),
L"%ws\\OSChooser\\%s",
IntelliMirrorPathW,
DEFAULT_SCREEN_NAME
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
} else {
BinlPrintDbg(( DEBUG_OSC, "Retrieving Welcome Screen: %ws\n", FilePath));
}
}
else
{
//
// Process incoming arguments and get the next screen's name
//
Error = OscProcessScreenArguments( RequestContext, clientState, &NameLoc );
GrabAnotherScreen:
//
// Process special responses.
//
// INSTALL: Indicates that user wants to create a new machine.
// RESTART: Indicates the client wants to restart setup.
//
if ( Error == ERROR_SUCCESS )
{
PWCHAR pCheckDomain = OscFindVariableW( clientState, "CHECKDOMAIN" );
if (pCheckDomain[0] == L'1')
{
//
// After the first login, the client will set this to
// tell us to verify that the domain name used in this
// context matches what the user requested. This will
// prevent an invalid domain from being let through
// due to SSPI using the default domain in that case.
//
BOOLEAN failedCheck = FALSE;
DWORD impersonateError;
PWCHAR pUserDomain = OscFindVariableW( clientState, "USERDOMAIN" );
if ( pUserDomain[0] != L'\0' )
{
SecPkgCredentials_Names names;
SECURITY_STATUS secStatus;
PWCHAR backslash;
PWSTR netbiosUserDomain = NULL;
DWORD Flags;
secStatus = QueryContextAttributes(
&clientState->ServerContextHandle,
SECPKG_CRED_ATTR_NAMES,
&names);
if (secStatus == STATUS_SUCCESS) {
//
// The part up to the '\\' is the domain.
//
backslash = wcschr(names.sUserName, L'\\');
if (backslash != NULL) {
*backslash = L'\0';
}
if (ERROR_SUCCESS != GetDomainNetBIOSName(pUserDomain,&netbiosUserDomain)) {
Error = ERROR_BINL_USER_LOGIN_FAILED;
failedCheck = TRUE;
} else {
//
// If the domain names don't match, then the login
// succeeded due to the security package trying the
// server's domain name. We don't want to let those
// through since the LogonUser call will eventually
// fail. So fail right here.
//
if (_wcsicmp(netbiosUserDomain, names.sUserName) != 0) {
Error = ERROR_BINL_USER_LOGIN_FAILED;
failedCheck = TRUE;
}
}
if (netbiosUserDomain) {
BinlFreeMemory( netbiosUserDomain );
}
FreeContextBuffer(names.sUserName);
}
}
//
// If we haven't already failed, try to impersonate and
// revert. This verifies that the user has batch logon
// permission.
//
if (!failedCheck) {
impersonateError = OscImpersonate(clientState);
if (impersonateError != ERROR_SUCCESS) {
if ( impersonateError == ERROR_LOGON_TYPE_NOT_GRANTED )
{
BinlPrint(( DEBUG_OSC_ERROR,
"!!Error 0x%08x - CheckDomain: Batch Logon type not granted\n",
impersonateError ));
Error = ERROR_BINL_LOGON_TYPE_NOT_GRANTED;
}
else
{
BinlPrint(( DEBUG_OSC_ERROR, "!!Error 0x%08x - CheckDomain: login failed\n", impersonateError ));
Error = ERROR_BINL_USER_LOGIN_FAILED;
}
} else {
OscRevert(clientState);
}
}
//
// Once we've done this once, don't need to do it again.
//
if ( OscAddVariableW( clientState, "CHECKDOMAIN", L"0" ) != ERROR_SUCCESS )
{
// don't overwrite the "Error" value unless we need to
Error = OscAddVariableW( clientState, "CHECKDOMAIN", L"0" );
}
}
if ( lstrcmpiA( NameLoc, "INSTALL" ) == 0 )
{
RspBinaryData = BinlAllocateMemory( sizeof(CREATE_DATA) );
if (RspBinaryData == NULL) {
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
goto SendResponse; // skip the rest and just send it.
}
RspBinaryDataLength = sizeof(CREATE_DATA);
//
// This will fill in RspBinaryData with the CREATE_DATA
// information.
//
Error = OscInstallClient( RequestContext, clientState, (PCREATE_DATA)RspBinaryData );
}
else if ( lstrcmpiA( NameLoc, "LAUNCH" ) == 0 )
{
//
// Launch the "LaunchFile" indicated in a SIF. Mainly used to
// launch tools and other real-mode utilities. In the case of
// the command console, we need to copy this sif and fix it up
// so that it looks like a textmode setup.
//
PCHAR pTemplatePath = OscFindVariableA( clientState, "SIF" );
PCREATE_DATA pCreate;
if ( pTemplatePath[0] == '\0' ) {
BinlPrint(( DEBUG_OSC_ERROR, "Missing SIF variable\n" ));
OscAddVariableA( clientState, "SUBERROR", "SIF" );
Error = ERROR_BINL_MISSING_VARIABLE;
goto SendResponse; // skip the rest and just send it.
}
if (RspMessage != NULL) {
BinlFreeMemory(RspMessage);
}
RspMessageLength = strlen("LAUNCH") + 1 + sizeof(CREATE_DATA);
RspMessage = BinlAllocateMemory( RspMessageLength );
if ( RspMessage == NULL ) {
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
goto SendResponse; // skip the rest and just send it.
}
strcpy(RspMessage, "LAUNCH");
RspBinaryData = BinlAllocateMemory( sizeof(CREATE_DATA) );
if (RspBinaryData == NULL) {
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
goto SendResponse; // skip the rest and just send it.
}
ZeroMemory( RspBinaryData, sizeof(CREATE_DATA) );
pCreate = (PCREATE_DATA)RspBinaryData;
pCreate->VersionNumber = OSC_CREATE_DATA_VERSION;
if (OscSifIsCmdConsA(pTemplatePath)) {
//
// Setup the client for an install
//
Error = OscSetupClient( clientState, FALSE );
if (Error != ERROR_SUCCESS) {
goto SendResponse;
}
pTemplatePath = (PCHAR)FilePath;
strcpy(pTemplatePath, IntelliMirrorPathA);
strcat(pTemplatePath, "\\");
strcat(pTemplatePath, OscFindVariableA( clientState, "SIFFILE" ));
pCreate->RebootParameter = OSC_REBOOT_COMMAND_CONSOLE_ONLY;
}
if (OscSifIsASR(pTemplatePath)) {
PSTR pGuid,pPathToAsrFile;
//
// Setup the client for an install
//
Error = OscSetupClient( clientState, FALSE );
if (Error != ERROR_SUCCESS) {
goto SendResponse;
}
pTemplatePath = (PCHAR)FilePath;
strcpy(pTemplatePath, IntelliMirrorPathA);
strcat(pTemplatePath, "\\");
strcat(pTemplatePath, OscFindVariableA( clientState, "SIFFILE" ));
#if 0
pGuid = OscFindVariableA( clientState, "GUID" );
pPathToAsrFile = BinlAllocateMemory( sizeof("ASRFiles\\") + strlen(pGuid) + sizeof(".sif") + 1 );
if (pPathToAsrFile) {
strcat(pPathToAsrFile, "ASRFiles\\");
strcat(pPathToAsrFile, pGuid );
strcat(pPathToAsrFile, ".sif" );
WritePrivateProfileStringA(
OSCHOOSER_SIF_SECTIONA,
"ASRFile",
pPathToAsrFile,
pTemplatePath );
BinlFreeMemory( pPathToAsrFile );
}
#endif
pCreate->RebootParameter = OSC_REBOOT_ASR;
}
GetPrivateProfileStringA( OSCHOOSER_SIF_SECTIONA,
"LaunchFile",
"",
pCreate->NextBootfile,
sizeof(pCreate->NextBootfile),
pTemplatePath );
strcpy(pCreate->SifFile, pTemplatePath + strlen(IntelliMirrorPathA) + 1); // skip the next backslash
BinlPrint(( DEBUG_OSC_ERROR, "Client will use SIF file %s\n", pCreate->SifFile ));
BinlPrint(( DEBUG_OSC_ERROR, "Client rebooting to %s\n", pCreate->NextBootfile ));
RspBinaryDataLength = sizeof(CREATE_DATA);
goto SendResponse; // skip the rest and just send it.
}
else if ( lstrcmpiA( NameLoc, "RESTART" ) == 0 )
{
//
// NOTE: This is very similar to the GETCREATE processing
// except for the error case. We send down a packet with
// only the create data in it.
//
//
// Make RspMessage be NULL.
//
if (RspMessage != NULL) {
BinlFreeMemory(RspMessage);
RspMessage = NULL;
}
RspMessageLength = 0;
RspBinaryData = BinlAllocateMemory( sizeof(CREATE_DATA) );
if (RspBinaryData == NULL) {
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
goto SendResponse; // skip the rest and just send it.
}
Error = OscGetCreateData( RequestContext, clientState, (PCREATE_DATA)RspBinaryData );
if ( Error == ERROR_SUCCESS ) {
RspBinaryDataLength = sizeof(CREATE_DATA);
goto SendResponse; // skip the rest and just send it.
} else {
BinlAssert( sizeof(TmpName) >= sizeof("RSTRTERR") );
BinlFreeMemory(RspBinaryData);
RspBinaryData = NULL;
strcpy(TmpName, "RSTRTERR");
NameLoc = TmpName;
Error = ERROR_SUCCESS;
}
}
}
//
// Try to retrieve the next screen
//
if ( Error == ERROR_SUCCESS )
{
//
// If NULL message, then send down the welcome screen.
//
if ( NameLoc == NULL || *NameLoc == '\0' )
{
if ( _snwprintf( FilePath,
sizeof(FilePath) / sizeof(FilePath[0]),
L"%ws\\OSChooser\\%s",
IntelliMirrorPathW,
DEFAULT_SCREEN_NAME
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
} else {
BinlPrint(( DEBUG_OSC, "NULL screen name so we are retrieving the Welcome Screen.\n"));
}
}
else
{
WCHAR NameLocW[ MAX_PATH ];
ULONG NameLocLength;
//
// Create path to possible OSC file. Should look something like:
// "D:\RemoteInstall\OSChooser\English\NameLoc.OSC"
BinlAssert( NameLoc );
NameLocLength = strlen(NameLoc) + 1;
if (NameLocLength > MAX_PATH) {
NameLocLength = MAX_PATH-1;
NameLocW[ MAX_PATH-1 ] = L'\0';
}
mbstowcs( NameLocW, NameLoc, NameLocLength );
#if DBG
if (OscWatchVariable[0] != '\0') {
DbgPrint("Looking for screen <%ws>\n", NameLocW);
}
#endif
if ( _snwprintf( FilePath,
sizeof(FilePath) / sizeof(FilePath[0]),
L"%ws\\OSChooser\\%ws\\%ws.OSC",
IntelliMirrorPathW,
OscFindVariableW( clientState, "LANGUAGE" ),
NameLocW
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
}
}
}
}
if ( Error == ERROR_SUCCESS )
{
//
// If we find the file, load it into memory.
//
BinlPrint(( DEBUG_OSC, "Retrieving screen file: '%ws'\n", FilePath));
hfile = CreateFile( FilePath, 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;
//
// This might be non-NULL if we looped back to GrabAnotherScreen.
//
if (RspMessage != NULL) {
BinlFreeMemory(RspMessage);
}
RspMessage = BinlAllocateMemory( FileSize + RspBinaryDataLength + 3 );
if ( RspMessage == NULL )
{
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
}
else
{
RspMessageLength = 0;
RspMessage[0] = '\0';
while ( dwRead != FileSize )
{
BOOL b;
DWORD dw;
b = ReadFile( hfile, &RspMessage[dwRead], FileSize - dwRead, &dw, NULL );
if (!b)
{
BinlPrint(( DEBUG_OSC_ERROR, "Error reading screen file: Seek=%u, Size=%u, File=%ws\n",
dwRead, FileSize - dwRead, FilePath ));
Error = GetLastError( );
break;
}
dwRead += dw;
}
RspMessageLength = FileSize + 1;
RspMessage[RspMessageLength - 1] = '\0';
}
}
else
{
BinlPrintDbg((DEBUG_OSC, "!!Error - Could not determine file size.\n"));
Error = GetLastError();
}
CloseHandle( hfile );
}
else
{
BinlPrint((DEBUG_OSC, "!!Error - Did not find screen file: '%ws'\n", FilePath));
Error = GetLastError();
OscAddVariableW( clientState, "SUBERROR", FilePath );
}
}
//
// Check the outgoing screen for special NAMEs
//
if ( Error == ERROR_SUCCESS )
{
//
// Read the screen name from the "NAME" section of the file. This allows
// the admin to have different screens with the same "NAME" section but
// that have a different layout and/or text associated with them.
//
PCHAR ServerMeta = RspMessage;
while ( ServerMeta && !!*ServerMeta )
{
CHAR tmpCh = '>'; // save (default)
LPSTR EndofLine;
ServerMeta = StrStrIA( ServerMeta, "<META " );
if ( !ServerMeta )
break;
// Find the end of the meta line
EndofLine = StrChrA( ServerMeta, '>' );
if ( !EndofLine )
break;
*EndofLine = '\0'; // terminate
// Is it a server side meta?
ServerMeta = StrStrIA( ServerMeta, "SERVER" );
if ( !ServerMeta )
goto SkipLine; // nope, skip it
// Find the action
ServerMeta = StrStrIA( ServerMeta, "ACTION=" );
if ( !ServerMeta )
goto SkipLine; // nothing to do, skip it
ServerMeta += sizeof("ACTION=") - sizeof("");
// If the ACTION encapsulated in a pair of quotes, the only use
// the part that is within the quotes.
if ( *ServerMeta == '\"' ) {
*EndofLine = '>'; // restore
ServerMeta++;
EndofLine = StrChrA( ServerMeta, '\"' );
if ( EndofLine ) {
tmpCh = '\"'; // save
} else {
EndofLine = StrChrA( ServerMeta, '>' );
}
*EndofLine = '\0'; // terminate
}
BinlPrintDbg(( DEBUG_OSC, "Processing SERVER side ACTION: %s\n", ServerMeta ));
if ( StrCmpNIA( ServerMeta, "ENUM ", sizeof("ENUM ")-sizeof("") ) == 0 )
{
PCHAR pOptionBuffer;
ULONG OptionBufferLength;
PCHAR pOptionBufferTemp;
ULONG OptionBufferLengthTemp;
PCHAR pCurOptionBuffer;
PCHAR pDirToEnum;
CHAR SaveChar;
//
// They are asking for the screen that has the listing
// of the different types of network booted installations.
//
ServerMeta += sizeof("ENUM ") - sizeof("");
OscResetVariable( clientState, "OPTIONS" );
while (*ServerMeta != '\0') {
//
// Skip leading blanks
//
while (*ServerMeta == ' ') {
ServerMeta++;
}
if (*ServerMeta == '\0') {
break;
}
//
// Save beginning of the dir
//
pDirToEnum = ServerMeta;
//
// Skip to the end of the word
//
while ((*ServerMeta != ' ') &&
(*ServerMeta != '\"') &&
(*ServerMeta != '>') &&
(*ServerMeta != '\0')){
ServerMeta++;
}
//
// Temporarily terminate the word, we will restore it in this loop.
//
SaveChar = *ServerMeta;
*ServerMeta = '\0';
BinlPrintDbg(( DEBUG_OSC, "Processing SERVER side ACTION: ENUM, directory %s\n", pDirToEnum));
//
// Start a buffer for this directory
//
OptionBufferLengthTemp = 512;
pOptionBufferTemp = BinlAllocateMemory( OptionBufferLengthTemp );
if ( pOptionBufferTemp == NULL )
{
OscResetVariable( clientState, "OPTIONS" );
*ServerMeta = SaveChar;
if (SaveChar != '\0') {
ServerMeta++;
}
break;
}
BinlAssert(RspBinaryData == NULL);
*pOptionBufferTemp = '\0';
SearchAndGenerateOSMenu( &pOptionBufferTemp,
&OptionBufferLengthTemp,
pDirToEnum,
clientState
);
if (*pOptionBufferTemp == '\0') {
BinlFreeMemory( pOptionBufferTemp );
*ServerMeta = SaveChar;
if (SaveChar != '\0') {
ServerMeta++;
}
continue;
}
pCurOptionBuffer = OscFindVariableA( clientState, "OPTIONS" );
OptionBufferLength = strlen(pCurOptionBuffer) + sizeof("");
pOptionBuffer = BinlAllocateMemory( OptionBufferLength + OptionBufferLengthTemp );
if (pOptionBuffer == NULL) {
BinlFreeMemory( pOptionBufferTemp );
OscResetVariable( clientState, "OPTIONS" );
*ServerMeta = SaveChar;
if (SaveChar != '\0') {
ServerMeta++;
}
break;
}
strcpy( pOptionBuffer, pCurOptionBuffer );
strcat( pOptionBuffer, pOptionBufferTemp);
OscAddVariableA( clientState, "OPTIONS", pOptionBuffer );
BinlFreeMemory( pOptionBuffer );
BinlFreeMemory( pOptionBufferTemp );
*ServerMeta = SaveChar;
if (SaveChar != '\0') {
ServerMeta++;
}
}
//
// If this generated no options, send down the
// NOOSES screen.
//
pOptionBuffer = OscFindVariableA( clientState, "OPTIONS" );
if (*pOptionBuffer == '\0') {
BinlAssert( sizeof(TmpName) >= sizeof("NOOSES") );
strcpy(TmpName, "NOOSES");
NameLoc = TmpName;
goto GrabAnotherScreen;
}
}
else if ( StrCmpNIA( ServerMeta, "WARNING", sizeof("WARNING")-sizeof("") ) == 0 )
{
LPSTR pszSIF = OscFindVariableA( clientState, "SIF" );
if ( pszSIF )
{
//
// lets check if we're repartitioning or not. if we aren't,
// then there's no need to show the warning screen.
//
CHAR szRepartition[ 64 ];
BOOL DoRepartitionWarning = TRUE;
GetPrivateProfileStringA( "RemoteInstall",
"Repartition",
"Yes",
szRepartition,
sizeof(szRepartition)/sizeof(szRepartition[0]),
pszSIF );
if ( StrCmpIA( szRepartition, "no") != 0) {
LPSTR pszPart;
//
// check if 'repartition' points to an OSC variable
//
if (szRepartition[0] = '%' && szRepartition[strlen(szRepartition)-1] == '%') {
szRepartition[strlen(szRepartition)-1] = '\0';
pszPart= OscFindVariableA( clientState, &szRepartition[1] );
if (StrCmpIA( pszPart, "no") == 0) {
DoRepartitionWarning = FALSE;
}
}
} else {
DoRepartitionWarning = FALSE;
}
if ( DoRepartitionWarning == FALSE ) {
// skip the warning screen
BinlPrintDbg(( DEBUG_OSC, "Repartition == NO. Skipping WARNING screen.\n" ));
*EndofLine = '>'; // restore
ServerMeta = StrStrIA( RspMessage, "ENTER" );
if ( ServerMeta )
{
ServerMeta = StrStrIA( ServerMeta, "HREF=" );
ServerMeta += sizeof("HREF=") - sizeof("");
// If the HREF encapsulated in a pair of quotes, the only use
// the part that is within the quotes.
if ( *ServerMeta == '\"' ) {
ServerMeta ++;
EndofLine = StrChrA( ServerMeta, '\"' );
if ( !EndofLine) {
EndofLine = StrChrA( ServerMeta, '>' );
}
if ( EndofLine ) {
*EndofLine = '\0'; // terminate
}
}
NameLoc = ServerMeta;
goto GrabAnotherScreen;
}
}
}
}
else if ( StrCmpNIA( ServerMeta, "FILTER ", sizeof("FILTER ")-sizeof("") ) == 0 )
{
ULONG OptionCount;
//
// The options on the form on this screen are supposed
// to be filtered by looking at the GPO list for this
// client. This call may modify RspMessageLength, but
// it will only shorten it.
//
// NOTE: We assume that the data we want to modify is after
// the META SERVER ACTION tag. This assumption shows up in
// two ways:
// a) The second parameter to FilterFormOptions is the place
// to start filtering -- we pass it EndOfLine+1 (that is,
// the point right after the FILTER name) because
// we have put a NULL character at EndOfLine. But this means
// it won't process any of the screen before the META tag.
// b) FilterFormOptions may remove parts of the message at
// any place after the "start filtering" location -- so in
// order for the code below after SkipLine: to still work,
// we have to assume that the place in the message that
// EndOfLine points to has not changed.
//
OptionCount = FilterFormOptions(
RspMessage, // start of message
EndofLine+1, // where we start filtering -- after the FILTER name,
// since that is NULL-terminated.
&RspMessageLength,
ServerMeta + sizeof("FILTER ") - sizeof(""),
clientState);
//
// If the result of the filtering is no options, then
// send down the NOCHOICE screen.
//
if (OptionCount == 0) {
BinlAssert( sizeof(TmpName) >= sizeof("NOCHOICE") );
strcpy(TmpName, "NOCHOICE");
NameLoc = TmpName;
goto GrabAnotherScreen;
}
}
else if ( StrCmpNIA( ServerMeta, "CHECKGUID ", sizeof("CHECKGUID ")-sizeof("") ) == 0 )
{
//
// Search the DS for accounts that have this same GUID and
// fill in the form with all the dups. If it's fatal, tell
// them so here, otherwise allow them to continue warned.
//
PCHAR successScreen;
PCHAR failureScreen;
Error = OscCheckMachineDN( clientState );
//
// If there are DN warnings, then the text is saved off in
// %SUBERROR% string.
//
ServerMeta += sizeof("CHECKGUID ") - sizeof("");
successScreen = ServerMeta;
while (*successScreen == ' ') {
successScreen++;
}
failureScreen = successScreen;
while (*failureScreen != ' ' &&
*failureScreen != '>' &&
*failureScreen != '\0' ) {
failureScreen++;
}
if (*failureScreen == ' ') {
// terminate the success screen name
*failureScreen = '\0';
failureScreen++;
//
// if they neglected to put a second parameter, then they
// must not care about the warning case.
//
while (*failureScreen == ' ') {
failureScreen++;
}
}
if ((*failureScreen == '>') || (*failureScreen == '\0')) {
failureScreen = successScreen;
}
if (Error == ERROR_BINL_DUPLICATE_MACHINE_NAME_FOUND) {
//
// in the case of failure, we grab the second parameter
// to CHECKGUID as the warning screen.
//
NameLoc = failureScreen;
} else if (Error != ERROR_SUCCESS) {
goto SendResponse;
} else {
NameLoc = successScreen;
}
//
// in the case of success, we grab the first parameter
// to CHECKGUID as the success screen.
//
Error = ERROR_SUCCESS;
goto GrabAnotherScreen;
}
else if ( StrCmpNIA( ServerMeta, "DNRESET", sizeof("DNRESET")-sizeof("") ) == 0 )
{
//
// the client went back to select between auto and custom,
// therefore we need to reset machineou, machinename,
// machinedn, and machinedomain
//
clientState->fHaveSetupMachineDN = FALSE;
OscResetVariable( clientState, "MACHINEOU" );
OscResetVariable( clientState, "MACHINENAME" );
OscResetVariable( clientState, "MACHINEDN" );
OscResetVariable( clientState, "MACHINEDOMAIN" );
OscResetVariable( clientState, "NETBIOSNAME" );
OscResetVariable( clientState, "DEFAULTDOMAINOU" );
}
SkipLine:
*EndofLine = tmpCh; // restore
EndofLine++;
ServerMeta = EndofLine;
}
}
SendResponse:
//
// If there were any errors, switch to the error screen.
//
if ( Error != ERROR_SUCCESS )
{
//
// Send down message about account creation failure.
//
if ( RspMessage )
{
BinlFreeMemory( RspMessage );
RspMessage = NULL; // paranoid
}
if ( RspBinaryData )
{
BinlFreeMemory( RspBinaryData );
RspBinaryData = NULL; // paranoid
}
Error = GenerateErrorScreen( &RspMessage,
&RspMessageLength,
Error,
clientState );
BinlAssert( Error == ERROR_SUCCESS );
if ( Error != ERROR_SUCCESS )
goto Cleanup;
// Don't send this
RspBinaryDataLength = 0;
}
//
// Make some adjustments to the outgoing screen
//
if ( Error == ERROR_SUCCESS )
{
if (RspMessage) {
//
// Do replacements for dynamic screens
//
SearchAndReplace( clientState->Variables,
&RspMessage,
clientState->nVariables,
RspMessageLength,
RspBinaryDataLength);
RspMessageLength = strlen( RspMessage ) + 1;
//
// NULL terminate RspMessage, and copy binary data if any exists.
//
RspMessage[RspMessageLength-1] = '\0';
if (RspBinaryDataLength) {
memcpy(RspMessage + RspMessageLength, RspBinaryData, RspBinaryDataLength);
RspMessageLength += RspBinaryDataLength;
}
} else {
//
// No RspMessage, RspBinaryData must be the entire thing.
//
BinlAssert( RspBinaryData );
RspMessage = RspBinaryData;
RspBinaryData = NULL;
RspMessageLength = RspBinaryDataLength;
RspBinaryDataLength = 0;
}
}
//
// Send out a signed response
//
BinlAssert( RspMessage );
// BinlPrint((DEBUG_OSC, "Sending Signed:\n%s\n", RspMessage));
#if DBG
if (OscWatchVariable[0] != '\0') {
DbgPrint("VALUE OF <%s> IS <%ws>\n", OscWatchVariable, OscFindVariableW(clientState, OscWatchVariable));
}
#endif
Error = OscSendSignedMessage( RequestContext, clientState, RspMessage, RspMessageLength );
Cleanup:
//
// Free any memory the we allocated for the screen.
//
if ( RspMessage ) {
BinlFreeMemory( RspMessage );
}
if ( RspBinaryData ) {
BinlFreeMemory( RspBinaryData );
}
// Clear the unencrypted buffer to ensure private data is erased.
ZeroMemory(&signedMessage->SequenceNumber, signedMessage->Length);
return Error;
}
//
//
//
DWORD
OscProcessSetupRequest(
LPBINL_REQUEST_CONTEXT RequestContext,
PCLIENT_STATE clientState
)
/*++
Routine Description:
This function processes a request message sent by textmode setup on a client.
IT IS CALLED WITH clientState->CriticalSection HELD.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
clientState - The client state for the remote.
Return Value:
Windows Error.
--*/
{
DWORD Error = ERROR_SUCCESS;
SPUDP_PACKET UNALIGNED * Message = (SPUDP_PACKET UNALIGNED *)RequestContext->ReceiveBuffer;
PCHAR RspMessage = NULL;
PSPUDP_PACKET SuccessPacket;
SPUDP_PACKET ErrorResponsePacket;
CLIENT_STATE TempClientState; // used to call UdpSendMessage
ULONG SuccessPacketLength;
PNETCARD_RESPONSE_DATABASE pInfEntry = NULL;
PSP_NETCARD_INFO_REQ pReqData;
PSP_NETCARD_INFO_RSP pRspData;
PLIST_ENTRY CopyHead;
PLIST_ENTRY CopyListEntry;
PNETCARD_FILECOPY_PARAMETERS cpyParam;
PWCHAR pTmp;
TraceFunc("OscProcessSetupRequest( )\n");
//
// All clients start with at least one unsigned request. When we get an
// unsigned request, the client may have rebooted and be asking for a
// different request with the same sequence number. To avoid having
// to check for this, we don't bother resending unsigned messages. We
// do save the incoming sequence number since we use that for sending
// the response.
//
clientState->LastSequenceNumber = Message->SequenceNumber;
//
// Get info from the INF file.
//
pReqData = (PSP_NETCARD_INFO_REQ)(&(Message->Data[0]));
Error = NetInfFindNetcardInfo(pReqData->SetupPath,
pReqData->Architecture,
pReqData->Version,
&pReqData->CardInfo,
NULL,
&pInfEntry
);
if (Error != ERROR_SUCCESS) {
BinlPrint(( DEBUG_OSC_ERROR, "OscProcessSetupRequest( Card not found ) \n"));
SendErrorResponse:
BinlPrintDbg(( DEBUG_OSC_ERROR, "OscProcessSetupRequest( ) sending Error response \n"));
memcpy(ErrorResponsePacket.Signature, NetcardErrorSignature, 4);
ErrorResponsePacket.Length = sizeof(ULONG) * 2;
ErrorResponsePacket.RequestType = Message->RequestType;
ErrorResponsePacket.Status = STATUS_INVALID_PARAMETER;
TempClientState.LastResponse = (PUCHAR)&ErrorResponsePacket;
TempClientState.LastResponseLength = 12;
Error = SendUdpMessage(RequestContext, &TempClientState, FALSE, FALSE);
goto CleanUp;
}
//
// We found a match, so construct a response. We first need to
// calculate how big the buffer needs to be.
//
CopyHead = &pInfEntry->FileCopyList;
SuccessPacketLength = sizeof(SP_NETCARD_INFO_RSP) - sizeof(WCHAR); // everything except the data
SuccessPacketLength += sizeof(WCHAR) * (wcslen(pInfEntry->DriverName) +
wcslen(pInfEntry->InfFileName) + 4);
CopyListEntry = CopyHead->Flink;
while (CopyListEntry != CopyHead) {
cpyParam = (PNETCARD_FILECOPY_PARAMETERS) CONTAINING_RECORD(CopyListEntry,
NETCARD_FILECOPY_PARAMETERS,
FileCopyListEntry
);
SuccessPacketLength += cpyParam->SourceFile.Length;
if (cpyParam->SourceFile.Buffer[cpyParam->SourceFile.Length / sizeof(WCHAR)] != UNICODE_NULL) {
SuccessPacketLength += sizeof(WCHAR);
}
if (cpyParam->DestFile.Buffer == NULL) {
SuccessPacketLength += sizeof(UNICODE_NULL);
} else {
SuccessPacketLength += cpyParam->DestFile.Length;
if (cpyParam->DestFile.Buffer[cpyParam->DestFile.Length / sizeof(WCHAR)] != UNICODE_NULL) {
SuccessPacketLength += sizeof(WCHAR);
}
}
CopyListEntry = CopyListEntry->Flink;
}
//
// Build response message
//
RspMessage = BinlAllocateMemory(SuccessPacketLength);
if (RspMessage == NULL) {
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
goto SendErrorResponse;
}
pRspData = (PSP_NETCARD_INFO_RSP)RspMessage;
pRspData->cFiles = 0;
pTmp = &(pRspData->MultiSzFiles[0]);
CopyListEntry = CopyHead->Flink;
while (CopyListEntry != CopyHead) {
pRspData->cFiles++;
cpyParam = (PNETCARD_FILECOPY_PARAMETERS) CONTAINING_RECORD(CopyListEntry,
NETCARD_FILECOPY_PARAMETERS,
FileCopyListEntry
);
RtlCopyMemory(pTmp, cpyParam->SourceFile.Buffer, cpyParam->SourceFile.Length);
pTmp = &(pTmp[cpyParam->SourceFile.Length / sizeof(WCHAR)]);
if (*pTmp != UNICODE_NULL) {
pTmp++;
*pTmp = UNICODE_NULL;
}
pTmp++;
if (cpyParam->DestFile.Buffer == NULL) {
*pTmp = UNICODE_NULL;
pTmp++;
} else {
RtlCopyMemory(pTmp, cpyParam->DestFile.Buffer, cpyParam->DestFile.Length);
pTmp = &(pTmp[cpyParam->DestFile.Length / sizeof(WCHAR)]);
if (*pTmp != UNICODE_NULL) {
pTmp++;
*pTmp = UNICODE_NULL;
}
pTmp++;
}
CopyListEntry = CopyListEntry->Flink;
}
//
// Add the driver name and INF file to the list
//
wcscpy(pTmp, pInfEntry->DriverName);
pTmp = pTmp + (wcslen(pTmp) + 1);
*pTmp = UNICODE_NULL;
pTmp++;
wcscpy(pTmp, pInfEntry->InfFileName);
pTmp = pTmp + (wcslen(pTmp) + 1);
*pTmp = UNICODE_NULL;
pTmp++;
pRspData->cFiles += 2;
//
// Send out a response
//
BinlAssert(RspMessage);
Error = OscSendSetupMessage(RequestContext,
clientState,
Message->RequestType,
RspMessage,
SuccessPacketLength
);
CleanUp:
//
// Free any memory the we allocated for the screen.
//
if (pInfEntry) {
NetInfDereferenceNetcardEntry(pInfEntry);
}
if (RspMessage) {
BinlFreeMemory(RspMessage);
}
return Error;
}
DWORD
OscProcessLogoff(
LPBINL_REQUEST_CONTEXT RequestContext,
PCLIENT_STATE clientState
)
/*++
Routine Description:
This function processes a logoff message.
IT IS CALLED WITH clientState->CriticalSection HELD.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
clientState - The client state for the remote.
Return Value:
Windows Error.
--*/
{
//
// clientState will already have been removed from the
// client database. All we need to do is add one to the
// NegativeRefCount and the client will then be deleted
// once everyone else is done using it.
//
TraceFunc("OscProcessLogoff( )\n");
++clientState->NegativeRefCount;
if (clientState->PositiveRefCount != (clientState->NegativeRefCount+1)) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "Refcount not equal at logoff for %s\n", inet_ntoa(*(struct in_addr *)&(clientState->RemoteIp)) ));
}
return ERROR_SUCCESS;
}
DWORD
OscProcessNetcardRequest(
LPBINL_REQUEST_CONTEXT RequestContext
)
/*++
Routine Description:
This function processes requests from clients for information
about network cards.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
Return Value:
Windows Error.
--*/
{
NETCARD_REQUEST_PACKET UNALIGNED * netcardRequestMessage = (NETCARD_REQUEST_PACKET UNALIGNED *)RequestContext->ReceiveBuffer;
NETCARD_RESPONSE_PACKET ErrorResponsePacket;
PNETCARD_RESPONSE_PACKET SuccessResponsePacket;
ULONG SuccessResponsePacketLength;
CLIENT_STATE TempClientState; // used to call UdpSendMessage
PWCHAR driverPath = NULL;
PWCHAR setupPath = NULL;
PCHAR ansiSetupPath = NULL;
ULONG ansiSetupLength;
DWORD Error;
ULONG i;
PNETCARD_RESPONSE_DATABASE pInfEntry = NULL;
TraceFunc("OscProcessNetcardRequest( )\n");
if (netcardRequestMessage->Version != OSCPKT_NETCARD_REQUEST_VERSION) {
Error = STATUS_INVALID_PARAMETER;
TraceFunc("OscProcessNetcardRequest( Version not correct ) \n");
goto sendErrorResponse;
}
if (RequestContext->ReceiveMessageSize < sizeof(NETCARD_REQUEST_PACKET)) {
Error = STATUS_INVALID_PARAMETER;
TraceFunc("OscProcessNetcardRequest( Message too short ) \n");
goto sendErrorResponse;
}
if ((netcardRequestMessage->SetupDirectoryLength >
RequestContext->ReceiveMessageSize -
sizeof(NETCARD_REQUEST_PACKET))
) {
Error = STATUS_INVALID_PARAMETER;
TraceFunc("OscProcessNetcardRequest( setup path length invalid ) \n");
goto sendErrorResponse;
}
ansiSetupLength = netcardRequestMessage->SetupDirectoryLength;
ansiSetupPath = BinlAllocateMemory( ansiSetupLength + 1 );
if (ansiSetupPath == NULL) {
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
TraceFunc("OscProcessNetcardRequest( couldn't allocate temp buffer ) \n");
goto sendErrorResponse;
}
//
// convert the setup path to unicode safely
//
memcpy( ansiSetupPath,
&netcardRequestMessage->SetupDirectoryPath[0],
ansiSetupLength );
*(ansiSetupPath + ansiSetupLength) = '\0';
setupPath = (PWCHAR) BinlAllocateMemory( (ansiSetupLength + 1) * sizeof(WCHAR) );
if (setupPath == NULL) {
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
TraceFunc("OscProcessNetcardRequest( couldn't allocate setup path buffer ) \n");
goto sendErrorResponse;
}
mbstowcs( setupPath,
ansiSetupPath,
ansiSetupLength + 1); // include null
BinlFreeMemory( ansiSetupPath );
//
// Make sure this is a PCI card and the client request structure
// is version 0.
//
BinlPrintDbg(( DEBUG_OSC, "Searching %ws for NIC INF...\n", setupPath ));
Error = NetInfFindNetcardInfo( setupPath,
netcardRequestMessage->Architecture,
netcardRequestMessage->Version,
&netcardRequestMessage->CardInfo,
NULL,
&pInfEntry );
BinlAssert (pInfEntry != NULL || Error != ERROR_SUCCESS);
if (Error != ERROR_SUCCESS) {
BinlPrint(( DEBUG_OSC_ERROR, "OscProcessNetcardRequest( Card not found ) \n"));
sendErrorResponse:
BinlPrintDbg(( DEBUG_OSC_ERROR, "OscProcessNetcardRequest( ) sending Error response \n"));
memcpy(ErrorResponsePacket.Signature, NetcardErrorSignature, 4);
ErrorResponsePacket.Length = sizeof(ULONG);
ErrorResponsePacket.Status = STATUS_INVALID_PARAMETER;
TempClientState.LastResponse = (PUCHAR)&ErrorResponsePacket;
TempClientState.LastResponseLength = 12;
Error = SendUdpMessage(RequestContext, &TempClientState, FALSE, FALSE);
} else {
//
// We found a match, so construct a response. We first need to
// calculate how bit the buffer needs to be.
//
PLIST_ENTRY registryHead;
PLIST_ENTRY registryListEntry;
PNETCARD_REGISTRY_PARAMETERS regParam;
ULONG registryLength = 0;
registryHead = &pInfEntry->Registry;
SuccessResponsePacketLength = sizeof(NETCARD_RESPONSE_PACKET) +
+ (( lstrlenW( pInfEntry->HardwareId ) + 1 ) * sizeof(WCHAR)) +
+ (( lstrlenW( pInfEntry->DriverName ) + 1 ) * sizeof(WCHAR)) +
+ (( lstrlenW( pInfEntry->ServiceName ) + 1 ) * sizeof(WCHAR)) +
sizeof(WCHAR); // termination for registry field
registryListEntry = registryHead->Flink;
while (registryListEntry != registryHead) {
//
// each entry is a field name, field type (2 = string, 1 = int)
// and field value. All of these are unicode strings terminated
// with a unicode null.
//
regParam = (PNETCARD_REGISTRY_PARAMETERS) CONTAINING_RECORD(
registryListEntry,
NETCARD_REGISTRY_PARAMETERS,
RegistryListEntry );
registryLength += regParam->Parameter.Length + 1;
registryLength += 2; // field type
registryLength += regParam->Value.Length + 1;
registryListEntry = registryListEntry->Flink;
}
registryLength += sizeof("Description");
registryLength += 2; // field type
registryLength += lstrlenW( pInfEntry->DriverDescription ) + 1;
SuccessResponsePacket = (PNETCARD_RESPONSE_PACKET)
BinlAllocateMemory(
SuccessResponsePacketLength +
registryLength );
if (SuccessResponsePacket == NULL) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not allocate SuccessResponsePacket of %ld bytes\n",
SuccessResponsePacketLength + registryLength));
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
goto sendErrorResponse;
} else {
PWCHAR nextWideField;
PCHAR nextField;
PCHAR startOfRegistry;
ANSI_STRING aString;
UNICODE_STRING descriptionString;
RtlZeroMemory(SuccessResponsePacket,
SuccessResponsePacketLength + registryLength);
memcpy(SuccessResponsePacket->Signature, NetcardResponseSignature, 4);
SuccessResponsePacket->Status = STATUS_SUCCESS;
SuccessResponsePacket->Version = OSCPKT_NETCARD_REQUEST_VERSION;
nextWideField = (PWCHAR)(PCHAR)((PCHAR) &SuccessResponsePacket->RegistryOffset
+ sizeof(ULONG));
lstrcpyW( nextWideField, pInfEntry->HardwareId );
SuccessResponsePacket->HardwareIdOffset = (ULONG)((PCHAR) nextWideField - (PCHAR) SuccessResponsePacket);
nextWideField += lstrlenW( pInfEntry->HardwareId ) + 1;
lstrcpyW( nextWideField, pInfEntry->DriverName );
SuccessResponsePacket->DriverNameOffset = (ULONG)((PCHAR) nextWideField - (PCHAR) SuccessResponsePacket);
nextWideField += lstrlenW( pInfEntry->DriverName ) + 1;
lstrcpyW( nextWideField, pInfEntry->ServiceName );
SuccessResponsePacket->ServiceNameOffset = (ULONG)((PCHAR) nextWideField - (PCHAR) SuccessResponsePacket);
nextWideField += lstrlenW( pInfEntry->ServiceName ) + 1;
SuccessResponsePacket->RegistryOffset = (ULONG)((PCHAR) nextWideField - (PCHAR) SuccessResponsePacket);
startOfRegistry = nextField = (PCHAR) nextWideField;
//
// the first registry value should be description, otherwise
// bad things happen in NDIS on the client.
//
strcpy( nextField, "Description" );
nextField += sizeof("Description");
//
// then copy in the type of the field, int or string
//
*(nextField++) = NETCARD_REGISTRY_TYPE_STRING;
*(nextField++) = '\0';
//
// then copy in the registry value
//
RtlInitUnicodeString( &descriptionString, pInfEntry->DriverDescription );
aString.Buffer = nextField;
aString.Length = 0;
aString.MaximumLength = ( descriptionString.Length + 1 ) * sizeof(WCHAR);
RtlUnicodeStringToAnsiString( &aString,
&descriptionString,
FALSE );
nextField += aString.Length + 1;
registryListEntry = registryHead->Flink;
while (registryListEntry != registryHead) {
//
// each entry is a field name, field type (2 = string, 1 = int)
// and field value. All of these are unicode strings terminated
// with a unicode null.
//
regParam = (PNETCARD_REGISTRY_PARAMETERS) CONTAINING_RECORD(
registryListEntry,
NETCARD_REGISTRY_PARAMETERS,
RegistryListEntry );
if (regParam->Parameter.Length > 0) {
//
// first copy in the registry value name
//
aString.Buffer = nextField;
aString.Length = 0;
aString.MaximumLength = ( regParam->Parameter.Length + 1 ) * sizeof(WCHAR);
RtlUnicodeStringToAnsiString( &aString,
&regParam->Parameter,
FALSE );
nextField += aString.Length + 1;
//
// then copy in the type of the field, int or string
//
*(nextField++) = (UCHAR) regParam->Type;
*(nextField++) = '\0';
//
// then copy in the registry value
//
aString.Buffer = nextField;
aString.Length = 0;
aString.MaximumLength = ( regParam->Value.Length + 1 ) * sizeof(WCHAR);
RtlUnicodeStringToAnsiString( &aString,
&regParam->Value,
FALSE );
nextField += aString.Length + 1;
}
registryListEntry = registryListEntry->Flink;
}
//
// put in extra null terminator for end of registry section
//
*nextField = '\0';
nextField++;
SuccessResponsePacket->RegistryLength = (ULONG) (nextField - startOfRegistry);
SuccessResponsePacketLength += SuccessResponsePacket->RegistryLength;
//
// The length field in the packet is set to the length of the
// packet starting at the Status field. If we put in a field
// between LENGTH and STATUS, we need to update this code.
//
SuccessResponsePacket->Length = (ULONG)((PCHAR) nextField -
(PCHAR) &SuccessResponsePacket->Status);
TempClientState.LastResponse = (PUCHAR)SuccessResponsePacket;
TempClientState.LastResponseLength = SuccessResponsePacketLength;
Error = SendUdpMessage(RequestContext, &TempClientState, FALSE, FALSE);
BinlFreeMemory(SuccessResponsePacket);
}
}
if (pInfEntry) {
NetInfDereferenceNetcardEntry( pInfEntry );
}
if (driverPath) {
BinlFreeMemory( driverPath );
}
if (setupPath) {
BinlFreeMemory( setupPath );
}
return Error;
}
DWORD
OscProcessHalRequest(
LPBINL_REQUEST_CONTEXT RequestContext,
PCLIENT_STATE clientState
)
/*++
Routine Description:
This function processes requests from clients for taking a
detected HAL name string and mapping it to a <hal>.dll name
and then copying that hal to the machine directory.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
clientState - contains client state for the remote machine
Return Value:
Windows Error.
--*/
{
HAL_REQUEST_PACKET UNALIGNED * halRequestMessage = (HAL_REQUEST_PACKET UNALIGNED *)RequestContext->ReceiveBuffer;
HAL_RESPONSE_PACKET responsePacket;
CLIENT_STATE TempClientState; // used to call UdpSendMessage
DWORD Error;
WCHAR MachinePath[MAX_PATH];
WCHAR SrcPath[MAX_PATH];
WCHAR DestPath[MAX_PATH];
WCHAR HalName[MAX_HAL_NAME_LENGTH+1];
WCHAR HalInfo[MAX_PATH];
ULONG HalNameLength;
ULONG len, index;
BOOL b;
PMACHINE_INFO pMachineInfo = NULL;
USHORT SystemArchitecture;
TraceFunc("OscProcessHalRequest( )\n");
//
// Find the length of the HAL name. To avoid overflowing past the
// end of the received message, check it ourselves.
//
HalNameLength = 0;
while (halRequestMessage->HalName[HalNameLength] != '\0') {
++HalNameLength;
if (HalNameLength >= sizeof(HalName)/sizeof(WCHAR)) {
Error = ERROR_INVALID_DATA;
TraceFunc("OscProcessHalRequest( Exit 0 ) \n");
goto SendResponse;
}
}
++HalNameLength; // convert the '\0' also
mbstowcs( HalName, halRequestMessage->HalName, HalNameLength );
SystemArchitecture = OscPlatformToArchitecture( clientState );
//
// Retrieve information from the DS
//
Error = GetBootParameters( halRequestMessage->Guid,
&pMachineInfo,
MI_NAME | MI_SETUPPATH | MI_HOSTNAME,
SystemArchitecture,
FALSE );
if (Error != ERROR_SUCCESS) {
TraceFunc("OscProcessHalRequest( Exit 1 ) \n");
goto SendResponse;
}
//
// Find the HAL
//
//
// Resulting string should be something like:
// "\\ADAMBA4\REMINST\Setup\English\Images\NTWKS5.0\i386\txtsetup.sif"
if ( _snwprintf( SrcPath,
sizeof(SrcPath) / sizeof(SrcPath[0]),
L"%ws\\txtsetup.sif",
pMachineInfo->SetupPath
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
goto SendResponse;
}
len = GetPrivateProfileString(L"hal",
HalName,
L"",
HalInfo,
sizeof(HalInfo)/sizeof(HalInfo[0]),
SrcPath
);
if (len == 0) {
TraceFunc("OscProcessHalRequest( Exit 3 ) \n");
goto SendResponse;
}
//
// Parse the response which should be in the form:
// "newhal.dll,2,hal.dll"
//
index = 0;
while ( HalInfo[index] )
{
if (HalInfo[index] == L' ' || HalInfo[index] == L',' )
break;
index++;
}
HalInfo[index] = L'\0';
if (HalInfo[0] == L'\0' ) {
Error = ERROR_BINL_HAL_NOT_FOUND;
goto SendResponse;
}
//
// Copy the HAL to the machine directory
//
if ( _snwprintf( SrcPath,
sizeof(SrcPath) / sizeof(SrcPath[0]),
L"%ws\\%ws",
pMachineInfo->SetupPath,
HalInfo
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
goto SendResponse;
}
if ( _snwprintf( DestPath,
sizeof(DestPath) / sizeof(DestPath[0]),
L"%ws\\winnt\\system32\\hal.dll",
MachinePath
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
goto SendResponse;
}
BinlPrintDbg((DEBUG_OSC, "Copying %ws to %ws...\n", SrcPath, DestPath));
b = CopyFile( SrcPath, DestPath, FALSE );
if (!b) {
Error = ERROR_BINL_HAL_NOT_FOUND;
TraceFunc("OscProcessHalRequest( Exit 4 ) \n");
goto SendResponse;
}
//
// Find which kernel to copy
//
index = wcslen(HalName);
while (index > 0) {
index--;
if (HalName[index] == L'_') {
index++;
break;
}
}
if ((index == 0) || (index == wcslen(HalName))) {
Error = ERROR_SERVER_KERNEL_NOT_FOUND;
goto SendResponse;
}
//
// Copy that too
//
if ((HalName[index] == L'u') ||
(HalName[index] == L'U')) {
if ( _snwprintf( SrcPath,
sizeof(SrcPath) / sizeof(SrcPath[0]),
L"%ws\\ntoskrnl.exe",
pMachineInfo->SetupPath
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
goto SendResponse;
}
} else {
if ( _snwprintf( SrcPath,
sizeof(SrcPath) / sizeof(SrcPath[0]),
L"%ws\\ntkrnlmp.exe",
pMachineInfo->SetupPath
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
goto SendResponse;
}
}
if ( _snwprintf( DestPath,
sizeof(DestPath) / sizeof(DestPath[0]),
L"%s\\winnt\\system32\\ntoskrnl.exe",
MachinePath
) == -1 ) {
Error = ERROR_BAD_PATHNAME;
goto SendResponse;
}
BinlPrintDbg((DEBUG_OSC, "Copying %ws to %ws...\n", SrcPath, DestPath));
b = CopyFile( SrcPath, DestPath, FALSE );
if (!b) {
Error = ERROR_SERVER_KERNEL_NOT_FOUND;
TraceFunc("OscProcessHalRequest( Exit 5 ) \n");
goto SendResponse;
}
Error = ERROR_SUCCESS;
TraceFunc("OscProcessHalRequest( SUCCESS ) \n");
SendResponse:
if ( pMachineInfo ) {
BinlDoneWithCacheEntry( pMachineInfo, FALSE );
}
memcpy(responsePacket.Signature, HalResponseSignature, 4);
responsePacket.Length = sizeof(ULONG);
responsePacket.Status = (Error == ERROR_SUCCESS) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
TempClientState.LastResponse = (PUCHAR)&responsePacket;
TempClientState.LastResponseLength = sizeof(responsePacket);
Error = SendUdpMessage(RequestContext, &TempClientState, FALSE, FALSE);
return Error;
}
//
// Process WINNT.SIF file for the client setup
//
DWORD
OscProcessSifFile(
PCLIENT_STATE clientState,
LPWSTR TemplateFile,
LPWSTR WinntSifPath )
{
DWORD dwErr = ERROR_SUCCESS;
DWORD len;
HANDLE hFile = INVALID_HANDLE_VALUE;
SECURITY_ATTRIBUTES SecurityAttributes;
EXPLICIT_ACCESS ExplicitAccessList[2];
PACL pAcl;
PSID pSid;
PWCHAR pszUserName;
PWCHAR pszDomainName;
WCHAR UniqueUdbPath[ MAX_PATH ]; // ie "D:\RemoteInstall\Setup\English\Images\NT50.WKS\i386\Templates\unique.udb"
SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
PSECURITY_DESCRIPTOR pSd;
TraceFunc("OscProcessSifFile( )\n");
//
// Impersonate while opening the file, in case the administrator messed
// up and didn't give local system permission.
//
dwErr = OscImpersonate(clientState);
if (dwErr == ERROR_SUCCESS) {
LPWSTR uniqueUdbId = OscFindVariableW( clientState, "UNIQUEUDBID" );
if (uniqueUdbId[0] != L'\0') {
//
// See if a unique.udb file name was specified in the template file.
// The default name is "unique.udb".
//
len = GetPrivateProfileStringW(OSCHOOSER_SIF_SECTIONW,
L"UniqueUdbFile",
L"unique.udb", // default
UniqueUdbPath,
sizeof(UniqueUdbPath)/sizeof(UniqueUdbPath[0]),
TemplateFile
);
if (len == 0) {
UniqueUdbPath[0] = L'\0'; // means not to process it
} else {
//
// Prepend the path to our template file to UniqueUdbPath.
//
PWCHAR EndOfTemplatePath = wcsrchr(TemplateFile, L'\\');
if (EndOfTemplatePath != NULL) {
DWORD PathLength = (DWORD)(EndOfTemplatePath - TemplateFile + 1);
DWORD FileNameLength = wcslen(UniqueUdbPath) + 1;
if (PathLength + FileNameLength <= MAX_PATH) {
memmove(UniqueUdbPath + PathLength, UniqueUdbPath, FileNameLength * sizeof(WCHAR));
memmove(UniqueUdbPath, TemplateFile, PathLength * sizeof(WCHAR));
}
}
}
}
//
// Open the template file
//
hFile = CreateFile( TemplateFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL, // security attribs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, // maybe FILE_ATTRIBUTE_HIDDEN(?)
NULL ); // template
OscRevert(clientState);
if ( hFile != INVALID_HANDLE_VALUE )
{
DWORD dwFileSize = GetFileSize( hFile, NULL );
if ( dwFileSize != -1 )
{
LPSTR pBuffer = BinlAllocateMemory( dwFileSize + 1); // SIF file buffer
if ( pBuffer )
{
DWORD dw;
//
// Read the file in
//
ReadFile( hFile, pBuffer, dwFileSize, &dw, NULL );
CloseHandle( hFile );
pBuffer[ dwFileSize ] = '\0'; // terminate
//
// Process the unique.udb overlay. We change the
// in-memory version of the file in pBuffer. NOTE
// we do this before calling SearchAndReplace in
// case unique.udb has any variables in it, or
// has a hard-coded value for something that it
// normally a variable in the file.
//
if ((uniqueUdbId[0] != L'\0') &&
(UniqueUdbPath[0] != L'\0')) {
ProcessUniqueUdb( &pBuffer,
dwFileSize + 1,
UniqueUdbPath,
uniqueUdbId );
dwFileSize = strlen( pBuffer );
}
//
// search and replace defined macros
//
SearchAndReplace( clientState->Variables,
&pBuffer,
clientState->nVariables,
dwFileSize + 1,
0 );
dwFileSize = strlen( pBuffer );
//
// HACK:
// If there is a line 'FullName = " "' in the SIF, get rid
// of the space in the quotes. This deals with the case
// where the template SIF had something like:
// FullName = "%USERFIRSTNAME% %USERLASTNAME%"
// and OscGetUserDetails() was unable to get the necessary
// user information from the DS. If we leave the space in
// there, setup won't prompt for the user name.
//
#define BLANK_FULL_NAME "FullName = \" \"\r\n"
{
LPSTR p = pBuffer;
while ( *p != 0 ) {
if ( StrCmpNIA( p, BLANK_FULL_NAME, strlen(BLANK_FULL_NAME) ) == 0 ) {
p = p + strlen(BLANK_FULL_NAME) - 4;
memmove( p, p+1, dwFileSize - (p - pBuffer) ); // move terminator too
dwFileSize--;
break;
}
while ( (*p != 0) && (*p != '\r') && (*p != '\n') ) {
p++;
}
while ( (*p != 0) && ((*p == '\r') || (*p == '\n')) ) {
p++;
}
}
}
//
// Setup the ACL for this file, first is to grant admins all rights.
//
if (!AllocateAndInitializeSid(&SidAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pSid
)) {
OscCreateWin32SubError( clientState, GetLastError( ) );
dwErr = ERROR_BINL_FAILED_TO_CREATE_TEMP_SIF;
BinlFreeMemory(pBuffer);
return dwErr;
}
ExplicitAccessList[0].grfAccessMode = SET_ACCESS;
ExplicitAccessList[0].grfAccessPermissions = GENERIC_READ | GENERIC_WRITE | DELETE;
ExplicitAccessList[0].grfInheritance = NO_INHERITANCE;
ExplicitAccessList[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ExplicitAccessList[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ExplicitAccessList[0].Trustee.ptstrName = pSid;
//
// Now grant the user all rights.
//
pszUserName = OscFindVariableW(clientState, "USERNAME");
pszDomainName = OscFindVariableW(clientState, "USERDOMAIN");
if (pszUserName[0] == L'\0') {
dwErr = ERROR_BINL_FAILED_TO_CREATE_TEMP_SIF;
FreeSid(pSid);
BinlFreeMemory(pBuffer);
return dwErr;
}
if (pszDomainName[0] != L'\0') {
swprintf(UniqueUdbPath, L"%s\\", pszDomainName);
} else {
UniqueUdbPath[0] = L'\0';
}
wcscat(UniqueUdbPath, pszUserName);
ExplicitAccessList[1].grfAccessMode = SET_ACCESS;
ExplicitAccessList[1].grfAccessPermissions = GENERIC_READ | GENERIC_WRITE | DELETE;
ExplicitAccessList[1].grfInheritance = NO_INHERITANCE;
ExplicitAccessList[1].Trustee.TrusteeType = TRUSTEE_IS_USER;
ExplicitAccessList[1].Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ExplicitAccessList[1].Trustee.ptstrName = UniqueUdbPath;
//
// Create an ACL with these two.
//
dwErr = SetEntriesInAcl(2, ExplicitAccessList, NULL, &pAcl);
if (dwErr != ERROR_SUCCESS) {
OscCreateWin32SubError( clientState, GetLastError( ) );
dwErr = ERROR_BINL_FAILED_TO_CREATE_TEMP_SIF;
FreeSid(pSid);
BinlFreeMemory(pBuffer);
return dwErr;
}
//
// Create an SD for this ACL
//
pSd = BinlAllocateMemory(SECURITY_DESCRIPTOR_MIN_LENGTH);
if (pSd == NULL) {
dwErr = ERROR_BINL_FAILED_TO_CREATE_TEMP_SIF;
FreeSid(pSid);
BinlFreeMemory(pBuffer);
return dwErr;
}
if (!InitializeSecurityDescriptor(pSd, SECURITY_DESCRIPTOR_REVISION) ||
!SetSecurityDescriptorDacl(pSd, TRUE, pAcl, FALSE)) {
OscCreateWin32SubError( clientState, GetLastError( ) );
dwErr = ERROR_BINL_FAILED_TO_CREATE_TEMP_SIF;
FreeSid(pSid);
BinlFreeMemory(pBuffer);
return dwErr;
}
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
SecurityAttributes.lpSecurityDescriptor = pSd;
SecurityAttributes.bInheritHandle = FALSE;
//
// Create the destination file
//
hFile = CreateFile( WinntSifPath,
GENERIC_WRITE | GENERIC_READ | DELETE,
FILE_SHARE_READ,
&SecurityAttributes,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL ); // template
BinlFreeMemory(pSd);
FreeSid(pSid);
LocalFree(pAcl);
if ( hFile != INVALID_HANDLE_VALUE )
{
//
// Write it all at once
//
if (!WriteFile( hFile, pBuffer, dwFileSize, &dw, NULL )) {
OscCreateWin32SubError( clientState, GetLastError( ) );
dwErr = ERROR_BINL_FAILED_TO_CREATE_TEMP_SIF;
}
CloseHandle( hFile );
}
else
{
OscCreateWin32SubError( clientState, GetLastError( ) );
dwErr = ERROR_BINL_FAILED_TO_CREATE_TEMP_SIF;
}
BinlFreeMemory(pBuffer);
} else {
CloseHandle( hFile );
OscCreateWin32SubError( clientState, GetLastError( ) );
dwErr = ERROR_BINL_FAILED_TO_CREATE_TEMP_SIF;
}
} else {
CloseHandle( hFile );
}
}
else
{
dwErr = GetLastError( );
}
}
else
{
OscCreateWin32SubError( clientState, GetLastError( ) );
dwErr = ERROR_BINL_FAILED_TO_CREATE_TEMP_SIF;
}
return dwErr;
}
//
// Creates the client image directory, copy the files needed to run
// launch text mode setup, munges Winnt.Sif, more...
//
DWORD
OscSetupClient(
PCLIENT_STATE clientState,
BOOLEAN ErrorDuplicateName
)
{
DWORD dwErr = ERROR_SUCCESS;
WCHAR SetupPath[ MAX_PATH ]; // ie "D:\RemoteInstall\Setup\English\Images\NT50.WKS\i386"
PWCHAR pTemplatePath; // ie "D:\RemoteInstall\Setup\English\Images\NT50.WKS\i386\Templates\RemBoot.SIF"
WCHAR WinntSifPath[ MAX_PATH ]; // ie "D:\RemoteInstall\Clients\NP00805F7F4C85$\winnt.sif"
PWCHAR pwc; // parsing pointer
WCHAR wch; // temp wide char
PWCHAR pMachineName; // Pointer to Machine Name variable value
PWCHAR pMachineOU; // Pointer to where the MAO will be created
PWCHAR pDomain; // Pointer to Domain variable name
PWCHAR pGuid; // Pointer to Guid variable name
WCHAR Path[MAX_PATH]; // general purpose path buffer
WCHAR TmpPath[MAX_PATH]; // general purpose path buffer
ULONG lenIntelliMirror; // Lenght of IntelliMirrorPath (eg "D:\RemoteInstall")
HANDLE hDir; // Directory handle
ULONG i; // general counter
BOOL b; // general purpose BOOLean.
BOOLEAN ExactMatch;
UINT uSize;
LARGE_INTEGER KernelVersion;
WCHAR VersionString[20];
PCHAR pszGuid;
UCHAR Guid[ BINL_GUID_LENGTH ];
USHORT SystemArchitecture;
PMACHINE_INFO pMachineInfo = NULL;
TraceFunc("OscSetupClient( )\n");
lenIntelliMirror = wcslen(IntelliMirrorPathW) + 1;
dwErr = OscCheckMachineDN( clientState );
if ((dwErr == ERROR_BINL_DUPLICATE_MACHINE_NAME_FOUND) && !ErrorDuplicateName) {
dwErr = ERROR_SUCCESS;
}
if (dwErr != ERROR_SUCCESS) {
BinlPrintDbg(( DEBUG_OSC_ERROR, "OscCheckMachineDN returned 0x%x\n", dwErr ));
goto e0;
}
//
// Get the machine GUID and get any overriding parameters.
//
pszGuid = OscFindVariableA( clientState, "GUID" );
if ( pszGuid[0] == '\0' ) {
BinlPrintDbg((DEBUG_OSC_ERROR, "OscSetupClient: could not find GUID" ));
OscAddVariableA( clientState, "SUBERROR", "GUID" );
dwErr = ERROR_BINL_MISSING_VARIABLE;
goto e0;
}
pGuid = OscFindVariableW( clientState, "GUID" );
if ( pGuid[0] == L'\0' ) {
OscAddVariableA( clientState, "SUBERROR", "GUID" );
dwErr = ERROR_BINL_MISSING_VARIABLE;
goto e0;
}
dwErr = OscGuidToBytes( pszGuid, Guid );
if ( dwErr != ERROR_SUCCESS ) {
BinlPrintDbg((DEBUG_OSC_ERROR, "OscSetupClient: OscGuidToBytes failed\n" ));
goto e0;
}
SystemArchitecture = OscPlatformToArchitecture(clientState);
dwErr = GetBootParameters( Guid,
&pMachineInfo,
MI_SIFFILENAME_ALLOC,
SystemArchitecture,
FALSE );
if ( dwErr == ERROR_SUCCESS ) {
//
// Set default values
//
if (pMachineInfo->dwFlags & MI_SIFFILENAME_ALLOC) {
dwErr = OscAddVariableW( clientState, "FORCESIFFILE", pMachineInfo->ForcedSifFileName );
if ( dwErr != ERROR_SUCCESS ) {
goto e0;
}
}
}
//
// Get SIF File name.
//
pTemplatePath = OscFindVariableW( clientState, "SIF" );
if ( pTemplatePath[0] == L'\0' ) {
BinlPrint(( DEBUG_OSC_ERROR, "Missing SIF variable\n" ));
OscAddVariableA( clientState, "SUBERROR", "SIF" );
dwErr = ERROR_BINL_MISSING_VARIABLE;
goto e0;
}
//
// validate the machine name. Note the extra check for a
// period that DnsValidateDnsName_W won't catch for us.
//
pMachineName = OscFindVariableW( clientState, "MACHINENAME" );
if ( pMachineName[0] == L'\0' ) {
OscAddVariableA( clientState, "SUBERROR", "MACHINENAME" );
dwErr = ERROR_BINL_MISSING_VARIABLE;
goto e0;
} else if ( DnsValidateDnsName_W( pMachineName ) != ERROR_SUCCESS ) {
dwErr = ERROR_BINL_INVALID_MACHINE_NAME;
OscAddVariableA( clientState, "SUBERROR", " " );
goto e0;
} else if ( StrStrI( pMachineName,L".")) {
dwErr = ERROR_BINL_INVALID_MACHINE_NAME;
OscAddVariableA( clientState, "SUBERROR", " " );
goto e0;
}
pMachineOU = OscFindVariableW( clientState, "MACHINEOU" );
if ( pMachineOU[0] == L'\0' ) {
OscAddVariableA( clientState, "SUBERROR", "MACHINEOU" );
dwErr = ERROR_BINL_MISSING_VARIABLE;
goto e0;
}
// Do we have a domain yet?
pDomain = OscFindVariableW( clientState, "MACHINEDOMAIN" );
if ( pDomain[0] == L'\0' ) {
OscAddVariableA( clientState, "SUBERROR", "MACHINEDOMAIN" );
dwErr = ERROR_BINL_MISSING_VARIABLE;
goto e0;
}
if (OscSifIsSysPrep(pTemplatePath)) {
DWORD SysPrepSku;
//
// Get the system path from the SIF file
//
dwErr = GetPrivateProfileStringW(OSCHOOSER_SIF_SECTIONW,
L"SysPrepSystemRoot",
L"",
TmpPath,
sizeof(TmpPath)/sizeof(TmpPath[0]),
pTemplatePath
);
if (dwErr == 0) {
dwErr = ERROR_BINL_CD_IMAGE_NOT_FOUND;
goto e0;
}
SysPrepSku = GetPrivateProfileInt(
OSCHOOSER_SIF_SECTIONW,
L"ProductType",
0,
pTemplatePath );
//
// Get the root of the mirror directory from the template path.
//
// pTemplatePath looks like
// "D:\RemoteInstall\Setup\English\Images\NT50.Prep\i386\templates\riprep.sif",
// truncate it temporarily to
// "D:\RemoteInstall\Setup\English\Images\NT50.Prep\i386"
// by NULLing out the second-to-last '\'.
//
pwc = pTemplatePath + wcslen( pTemplatePath );
for ( i = 0; i < 2; i++ )
{
while ( pwc > pTemplatePath && *pwc != L'\\' )
pwc--;
pwc--;
}
pwc++;
wch = *pwc; // remember
*pwc = L'\0'; // terminate
//
// Now make Path be something like
// "D:\RemoteInstall\Setup\English\Images\NT50.Prep\i386\Mirror1\UserData\WINNT\system32"
// which is where the ntoskrnl.exe whose version we want is located.
//
// Make sure there is room for pTemplatePath + "\" (1 byte) +
// TmpPath + "\system32" (9 bytes) + '\0' (1 byte).
//
if (wcslen(pTemplatePath) + wcslen(TmpPath) + 11 > sizeof(Path)/sizeof(Path[0])) {
dwErr = ERROR_BAD_PATHNAME;
goto e0;
}
wcscpy(Path, pTemplatePath);
wcscat(Path, L"\\");
wcscat(Path, TmpPath);
wcscat(Path, L"\\system32");
//
// For NT 5.0, we'll bomb out if it's not an exact match.
//
if (!OscGetClosestNt(
Path,
SysPrepSku,
clientState,
SetupPath,
&ExactMatch) ||
( ExactMatch == FALSE )) {
dwErr = ERROR_BINL_CD_IMAGE_NOT_FOUND;
goto e0;
}
//
// SetupPath comes back as a path like
// "D:\RemoteInstall\Setup\English\Images\nt5.0\i386",
// If there was an exact match, we want SYSPREPDRIVERS to just be
// "Setup\English\Images\nt5.0\i386"
// otherwise we want it to be blank.
//
if (ExactMatch) {
OscAddVariableW(clientState, "SYSPREPDRIVERS", &(SetupPath[lenIntelliMirror]));
} else {
OscAddVariableW(clientState, "SYSPREPDRIVERS", L"");
}
//
// SYSPREPPATH will be the truncated pTemplatePath, without the
// local path at the front, something like
// "Setup\English\Images\NT50.Prep\i386".
//
OscAddVariableW(clientState, "SYSPREPPATH", &pTemplatePath[lenIntelliMirror]);
//
// Now restore pTemplatePath to what it was originally.
//
*pwc = wch;
dwErr = ERROR_SUCCESS;
} else {
//
// create the setup path to the workstation installation by stripping off
// the SIF filename. We'll search backwards for the first 2nd "\".
//
// "D:\RemoteInstall\Setup\English\NetBootOs\NT50.WKS\i386"
pwc = pTemplatePath + wcslen( pTemplatePath );
for ( i = 0; i < 2; i++ )
{
while ( pwc > pTemplatePath && *pwc != L'\\' )
pwc--;
pwc--;
}
pwc++;
wch = *pwc; // remember
*pwc = L'\0'; // terminate
wcscpy( SetupPath, pTemplatePath ); // copy
*pwc = wch; // restore
}
//
// Figure out the INSTALLPATH. This is the SetupPath minus the
// "D:\RemoteInstall" and should be:
// "Setup\English\Images\NT50.WKS"
wcscpy( Path, &SetupPath[lenIntelliMirror] );
Path[ wcslen(Path) - 1
- strlen( OscFindVariableA( clientState, "MACHINETYPE" ) ) ] = '\0';
dwErr = OscAddVariableW( clientState, "INSTALLPATH", Path );
if ( dwErr != ERROR_SUCCESS ) {
goto e0;
}
//
// record the build and version of the OS we're installing.
// if it fails, just fall back to NT 5.0.
//
if(!OscGetNtVersionInfo((PULONGLONG)&KernelVersion, SetupPath, clientState )) {
KernelVersion.LowPart = MAKELONG(0,2195);
KernelVersion.HighPart = MAKELONG(0,5);
}
wsprintf(VersionString,L"%d.%d", HIWORD(KernelVersion.HighPart), LOWORD(KernelVersion.HighPart));
OscAddVariableW( clientState, "IMAGEVERSION", VersionString );
wsprintf(VersionString,L"%d", HIWORD(KernelVersion.LowPart));
OscAddVariableW( clientState, "IMAGEBUILD", VersionString );
//
// Create the default path to the image
//
if ( _snwprintf( Path,
sizeof(Path) / sizeof(Path[0]),
L"%ws\\%ws\\templates",
OscFindVariableW( clientState, "INSTALLPATH" ),
OscFindVariableW( clientState, "MACHINETYPE" )
) == -1 ) {
dwErr = ERROR_BAD_PATHNAME;
goto e0;
}
//
// Create destination SIF file path.
//
if ( _snwprintf( WinntSifPath,
sizeof(WinntSifPath) / sizeof(WinntSifPath[0]),
L"%ws\\%ws\\%ws.sif",
IntelliMirrorPathW,
TEMP_DIRECTORY,
pGuid
) == -1 ) {
dwErr = ERROR_BAD_PATHNAME;
goto e0;
}
//
// Make sure there is a tmp directory below \remoteinstall.
//
dwErr = OscCheckTmpDirectory();
if (dwErr != ERROR_SUCCESS) {
goto e0;
}
//
// generate a machine password that we'll use in the SIF file and when
// setting up the MAO
//
wcscpy(TmpPath, Path );
dwErr = OscSetupMachinePassword( clientState, pTemplatePath );
if (dwErr != ERROR_SUCCESS) {
goto e0;
}
//
// Copy and process the selected SIF file
//
dwErr = OscProcessSifFile( clientState, pTemplatePath, WinntSifPath );
if ( dwErr != ERROR_SUCCESS ) {
goto e0;
}
//
// Get the boot file name
//
// Make sure there is room for Path + "\startrom.com" + NULL (so
// use sizeof to include the NULL).
//
if (wcslen(Path) + sizeof("\\startrom.com") > sizeof(Path)/sizeof(Path[0])) {
dwErr = ERROR_BAD_PATHNAME;
goto e0;
}
//
// construct default path in case the LaunchFile entry isn't
// found in the SIF file.
//
switch ( SystemArchitecture ) {
case DHCP_OPTION_CLIENT_ARCHITECTURE_IA64:
wcscat( Path, L"\\setupldr.efi" );
break;
default:
wcscat( Path, L"\\startrom.com" ); // construct default path
}
GetPrivateProfileString( OSCHOOSER_SIF_SECTIONW,
L"LaunchFile",
Path, // default
Path,
MAX_PATH,
WinntSifPath );
dwErr = OscAddVariableW( clientState, "BOOTFILE", Path );
if ( dwErr != ERROR_SUCCESS ) {
goto e0;
}
//
// Get the SIF file name
//
dwErr = OscAddVariableW( clientState, "SIFFILE", &WinntSifPath[lenIntelliMirror] );
if ( dwErr != ERROR_SUCCESS ) {
goto e0;
}
e0:
if (pMachineInfo != NULL) {
BinlDoneWithCacheEntry( pMachineInfo, FALSE );
}
return dwErr;
}
//
// Undoes whatever permanent things OscSetupClient does.
//
VOID
OscUndoSetupClient(
PCLIENT_STATE clientState
)
{
WCHAR WinntSifPath[ MAX_PATH ]; // ie "D:\RemoteInstall\tmp\NP00805F7F4C85$.sif"
PWCHAR pSifFile;
DWORD dwErr;
TraceFunc("OscUndoSetupClient( )\n");
pSifFile = OscFindVariableW( clientState, "SIFFILE" );
if ( pSifFile[0] == L'\0' ) {
return;
}
//
// Create destination SIF file path.
//
if ( _snwprintf( WinntSifPath,
sizeof(WinntSifPath) / sizeof(WinntSifPath[0]),
L"%ws\\%ws",
IntelliMirrorPathW,
pSifFile
) == -1 ) {
return;
}
//
// Impersonate so that we can get correct permissions to delete the file.
//
dwErr = OscImpersonate(clientState);
if (dwErr == ERROR_SUCCESS) {
//
// Delete the template file
//
DeleteFile( WinntSifPath );
OscRevert(clientState);
}
}
USHORT
OscPlatformToArchitecture(
PCLIENT_STATE clientState
)
/*++
Routine Description:
Translates the client architecture string value to a
DHCP_OPTION_CLIENT_ARCHITECTURE_*** flag.
Arguments:
ClientState - The client state. It's assumed that the MACHINETYPE
OSC variable has been set when you call this function. This occurs
after OSCHOICE logs on the user.
Return Value:
DHCP_OPTION_CLIENT_ARCHITECTURE_*** flag.
--*/
{
PCWSTR pArch;
pArch = OscFindVariableW( clientState, "MACHINETYPE");
if (!pArch) {
//
// if we have no architecture, just assume x86
//
return DHCP_OPTION_CLIENT_ARCHITECTURE_X86;
}
if (_wcsicmp(pArch, L"ia64") == 0) {
return DHCP_OPTION_CLIENT_ARCHITECTURE_IA64;
} else if (_wcsicmp(pArch, L"i386") == 0) {
return DHCP_OPTION_CLIENT_ARCHITECTURE_X86;
}
return DHCP_OPTION_CLIENT_ARCHITECTURE_X86;
}
DWORD
OscSetupMachinePassword(
IN PCLIENT_STATE clientState,
IN PCWSTR SifFile
)
/*++
Routine Description:
Generates and stores the machine password for later use.
Arguments:
ClientState - The client state.
SifFile - path to unattend SIF file.
Return Value:
DWORD Win32Error code indicating status of the operation.
--*/
{
WCHAR MachinePassword[LM20_PWLEN+1];
DWORD MachinePasswordLength;
PWCHAR pMachineName;
BOOL SecuredJoin;
PWCHAR pVersion;
WCHAR Answer[20];
//
// Figure out if we should be doing a secure domain join.
// In Win2K, there is no such thing, so we do the old
// style domain join with a weaker password. In all other
// cases, we use the secure domain join method.
//
pVersion = OscFindVariableW( clientState, "IMAGEVERSION" );
if (pVersion && (wcscmp(pVersion,L"5.0") == 0)) {
SecuredJoin = FALSE;
} else {
if (!GetPrivateProfileString( L"Identification",
L"DoOldStyleDomainJoin",
L"", // default
Answer,
20,
SifFile ) ||
0 == _wcsicmp(Answer, L"Yes" )) {
SecuredJoin = FALSE;
} else {
SecuredJoin = TRUE;
}
}
//
// Set up the password. For diskless clients it is the machine name
// in lowercase, for disked clients we generate a random one, making
// sure it has no NULLs in it.
//
//
// We have to change the password for DISKED machines, since
// they will have a random password that we can't query.
//
//
// Windows 2000 machines have to have the "well-known-password"
// Machine passwords are just the "MachineName" (without the "$")
//
if (!SecuredJoin) {
UINT i;
pMachineName = OscFindVariableW( clientState, "MACHINENAME" );
if (!pMachineName) {
return ERROR_INVALID_DATA;
}
memset( MachinePassword, 0, sizeof(MachinePassword) );
MachinePasswordLength = wcslen( pMachineName ) * sizeof(WCHAR);
if ( MachinePasswordLength > (LM20_PWLEN * sizeof(WCHAR)) ) {
MachinePasswordLength = LM20_PWLEN * sizeof(WCHAR);
}
//
// Lower-case the NT password.
//
for (i = 0; i < MachinePasswordLength / sizeof(WCHAR); i++) {
MachinePassword[i] = towlower(pMachineName[i]);
}
BinlPrintDbg(( DEBUG_OSC, "Using WKP\n" ));
} else {
PUCHAR psz = (PUCHAR) &MachinePassword[0];
UINT i;
OscGeneratePassword(MachinePassword, &MachinePasswordLength );
#if 0 && DBG
BinlPrintDbg(( DEBUG_OSC, "Generated password: " ));
for ( i = 0; i < MachinePasswordLength / sizeof(WCHAR); i++ ) {
BinlPrintDbg(( DEBUG_OSC, "x%02x ", psz[i] ));
}
BinlPrintDbg(( DEBUG_OSC, "\n" ));
#endif
}
RtlCopyMemory(clientState->MachineAccountPassword,MachinePassword, MachinePasswordLength);
clientState->MachineAccountPasswordLength = MachinePasswordLength;
//
// the password always consists of printable characters since it must be
// substituted into a text file.
//
OscAddVariableW( clientState, "MACHINEPASSWORD", clientState->MachineAccountPassword );
return(ERROR_SUCCESS);
}