mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1195 lines
31 KiB
1195 lines
31 KiB
/*++
|
|
|
|
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)
|
|
|