1196 lines
31 KiB
C
1196 lines
31 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
tftp.c
|
||
|
||
Abstract:
|
||
|
||
Boot loader TFTP routines.
|
||
|
||
Author:
|
||
|
||
Chuck Lenzmeier (chuckl) December 27, 1996
|
||
|
||
Revision History:
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
//
|
||
// This removes macro redefinitions which appear because we define __RPC_DOS__,
|
||
// but rpc.h defines __RPC_WIN32__
|
||
//
|
||
|
||
#pragma warning(disable:4005)
|
||
|
||
//
|
||
// As of 12/17/98, SECURITY_DOS is *not* defined - adamba
|
||
//
|
||
|
||
#if defined(SECURITY_DOS)
|
||
//
|
||
// These appear because we defined SECURITY_DOS
|
||
//
|
||
|
||
#define __far
|
||
#define __pascal
|
||
#define __loadds
|
||
#endif
|
||
|
||
#include <security.h>
|
||
#include <rpc.h>
|
||
#include <spseal.h>
|
||
|
||
#if defined(_X86_)
|
||
#include <bldrx86.h>
|
||
#endif
|
||
|
||
#if defined(SECURITY_DOS)
|
||
//
|
||
// PSECURITY_STRING is not supposed to be used when SECURITY_DOS is
|
||
// defined -- it should be a WCHAR*. Unfortunately ntlmsp.h breaks
|
||
// this rule and even uses the SECURITY_STRING structure, which there
|
||
// is really no equivalent for in 16-bit mode.
|
||
//
|
||
|
||
typedef SEC_WCHAR * SECURITY_STRING; // more-or-less the intention where it is used
|
||
typedef SEC_WCHAR * PSECURITY_STRING;
|
||
#endif
|
||
|
||
#include <ntlmsp.h>
|
||
|
||
#if DBG
|
||
ULONG NetDebugFlag =
|
||
DEBUG_ERROR |
|
||
DEBUG_CONN_ERROR |
|
||
//DEBUG_LOUD |
|
||
//DEBUG_REAL_LOUD |
|
||
//DEBUG_STATISTICS |
|
||
//DEBUG_SEND_RECEIVE |
|
||
//DEBUG_TRACE |
|
||
//DEBUG_ARP |
|
||
//DEBUG_INITIAL_BREAK |
|
||
0;
|
||
#endif
|
||
|
||
//
|
||
// Global variables
|
||
//
|
||
|
||
CONNECTION NetTftpConnection;
|
||
|
||
UCHAR NetTftpPacket[3][MAXIMUM_TFTP_PACKET_LENGTH];
|
||
|
||
#if defined(REMOTE_BOOT_SECURITY)
|
||
//
|
||
// Globals used during login.
|
||
//
|
||
|
||
static CHAR OutgoingMessage[1024];
|
||
static CHAR IncomingMessage[1024];
|
||
|
||
static CredHandle CredentialHandle;
|
||
static BOOLEAN CredentialHandleValid = FALSE;
|
||
CtxtHandle TftpClientContextHandle;
|
||
BOOLEAN TftpClientContextHandleValid = FALSE;
|
||
#endif // defined(REMOTE_BOOT_SECURITY)
|
||
|
||
//
|
||
// Local declarations.
|
||
//
|
||
|
||
NTSTATUS
|
||
TftpGet (
|
||
IN PCONNECTION Connection,
|
||
IN PTFTP_REQUEST Request
|
||
);
|
||
|
||
NTSTATUS
|
||
TftpPut (
|
||
IN PCONNECTION Connection,
|
||
IN PTFTP_REQUEST Request
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
TftpGetPut (
|
||
IN PTFTP_REQUEST Request
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
PCONNECTION connection = NULL;
|
||
ULONG FileSize;
|
||
ULONG basePage;
|
||
#if 0 && DBG
|
||
LARGE_INTEGER startTime;
|
||
LARGE_INTEGER endTime;
|
||
LARGE_INTEGER elapsedTime;
|
||
LARGE_INTEGER frequency;
|
||
ULONG seconds;
|
||
ULONG secondsFraction;
|
||
ULONG bps;
|
||
ULONG bpsFraction;
|
||
#endif
|
||
|
||
#if defined(REMOTE_BOOT)
|
||
if (!NetworkBootRom) {
|
||
|
||
// Booting from the hard disk cache because server is not available
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
#endif // defined(REMOTE_BOOT)
|
||
|
||
|
||
|
||
#ifndef EFI
|
||
//
|
||
// We don't need to do any of this initialization if
|
||
// we're in EFI.
|
||
//
|
||
|
||
|
||
FileSize = Request->MaximumLength;
|
||
|
||
status = ConnInitialize(
|
||
&connection,
|
||
Request->Operation,
|
||
Request->ServerIpAddress,
|
||
TFTP_PORT,
|
||
Request->RemoteFileName,
|
||
0,
|
||
#if defined(REMOTE_BOOT_SECURITY)
|
||
&Request->SecurityHandle, // will be set to 0 if security negotiation fails
|
||
#endif // defined(REMOTE_BOOT_SECURITY)
|
||
&FileSize
|
||
);
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
|
||
#if 0 && DBG
|
||
IF_DEBUG(STATISTICS) {
|
||
startTime = KeQueryPerformanceCounter( &frequency );
|
||
}
|
||
#endif
|
||
|
||
if ( Request->Operation == TFTP_RRQ ) {
|
||
|
||
if ( Request->MemoryAddress != NULL ) {
|
||
|
||
if ( Request->MaximumLength < FileSize ) {
|
||
ConnError(
|
||
connection,
|
||
connection->RemoteHost,
|
||
connection->RemotePort,
|
||
TFTP_ERROR_UNDEFINED,
|
||
"File too big"
|
||
);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// NB: (ChuckL) Removed code added by MattH to check for
|
||
// allocation >= 1/3 of (BlUsableLimit - BlUsableBase)
|
||
// because calling code now sets BlUsableLimit to 1 GB
|
||
// or higher.
|
||
//
|
||
|
||
|
||
status = BlAllocateAlignedDescriptor(
|
||
Request->MemoryType,
|
||
0,
|
||
BYTES_TO_PAGES(FileSize),
|
||
0,
|
||
&basePage
|
||
);
|
||
|
||
if (status != ESUCCESS) {
|
||
ConnError(
|
||
connection,
|
||
connection->RemoteHost,
|
||
connection->RemotePort,
|
||
TFTP_ERROR_UNDEFINED,
|
||
"File too big"
|
||
);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
Request->MemoryAddress = (PUCHAR)ULongToPtr( (basePage << PAGE_SHIFT) );
|
||
Request->MaximumLength = FileSize;
|
||
DPRINT( REAL_LOUD, ("TftpGetPut: allocated %d bytes at 0x%08x\n",
|
||
Request->MaximumLength, Request->MemoryAddress) );
|
||
}
|
||
|
||
status = TftpGet( connection, Request );
|
||
|
||
} else {
|
||
|
||
status = TftpPut( connection, Request );
|
||
}
|
||
|
||
#else // #ifndef EFI
|
||
|
||
if ( Request->Operation == TFTP_RRQ ) {
|
||
|
||
status = TftpGet( connection, Request );
|
||
} else {
|
||
|
||
status = TftpPut( connection, Request );
|
||
}
|
||
|
||
if( status != STATUS_SUCCESS ) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
#endif // #ifndef EFI
|
||
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
|
||
return status;
|
||
|
||
} // TftpGetPut
|
||
|
||
|
||
#ifdef EFI
|
||
|
||
extern VOID
|
||
FlipToPhysical (
|
||
);
|
||
|
||
extern VOID
|
||
FlipToVirtual (
|
||
);
|
||
|
||
NTSTATUS
|
||
TftpGet (
|
||
IN OUT PCONNECTION Connection,
|
||
IN PTFTP_REQUEST Request
|
||
)
|
||
{
|
||
EFI_STATUS Status;
|
||
CHAR16 *Size = NULL;
|
||
PVOID MyBuffer = NULL;
|
||
EFI_IP_ADDRESS MyServerIpAddress;
|
||
INTN Count = 0;
|
||
INTN BufferSizeX = sizeof(CHAR16);
|
||
ULONG basePage;
|
||
UINTN BlockSize = 512;
|
||
|
||
//
|
||
// They sent us an IP address as a ULONG. We need to convert
|
||
// that into an EFI_IP_ADDRESS.
|
||
//
|
||
for( Count = 0; Count < 4; Count++ ) {
|
||
MyServerIpAddress.v4.Addr[Count] = PXEClient->Mode->ProxyOffer.Dhcpv4.BootpSiAddr[Count];
|
||
}
|
||
|
||
|
||
//
|
||
// Get the file size, allocate some memory, then get the file.
|
||
//
|
||
FlipToPhysical();
|
||
Status = PXEClient->Mtftp( PXEClient,
|
||
EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
|
||
Size,
|
||
TRUE,
|
||
&BufferSizeX,
|
||
&BlockSize,
|
||
&MyServerIpAddress,
|
||
Request->RemoteFileName,
|
||
0,
|
||
FALSE );
|
||
FlipToVirtual();
|
||
|
||
|
||
if( Status != EFI_SUCCESS ) {
|
||
|
||
return (NTSTATUS)Status;
|
||
|
||
}
|
||
|
||
Status = BlAllocateAlignedDescriptor(
|
||
Request->MemoryType,
|
||
0,
|
||
(ULONG) BYTES_TO_PAGES(BufferSizeX),
|
||
0,
|
||
&basePage
|
||
);
|
||
|
||
if ( Status != ESUCCESS ) {
|
||
|
||
if( BdDebuggerEnabled ) {
|
||
DbgPrint( "TftpGet: BlAllocate failed! (%d)\n", Status );
|
||
}
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
Request->MemoryAddress = (PUCHAR)ULongToPtr( (basePage << PAGE_SHIFT) );
|
||
Request->MaximumLength = (ULONG)BufferSizeX;
|
||
|
||
|
||
//
|
||
// Make sure we send EFI a physical address.
|
||
//
|
||
MyBuffer = (PVOID)((ULONGLONG)(Request->MemoryAddress) & ~KSEG0_BASE);
|
||
|
||
FlipToPhysical();
|
||
Status = PXEClient->Mtftp( PXEClient,
|
||
EFI_PXE_BASE_CODE_TFTP_READ_FILE,
|
||
MyBuffer,
|
||
TRUE,
|
||
&BufferSizeX,
|
||
NULL,
|
||
&MyServerIpAddress,
|
||
Request->RemoteFileName,
|
||
0,
|
||
FALSE );
|
||
FlipToVirtual();
|
||
|
||
if( Status != EFI_SUCCESS ) {
|
||
|
||
if( BdDebuggerEnabled ) {
|
||
DbgPrint( "TftpGet: GetFile failed! (%d)\n", Status );
|
||
}
|
||
return (NTSTATUS)Status;
|
||
|
||
}
|
||
|
||
|
||
|
||
Request->BytesTransferred = (ULONG)BufferSizeX;
|
||
|
||
return (NTSTATUS)Status;
|
||
|
||
} // TftpGet
|
||
|
||
|
||
NTSTATUS
|
||
TftpPut (
|
||
IN OUT PCONNECTION Connection,
|
||
IN PTFTP_REQUEST Request
|
||
)
|
||
{
|
||
EFI_STATUS Status;
|
||
EFI_IP_ADDRESS MyServerIpAddress;
|
||
INTN Count = 0;
|
||
PVOID MyBuffer = NULL;
|
||
|
||
|
||
//
|
||
// They sent us an IP address as a ULONG. We need to convert
|
||
// that into an EFI_IP_ADDRESS.
|
||
//
|
||
for( Count = 0; Count < 4; Count++ ) {
|
||
MyServerIpAddress.v4.Addr[Count] = PXEClient->Mode->ProxyOffer.Dhcpv4.BootpSiAddr[Count];
|
||
}
|
||
|
||
//
|
||
// Make sure we send EFI a physical address.
|
||
//
|
||
MyBuffer = (PVOID)((ULONGLONG)(Request->MemoryAddress) & ~KSEG0_BASE);
|
||
|
||
FlipToPhysical();
|
||
Status = PXEClient->Mtftp( PXEClient,
|
||
EFI_PXE_BASE_CODE_TFTP_WRITE_FILE,
|
||
MyBuffer,
|
||
TRUE,
|
||
(UINTN *)(&Request->MaximumLength),
|
||
NULL,
|
||
&MyServerIpAddress,
|
||
Request->RemoteFileName,
|
||
0,
|
||
FALSE );
|
||
FlipToVirtual();
|
||
|
||
if( Status != EFI_SUCCESS ) {
|
||
|
||
if( BdDebuggerEnabled ) {
|
||
DbgPrint( "TftpPut: WriteFile failed! (%d)\n", Status );
|
||
}
|
||
|
||
}
|
||
|
||
return (NTSTATUS)Status;
|
||
|
||
} // TftpPut
|
||
|
||
#else // #ifdef EFI
|
||
|
||
NTSTATUS
|
||
TftpGet (
|
||
IN OUT PCONNECTION Connection,
|
||
IN PTFTP_REQUEST Request
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
PTFTP_PACKET packet;
|
||
ULONG length;
|
||
ULONG offset;
|
||
PUCHAR packetData;
|
||
ULONG lastProgressPercent = -1;
|
||
ULONG currentProgressPercent;
|
||
#if defined(REMOTE_BOOT_SECURITY)
|
||
UCHAR Sign[NTLMSSP_MESSAGE_SIGNATURE_SIZE];
|
||
SecBufferDesc SignMessage;
|
||
SecBuffer SigBuffers[2];
|
||
SECURITY_STATUS SecStatus;
|
||
#endif // defined(REMOTE_BOOT_SECURITY)
|
||
|
||
DPRINT( TRACE, ("TftpGet\n") );
|
||
|
||
#if defined(REMOTE_BOOT)
|
||
if (!NetworkBootRom) {
|
||
|
||
// Booting from the hard disk cache because server is not available
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
#endif // defined(REMOTE_BOOT)
|
||
|
||
offset = 0;
|
||
|
||
if ( Request->ShowProgress ) {
|
||
BlUpdateProgressBar(0);
|
||
}
|
||
|
||
do {
|
||
|
||
status = ConnReceive( Connection, &packet );
|
||
if ( !NT_SUCCESS(status) ) {
|
||
break;
|
||
}
|
||
|
||
length = Connection->CurrentLength - 4;
|
||
|
||
#if defined(REMOTE_BOOT_SECURITY)
|
||
//
|
||
// If we are doing a security transfer, then the first packet
|
||
// has the sign in it, so put that in the Sign buffer first.
|
||
//
|
||
|
||
if (Request->SecurityHandle && (offset == 0)) {
|
||
|
||
if (length < NTLMSSP_MESSAGE_SIGNATURE_SIZE) {
|
||
status = STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
break;
|
||
}
|
||
memcpy(Sign, packet->Data, NTLMSSP_MESSAGE_SIGNATURE_SIZE);
|
||
packetData = packet->Data + NTLMSSP_MESSAGE_SIGNATURE_SIZE;
|
||
length -= NTLMSSP_MESSAGE_SIGNATURE_SIZE;
|
||
|
||
} else
|
||
#endif // defined(REMOTE_BOOT_SECURITY)
|
||
{
|
||
|
||
packetData = packet->Data;
|
||
}
|
||
|
||
if ( (offset + length) > Request->MaximumLength ) {
|
||
length = Request->MaximumLength - offset;
|
||
}
|
||
|
||
RtlCopyMemory( Request->MemoryAddress + offset, packetData, length );
|
||
|
||
offset += length;
|
||
|
||
if ( Request->ShowProgress ) {
|
||
currentProgressPercent = (ULONG)(((ULONGLONG)offset * 100) / Request->MaximumLength);
|
||
if ( currentProgressPercent != lastProgressPercent ) {
|
||
BlUpdateProgressBar( currentProgressPercent );
|
||
}
|
||
lastProgressPercent = currentProgressPercent;
|
||
}
|
||
|
||
//
|
||
// End the loop when we get a packet smaller than the max size --
|
||
// the extra check is to handle the first packet (length == offset)
|
||
// since we get NTLMSSP_MESSAGE_SIGNATURE_SIZE bytes less.
|
||
//
|
||
|
||
} while ( (length == Connection->BlockSize)
|
||
#if defined(REMOTE_BOOT_SECURITY)
|
||
|| ((length == offset) &&
|
||
(length == (Connection->BlockSize - NTLMSSP_MESSAGE_SIGNATURE_SIZE)))
|
||
#endif // defined(REMOTE_BOOT_SECURITY)
|
||
);
|
||
|
||
#if defined(REMOTE_BOOT_SECURITY)
|
||
if (Request->SecurityHandle) {
|
||
|
||
//
|
||
// Unseal the message if it was encrypted.
|
||
//
|
||
|
||
SigBuffers[1].pvBuffer = Sign;
|
||
SigBuffers[1].cbBuffer = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
|
||
SigBuffers[1].BufferType = SECBUFFER_TOKEN;
|
||
|
||
SigBuffers[0].pvBuffer = Request->MemoryAddress;
|
||
SigBuffers[0].cbBuffer = offset;
|
||
SigBuffers[0].BufferType = SECBUFFER_DATA;
|
||
|
||
SignMessage.pBuffers = SigBuffers;
|
||
SignMessage.cBuffers = 2;
|
||
SignMessage.ulVersion = 0;
|
||
|
||
ASSERT (TftpClientContextHandleValid);
|
||
|
||
SecStatus = UnsealMessage(
|
||
&TftpClientContextHandle,
|
||
&SignMessage,
|
||
0,
|
||
0 );
|
||
|
||
if ( SecStatus != SEC_E_OK ) {
|
||
DPRINT( ERROR, ("TftpGet: UnsealMessage failed %x\n", SecStatus) );
|
||
status = STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
}
|
||
|
||
}
|
||
#endif // defined(REMOTE_BOOT_SECURITY)
|
||
|
||
Request->BytesTransferred = offset;
|
||
|
||
return status;
|
||
|
||
} // TftpGet
|
||
|
||
|
||
NTSTATUS
|
||
TftpPut (
|
||
IN OUT PCONNECTION Connection,
|
||
IN PTFTP_REQUEST Request
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
PTFTP_PACKET packet;
|
||
ULONG length;
|
||
ULONG offset;
|
||
|
||
DPRINT( TRACE, ("TftpPut\n") );
|
||
|
||
#if defined(REMOTE_BOOT)
|
||
if (!NetworkBootRom) {
|
||
|
||
// Booting from the hard disk cache because server is not available
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
#endif // defined(REMOTE_BOOT)
|
||
|
||
offset = 0;
|
||
|
||
do {
|
||
|
||
packet = ConnPrepareSend( Connection );
|
||
|
||
length = Connection->BlockSize;
|
||
if ( (offset + length) > Request->MaximumLength ) {
|
||
length = Request->MaximumLength - offset;
|
||
}
|
||
|
||
RtlCopyMemory( packet->Data, Request->MemoryAddress + offset, length );
|
||
|
||
status = ConnSend( Connection, length );
|
||
if ( !NT_SUCCESS(status) ) {
|
||
break;
|
||
}
|
||
|
||
offset += length;
|
||
|
||
} while ( length == Connection->BlockSize );
|
||
|
||
Request->BytesTransferred = offset;
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
status = ConnWaitForFinalAck( Connection );
|
||
}
|
||
|
||
return status;
|
||
|
||
} // TftpPut
|
||
#endif // #if defined(_IA64_)
|
||
|
||
#if defined(REMOTE_BOOT_SECURITY)
|
||
NTSTATUS
|
||
UdpSendAndReceiveForTftp(
|
||
IN PVOID SendBuffer,
|
||
IN ULONG SendBufferLength,
|
||
IN ULONG SendRemoteHost,
|
||
IN USHORT SendRemotePort,
|
||
IN ULONG SendRetryCount,
|
||
IN PVOID ReceiveBuffer,
|
||
IN ULONG ReceiveBufferLength,
|
||
OUT PULONG ReceiveRemoteHost,
|
||
OUT PUSHORT ReceiveRemotePort,
|
||
IN ULONG ReceiveTimeout,
|
||
IN USHORT ReceiveSequenceNumber
|
||
)
|
||
{
|
||
ULONG i, j;
|
||
ULONG length;
|
||
|
||
//
|
||
// Try sending the packet SendRetryCount times, until we receive
|
||
// a response with the right sequence number, waiting ReceiveTimeout
|
||
// each time.
|
||
//
|
||
|
||
for (i = 0; i < SendRetryCount; i++) {
|
||
|
||
length = UdpSend(
|
||
SendBuffer,
|
||
SendBufferLength,
|
||
SendRemoteHost,
|
||
SendRemotePort);
|
||
|
||
if ( length != SendBufferLength ) {
|
||
DPRINT( ERROR, ("UdpSend only sent %d bytes, not %d\n", length, SendBufferLength) );
|
||
return STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
}
|
||
|
||
ReReceive:
|
||
|
||
//
|
||
// NULL out the first 12 bytes in case we get shorter data.
|
||
//
|
||
|
||
memset(ReceiveBuffer, 0x0, 12);
|
||
|
||
length = UdpReceive(
|
||
ReceiveBuffer,
|
||
ReceiveBufferLength,
|
||
ReceiveRemoteHost,
|
||
ReceiveRemotePort,
|
||
ReceiveTimeout);
|
||
|
||
if ( length == 0 ) {
|
||
DPRINT( ERROR, ("UdpReceive timed out sending %d, %d sends\n", SendBufferLength, i) );
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Make sure that it is a TFTP security packet.
|
||
//
|
||
|
||
if (((USHORT UNALIGNED *)ReceiveBuffer)[0] != SWAP_WORD(0x10)) {
|
||
DPRINT( ERROR, ("UdpReceive not a TFTP packet\n") );
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Make sure that the sequence number is correct -- what we
|
||
// expect, or 0xffff.
|
||
//
|
||
|
||
if ((((USHORT UNALIGNED *)ReceiveBuffer)[1] == SWAP_WORD(0xffff)) ||
|
||
(((USHORT UNALIGNED *)ReceiveBuffer)[1] == SWAP_WORD(ReceiveSequenceNumber))) {
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
DPRINT( ERROR, ("UdpReceive expected seq %d, got %d\n",
|
||
ReceiveSequenceNumber, ((UCHAR *)ReceiveBuffer)[3]) );
|
||
|
||
}
|
||
|
||
DPRINT( ERROR, ("UdpReceive got wrong signature\n") );
|
||
|
||
// CLEAN THIS UP -- but the idea is not to UdpSend again just
|
||
// because we got a bad signature. Still need to respect the
|
||
// original ReceiveTimeout however!
|
||
|
||
goto ReReceive;
|
||
|
||
}
|
||
|
||
//
|
||
// We timed out.
|
||
//
|
||
|
||
return STATUS_TIMEOUT;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
TftpLogin (
|
||
IN PUCHAR Domain,
|
||
IN PUCHAR Name,
|
||
IN PUCHAR OwfPassword,
|
||
IN ULONG ServerIpAddress,
|
||
OUT PULONG LoginHandle
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
ARC_STATUS Status;
|
||
SECURITY_STATUS SecStatus;
|
||
NTSTATUS remoteStatus;
|
||
SecBufferDesc NegotiateDesc;
|
||
SecBuffer NegotiateBuffer;
|
||
SecBufferDesc ChallengeDesc;
|
||
SecBuffer ChallengeBuffer;
|
||
SecBufferDesc AuthenticateDesc;
|
||
SecBuffer AuthenticateBuffer;
|
||
ULONG ContextAttributes;
|
||
SEC_WINNT_AUTH_IDENTITY AuthIdentity;
|
||
TimeStamp Lifetime;
|
||
ULONG RemoteHost;
|
||
USHORT RemotePort;
|
||
ULONG LoginHeaderLength;
|
||
USHORT LocalPort;
|
||
PUCHAR OptionLoc;
|
||
ULONG MaxToken;
|
||
PSecPkgInfo PackageInfo;
|
||
|
||
#if defined(REMOTE_BOOT)
|
||
if (!NetworkBootRom) {
|
||
|
||
// Booting from the hard disk cache because server is not available
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
#endif // defined(REMOTE_BOOT)
|
||
|
||
//
|
||
// Get ourselves a UDP port.
|
||
//
|
||
|
||
LocalPort = UdpAssignUnicastPort();
|
||
|
||
|
||
//
|
||
// Delete both contexts if needed.
|
||
//
|
||
|
||
if (TftpClientContextHandleValid) {
|
||
|
||
SecStatus = DeleteSecurityContext( &TftpClientContextHandle );
|
||
TftpClientContextHandleValid = FALSE;
|
||
|
||
}
|
||
|
||
if (CredentialHandleValid) {
|
||
|
||
SecStatus = FreeCredentialsHandle( &CredentialHandle );
|
||
CredentialHandleValid = FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// Get info about the security packages.
|
||
//
|
||
|
||
SecStatus = QuerySecurityPackageInfoA( NTLMSP_NAME_A, &PackageInfo );
|
||
|
||
if ( SecStatus != SEC_E_OK ) {
|
||
DPRINT( ERROR, ("QuerySecurityPackageInfo failed %d", SecStatus) );
|
||
return (RtlMapSecurityErrorToNtStatus(SecStatus));
|
||
}
|
||
|
||
MaxToken = PackageInfo->cbMaxToken;
|
||
FreeContextBuffer(PackageInfo);
|
||
|
||
|
||
//
|
||
// Acquire a credential handle for the client side
|
||
//
|
||
|
||
|
||
RtlZeroMemory( &AuthIdentity, sizeof(AuthIdentity) );
|
||
|
||
AuthIdentity.Domain = Domain;
|
||
AuthIdentity.User = Name;
|
||
AuthIdentity.Password = OwfPassword;
|
||
|
||
SecStatus = AcquireCredentialsHandleA(
|
||
NULL, // New principal
|
||
NTLMSP_NAME_A, // Package Name
|
||
SECPKG_CRED_OUTBOUND | SECPKG_CRED_OWF_PASSWORD,
|
||
NULL,
|
||
&AuthIdentity,
|
||
NULL,
|
||
NULL,
|
||
&CredentialHandle,
|
||
&Lifetime );
|
||
|
||
if ( SecStatus != SEC_E_OK ) {
|
||
DPRINT( ERROR, ("AcquireCredentialsHandle failed: %s ", SecStatus) );
|
||
return (RtlMapSecurityErrorToNtStatus(SecStatus));
|
||
}
|
||
|
||
CredentialHandleValid = TRUE;
|
||
|
||
//
|
||
// Get the NegotiateMessage (ClientSide)
|
||
//
|
||
|
||
((USHORT UNALIGNED *)OutgoingMessage)[0] = SWAP_WORD(0x10); // TFTP packet type 16
|
||
|
||
memcpy(OutgoingMessage+2, "login", 6); // copy the final \0 also
|
||
strcpy(OutgoingMessage+8, NTLMSP_NAME_A);
|
||
|
||
NegotiateDesc.ulVersion = 0;
|
||
NegotiateDesc.cBuffers = 1;
|
||
NegotiateDesc.pBuffers = &NegotiateBuffer;
|
||
|
||
NegotiateBuffer.cbBuffer = MaxToken;
|
||
NegotiateBuffer.BufferType = SECBUFFER_TOKEN;
|
||
|
||
// allow 8 for the type and "login", then NTLMSP_NAME_A + 1 for the \0,
|
||
// plus four bytes for the length.
|
||
LoginHeaderLength = 8 + strlen(NTLMSP_NAME_A) + 1;
|
||
NegotiateBuffer.pvBuffer = OutgoingMessage + LoginHeaderLength + 4;
|
||
|
||
SecStatus = InitializeSecurityContextA(
|
||
&CredentialHandle,
|
||
NULL, // No Client context yet
|
||
NULL,
|
||
ISC_REQ_SEQUENCE_DETECT,
|
||
0, // Reserved 1
|
||
SECURITY_NATIVE_DREP,
|
||
NULL, // No initial input token
|
||
0, // Reserved 2
|
||
&TftpClientContextHandle,
|
||
&NegotiateDesc,
|
||
&ContextAttributes,
|
||
&Lifetime );
|
||
|
||
if ( (SecStatus != SEC_E_OK) && (SecStatus != SEC_I_CONTINUE_NEEDED) ) {
|
||
DPRINT( ERROR, ("InitializeSecurityContext (negotiate): %d", SecStatus) );
|
||
return (RtlMapSecurityErrorToNtStatus(SecStatus));
|
||
}
|
||
|
||
TftpClientContextHandleValid = TRUE;
|
||
|
||
//
|
||
// Send the negotiate buffer to the server and wait for a response.
|
||
//
|
||
|
||
*((ULONG UNALIGNED *)(OutgoingMessage + LoginHeaderLength)) =
|
||
SWAP_DWORD(NegotiateBuffer.cbBuffer);
|
||
|
||
Status = UdpSendAndReceiveForTftp(
|
||
OutgoingMessage,
|
||
NegotiateBuffer.cbBuffer + LoginHeaderLength + 4,
|
||
ServerIpAddress,
|
||
TFTP_PORT,
|
||
10, // retry count
|
||
IncomingMessage,
|
||
MaxToken + 8,
|
||
&RemoteHost,
|
||
&RemotePort,
|
||
3, // receive timeout
|
||
0); // sequence number);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
DPRINT( ERROR, ("UdpSendAndReceiveForTftp status is %x\n", Status) );
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Get the AuthenticateMessage (ClientSide)
|
||
//
|
||
|
||
AuthenticateDesc.ulVersion = 0;
|
||
AuthenticateDesc.cBuffers = 1;
|
||
AuthenticateDesc.pBuffers = &AuthenticateBuffer;
|
||
|
||
AuthenticateBuffer.cbBuffer = MaxToken;
|
||
AuthenticateBuffer.BufferType = SECBUFFER_TOKEN;
|
||
AuthenticateBuffer.pvBuffer = OutgoingMessage + 8;
|
||
|
||
ChallengeDesc.ulVersion = 0;
|
||
ChallengeDesc.cBuffers = 1;
|
||
ChallengeDesc.pBuffers = &ChallengeBuffer;
|
||
|
||
ChallengeBuffer.cbBuffer = SWAP_DWORD(((ULONG UNALIGNED *)IncomingMessage)[1]);
|
||
ChallengeBuffer.BufferType = SECBUFFER_TOKEN | SECBUFFER_READONLY;
|
||
ChallengeBuffer.pvBuffer = IncomingMessage + 8;
|
||
|
||
SecStatus = InitializeSecurityContextA(
|
||
NULL,
|
||
&TftpClientContextHandle,
|
||
NULL,
|
||
0,
|
||
0, // Reserved 1
|
||
SECURITY_NATIVE_DREP,
|
||
&ChallengeDesc,
|
||
0, // Reserved 2
|
||
&TftpClientContextHandle,
|
||
&AuthenticateDesc,
|
||
&ContextAttributes,
|
||
&Lifetime );
|
||
|
||
if ( (SecStatus != SEC_E_OK) ) {
|
||
DPRINT( ERROR, ("InitializeSecurityContext (Authenticate): %d\n", SecStatus) );
|
||
return (RtlMapSecurityErrorToNtStatus(SecStatus));
|
||
}
|
||
|
||
//
|
||
// Send the authenticate buffer to the server and wait for the response.
|
||
//
|
||
|
||
((USHORT UNALIGNED *)OutgoingMessage)[0] = SWAP_WORD(0x10); // TFTP packet type 16
|
||
((USHORT UNALIGNED *)OutgoingMessage)[1] = SWAP_WORD(0x00); // sequence number 0
|
||
|
||
((ULONG UNALIGNED *)OutgoingMessage)[1] = SWAP_DWORD(AuthenticateBuffer.cbBuffer);
|
||
|
||
Status = UdpSendAndReceiveForTftp(
|
||
OutgoingMessage,
|
||
AuthenticateBuffer.cbBuffer + 8,
|
||
ServerIpAddress,
|
||
RemotePort, // send it to whatever port he sent from
|
||
10, // retry count
|
||
IncomingMessage,
|
||
MaxToken + 8,
|
||
&RemoteHost,
|
||
&RemotePort,
|
||
5, // receive timeout
|
||
1); // sequence number (but we really expect 0xffff)
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
DPRINT( ERROR, ("UdpSendAndReceiveForTftp status is %x\n", Status) );
|
||
return Status;
|
||
}
|
||
|
||
if (((USHORT UNALIGNED *)IncomingMessage)[1] == SWAP_WORD(0xffff)) {
|
||
|
||
//
|
||
// Send a response to the server, but don't bother trying to
|
||
// resend it, since if he doesn't see it he eventually
|
||
// times out.
|
||
//
|
||
|
||
((USHORT UNALIGNED *)OutgoingMessage)[0] = SWAP_WORD(0x10); // TFTP packet type 16
|
||
((USHORT UNALIGNED *)OutgoingMessage)[1] = SWAP_WORD(0xffff); // sequence number 0
|
||
|
||
UdpSend(
|
||
OutgoingMessage,
|
||
4,
|
||
ServerIpAddress,
|
||
RemotePort);
|
||
|
||
//
|
||
// Parse the result to see if we succeeded.
|
||
//
|
||
|
||
OptionLoc = IncomingMessage + 4;
|
||
|
||
if (memcmp(OptionLoc, "status", 6) != 0) {
|
||
DPRINT( ERROR, ("Login response has no status!!\n") );
|
||
status = STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
}
|
||
|
||
OptionLoc += strlen("status") + 1;
|
||
|
||
remoteStatus = ConnSafeAtol(OptionLoc, OptionLoc+20); // end doesn't matter because it is NULL-terminated
|
||
|
||
if (remoteStatus == STATUS_SUCCESS) {
|
||
|
||
OptionLoc += strlen(OptionLoc) + 1;
|
||
|
||
if (memcmp(OptionLoc, "handle", 6) != 0) {
|
||
DPRINT( ERROR, ("Login success response has no handle!!\n") );
|
||
status = STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
} else {
|
||
OptionLoc += strlen("handle") + 1;
|
||
*LoginHandle = ConnSafeAtol(OptionLoc, OptionLoc+20); // end doesn't matter because it is NULL-terminated
|
||
DPRINT( ERROR, ("TftpLogin SUCCESS, remoteHandle %d\n", *LoginHandle) );
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
} else {
|
||
DPRINT( ERROR, ("Login reported failure %x\n", remoteStatus) );
|
||
status = remoteStatus;
|
||
}
|
||
|
||
} else {
|
||
|
||
DPRINT( ERROR, ("Got strange response to negotiate!!\n") );
|
||
status = STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
} // TftpLogin
|
||
|
||
|
||
NTSTATUS
|
||
TftpLogoff (
|
||
IN ULONG ServerIpAddress,
|
||
IN ULONG LoginHandle
|
||
)
|
||
{
|
||
SECURITY_STATUS SecStatus;
|
||
NTSTATUS status;
|
||
ULONG Status;
|
||
ULONG stringSize;
|
||
PUCHAR options;
|
||
ULONG length;
|
||
ULONG RemoteHost;
|
||
USHORT RemotePort;
|
||
|
||
#if defined(REMOTE_BOOT)
|
||
if (!NetworkBootRom) {
|
||
|
||
// Booting from the hard disk cache because server is not available
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
#endif // defined(REMOTE_BOOT)
|
||
|
||
//
|
||
// Delete both contexts if needed.
|
||
//
|
||
|
||
if (TftpClientContextHandleValid) {
|
||
|
||
SecStatus = DeleteSecurityContext( &TftpClientContextHandle );
|
||
TftpClientContextHandleValid = FALSE;
|
||
|
||
}
|
||
|
||
if (CredentialHandleValid) {
|
||
|
||
SecStatus = FreeCredentialsHandle( &CredentialHandle );
|
||
CredentialHandleValid = FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// Send the logoff message to the server.
|
||
//
|
||
|
||
((USHORT UNALIGNED *)OutgoingMessage)[0] = SWAP_WORD(0x10); // TFTP packet type 16
|
||
|
||
memcpy(OutgoingMessage+2, "logoff", 7); // copy the final \0 also
|
||
strcpy(OutgoingMessage+9, NTLMSP_NAME_A);
|
||
|
||
// allow 9 for the type and "logoff", then NTLMSP_NAME_A + 1 for the \0.
|
||
length= 9 + strlen(NTLMSP_NAME_A) + 1;
|
||
options = OutgoingMessage + length;
|
||
|
||
strcpy( options, "security" );
|
||
length += sizeof("security");
|
||
options += sizeof("security");
|
||
stringSize = ConnItoa( LoginHandle, options );
|
||
length += stringSize;
|
||
options += stringSize;
|
||
|
||
|
||
Status = UdpSendAndReceiveForTftp(
|
||
OutgoingMessage,
|
||
length,
|
||
ServerIpAddress,
|
||
TFTP_PORT,
|
||
3, // retry count
|
||
IncomingMessage,
|
||
512, // size - we don't expect a big response
|
||
&RemoteHost,
|
||
&RemotePort,
|
||
2, // receive timeout
|
||
0); // sequence number (but we really expect 0xffff)
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
DPRINT( ERROR, ("UdpSendAndReceiveForTftp status is %d\n", Status) );
|
||
return STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
}
|
||
|
||
if (((USHORT UNALIGNED *)IncomingMessage)[1] == SWAP_WORD(0xffff)) {
|
||
|
||
//
|
||
// Send a response to the server, but don't bother trying to
|
||
// resend it, since if he doesn't see it he eventually
|
||
// times out.
|
||
//
|
||
|
||
((USHORT UNALIGNED *)OutgoingMessage)[0] = SWAP_WORD(0x10); // TFTP packet type 16
|
||
((USHORT UNALIGNED *)OutgoingMessage)[1] = SWAP_WORD(0xffff); // sequence number 0
|
||
|
||
UdpSend(
|
||
OutgoingMessage,
|
||
4,
|
||
ServerIpAddress,
|
||
RemotePort);
|
||
|
||
//
|
||
// The status code follows the 0xffff, but for the moment we
|
||
// don't care.
|
||
//
|
||
|
||
DPRINT( ERROR, ("TftpLogoff SUCCESS, remoteHandle %d\n", LoginHandle) );
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
DPRINT( ERROR, ("Got strange response to logoff!!\n") );
|
||
status = STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
} // TftpLogoff
|
||
|
||
|
||
NTSTATUS
|
||
TftpSignString (
|
||
IN PUCHAR String,
|
||
OUT PUCHAR * Sign,
|
||
OUT ULONG * SignLength
|
||
)
|
||
{
|
||
SECURITY_STATUS SecStatus;
|
||
SecBufferDesc SignMessage;
|
||
SecBuffer SigBuffers[2];
|
||
static UCHAR StaticSign[NTLMSSP_MESSAGE_SIGNATURE_SIZE];
|
||
|
||
//
|
||
// Sign the name and send that, to make sure it is not changed.
|
||
//
|
||
|
||
SigBuffers[1].pvBuffer = StaticSign;
|
||
SigBuffers[1].cbBuffer = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
|
||
SigBuffers[1].BufferType = SECBUFFER_TOKEN;
|
||
|
||
SigBuffers[0].pvBuffer = String;
|
||
SigBuffers[0].cbBuffer = strlen(String);
|
||
SigBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
|
||
|
||
SignMessage.pBuffers = SigBuffers;
|
||
SignMessage.cBuffers = 2;
|
||
SignMessage.ulVersion = 0;
|
||
|
||
ASSERT (TftpClientContextHandleValid);
|
||
|
||
SecStatus = MakeSignature(
|
||
&TftpClientContextHandle,
|
||
0,
|
||
&SignMessage,
|
||
0 );
|
||
|
||
if ( SecStatus != SEC_E_OK ) {
|
||
DPRINT( ERROR, ("TftpSignString: MakeSignature: %lx\n", SecStatus) );
|
||
return STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
}
|
||
|
||
*Sign = StaticSign;
|
||
*SignLength = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
#endif // defined(REMOTE_BOOT_SECURITY)
|
||
|