/*++ Copyright (c) 1992-1996 Microsoft Corporation Module Name: tftpd.c Abstract: This implements an RFC 783 tftp daemon. The tftp daemon listens on it's well-known port waiting for requests. When a valid request comes in, it spawns a thread to process the request. Functions Defined: TftpdErrorPacket - sends an error reply. TftpdDoRead - read from file and convert. TftpdHandleRead - incoming read file request. read file => sendto. TftpdDoWrite - convert and write to file. TftpdHandleWrite - incoming write file request, calls TftpdDoWrite(). Author: Sam Patton (sampa) 08-apr-1992 Revision History: MohsinA, 02-Dec-96. --*/ #include "tftpd.h" #if defined(REMOTE_BOOT_SECURITY) #include #endif // defined(REMOTE_BOOT_SECURITY) extern TFTP_GLOBALS Globals; char * ErrorString[NUM_TFTP_ERROR_CODES] = { "Error undefined", "File not found", "Access violation", "Disk full or allocation exceeded", "Illegal TFTP operation", "Unknown transfer ID", "File already exists", "No such user", "Option negotiation failure" }; #if defined(REMOTE_BOOT_SECURITY) // // These routines manage the security info structures for clients // thay have logged in. I put all the code that deals with how the // structures are actually stored in these functions, in case I // change it. // PTFTPD_SECURITY SecurityArray = NULL; USHORT SecurityArrayLength = 0; USHORT SecurityValidation; CRITICAL_SECTION SecurityCriticalSection; #define INITIAL_SECURITY_ARRAY_SIZE 8 UCHAR TftpdHexDigitToChar( PUCHAR HexDigit ) { UCHAR Nibble; UCHAR ReturnValue = 0; int i; for (i = 0; i < 2; i++) { if ((HexDigit[i] >= '0') && (HexDigit[i] <= '9')) { Nibble = (UCHAR)(HexDigit[i] - '0'); } else if ((HexDigit[i] >= 'a') && (HexDigit[i] <= 'f')) { Nibble = (UCHAR)(HexDigit[i] - 'a' + 10); } else if ((HexDigit[i] >= 'A') && (HexDigit[i] <= 'F')) { Nibble = (UCHAR)(HexDigit[i] - 'A' + 10); } else { Nibble = 0; } ReturnValue = (UCHAR)((ReturnValue << 4) + Nibble); } return ReturnValue; } BOOL TftpdInitSecurityArray( VOID ) { int i; SecurityArray = (PTFTPD_SECURITY)malloc(sizeof(TFTPD_SECURITY) * INITIAL_SECURITY_ARRAY_SIZE); if (SecurityArray == NULL) { DbgPrint("TftpdInitSecurityArray: cannot allocate security array\n"); return FALSE; } for (i = 0; i < INITIAL_SECURITY_ARRAY_SIZE; i++) { SecurityArray[i].Validation = 0; // means this entry is free SecurityArray[i].LastFileRead[0] = '\0'; } SecurityArrayLength = INITIAL_SECURITY_ARRAY_SIZE; srand((unsigned)time(NULL)); SecurityValidation = (USHORT)rand(); RtlInitializeCriticalSection(&SecurityCriticalSection); return TRUE; } VOID TftpdUninitSecurityArray( VOID ) { free(SecurityArray); } BOOL TftpdAllocateSecurityEntry( PUSHORT Index, PTFTPD_SECURITY Security ) { USHORT i, j; RtlEnterCriticalSection (&SecurityCriticalSection); for (i = 0; i < SecurityArrayLength; i++) { if (SecurityArray[i].Validation == 0) { break; } } if (i == SecurityArrayLength) { PTFTPD_SECURITY NewSecurity; USHORT NewSecurityLength; // // Could not find a spot, double the array. // if (SecurityArrayLength < 0x8000) { NewSecurityLength = SecurityArrayLength * 2; } else { NewSecurityLength = 0xffff; } NewSecurity = malloc(sizeof(TFTPD_SECURITY) * NewSecurityLength); if (NewSecurity == NULL) { RtlLeaveCriticalSection (&SecurityCriticalSection); return FALSE; } memcpy(NewSecurity, SecurityArray, sizeof(TFTPD_SECURITY) * SecurityArrayLength); i = SecurityArrayLength; for (j = SecurityArrayLength; j < NewSecurityLength; j++) { NewSecurity[j].Validation = 0; // means this entry is free NewSecurity[j].LastFileRead[0] = '\0'; } SecurityArray = NewSecurity; SecurityArrayLength = NewSecurityLength; } SecurityArray[i].Validation = SecurityValidation; SecurityArray[i].CredentialsHandleValid = FALSE; SecurityArray[i].ServerContextHandleValid = FALSE; SecurityArray[i].GeneratedKey = FALSE; SecurityValidation = (SecurityValidation % 10000) + 1; *Security = SecurityArray[i]; RtlLeaveCriticalSection (&SecurityCriticalSection); *Index = i; return TRUE; } VOID TftpdFreeSecurityEntry( USHORT Index ) { TFTPD_SECURITY TempSecurity; // save it so we can leave the critical section RtlEnterCriticalSection (&SecurityCriticalSection); if (Index < SecurityArrayLength) { TempSecurity = SecurityArray[Index]; SecurityArray[Index].Validation = 0; // this marks it as free RtlLeaveCriticalSection (&SecurityCriticalSection); if (TempSecurity.ServerContextHandleValid) { DeleteSecurityContext(&TempSecurity.ServerContextHandle); } if (TempSecurity.CredentialsHandleValid) { FreeCredentialsHandle(&TempSecurity.CredentialsHandle); } } else { RtlLeaveCriticalSection (&SecurityCriticalSection); } } VOID TftpdGetSecurityEntry( USHORT Index, PTFTPD_SECURITY Security ) { RtlEnterCriticalSection (&SecurityCriticalSection); if (Index < SecurityArrayLength) { *Security = SecurityArray[Index]; } else { memset(Security, 0, sizeof(TFTPD_SECURITY)); } RtlLeaveCriticalSection (&SecurityCriticalSection); } VOID TftpdStoreSecurityEntry( USHORT Index, PTFTPD_SECURITY Security ) { RtlEnterCriticalSection (&SecurityCriticalSection); if (Index < SecurityArrayLength) { SecurityArray[Index] = *Security; } RtlLeaveCriticalSection (&SecurityCriticalSection); } VOID TftpdGenerateKeyForSecurityEntry( USHORT Index, PTFTPD_SECURITY Security ) { SecBufferDesc SignMessage; SecBuffer SigBuffers[2]; SECURITY_STATUS SecStatus; LARGE_INTEGER SystemTime; RtlEnterCriticalSection (&SecurityCriticalSection); if (Index < SecurityArrayLength) { if (!SecurityArray[Index].GeneratedKey) { // // Generate and sign a key. // NtQuerySystemTime(&SystemTime); SecurityArray[Index].Key = (ULONG)(SystemTime.QuadPart % SecurityArray[Index].ForeignAddress.sin_addr.s_addr); *(PULONG)(SecurityArray[Index].SignedKey) = SecurityArray[Index].Key; SigBuffers[0].pvBuffer = SecurityArray[Index].SignedKey; SigBuffers[0].cbBuffer = sizeof(SecurityArray[Index].SignedKey); SigBuffers[0].BufferType = SECBUFFER_DATA; SigBuffers[1].pvBuffer = SecurityArray[Index].Sign; SigBuffers[1].cbBuffer = NTLMSSP_MESSAGE_SIGNATURE_SIZE; SigBuffers[1].BufferType = SECBUFFER_TOKEN; SignMessage.pBuffers = SigBuffers; SignMessage.cBuffers = 2; SignMessage.ulVersion = 0; SecStatus = SealMessage( &(SecurityArray[Index].ServerContextHandle), 0, &SignMessage, 0 ); if (SecStatus == STATUS_SUCCESS) { SecurityArray[Index].GeneratedKey = TRUE; } } *Security = SecurityArray[Index]; } else { memset(Security, 0, sizeof(TFTPD_SECURITY)); } RtlLeaveCriticalSection (&SecurityCriticalSection); } SECURITY_STATUS TftpdVerifyFileSignature( USHORT Index, USHORT Validation, PTFTPD_SECURITY Security, char * FileName, char * Sign, USHORT ClientPort ) { unsigned long FileNameLength; char * CompareFileName; SecBufferDesc SignMessage; SecBuffer SigBuffers[2]; SECURITY_STATUS SecStatus; PTFTPD_SECURITY TmpSecurity; // points to the real location in the array // // First figure out where the last 64 characters of the // requested filename are since that is all we save. // FileNameLength = strlen(FileName); if (FileNameLength < sizeof(Security->LastFileRead)) { CompareFileName = FileName; } else { CompareFileName = FileName + (FileNameLength + 1 - sizeof(Security->LastFileRead)); } // // Make sure that the sign for the filename is valid. If this // is the same as the last filename requested for this security // entry, and it is coming in on the same port as before, // then we assume the client is retransmitting the request, // so therefore has not re-generated the sign, so we just compare // the sign with the one he sent last time instead of calling // VerifySignature again (to prevent us getting unbalanced with // his MakeSignature call). // RtlEnterCriticalSection (&SecurityCriticalSection); if ((Index < SecurityArrayLength) && (SecurityArray[Index].Validation == Validation)) { TmpSecurity = &SecurityArray[Index]; } else { memset(Security, 0, sizeof(TFTPD_SECURITY)); return (SECURITY_STATUS)STATUS_INVALID_HANDLE; } if ((strcmp(CompareFileName, TmpSecurity->LastFileRead) == 0) && (ClientPort == TmpSecurity->LastFileReadPort)) { // // Compare them, and fake a security error if they don't match. // if (memcmp(TmpSecurity->LastFileSign, Sign, NTLMSSP_MESSAGE_SIGNATURE_SIZE) == 0) { SecStatus = SEC_E_OK; } else { SecStatus = SEC_E_MESSAGE_ALTERED; } } else { // // Save the values in case this request is resent. // strcpy(TmpSecurity->LastFileRead, CompareFileName); memcpy(TmpSecurity->LastFileSign, Sign, NTLMSSP_MESSAGE_SIGNATURE_SIZE); TmpSecurity->LastFileReadPort = ClientPort; // // Now make sure the signature is correct. // SigBuffers[1].pvBuffer = Sign; SigBuffers[1].cbBuffer = NTLMSSP_MESSAGE_SIGNATURE_SIZE; SigBuffers[1].BufferType = SECBUFFER_TOKEN; SigBuffers[0].pvBuffer = FileName; SigBuffers[0].cbBuffer = FileNameLength; SigBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; SignMessage.pBuffers = SigBuffers; SignMessage.cBuffers = 2; SignMessage.ulVersion = 0; SecStatus = VerifySignature( &TmpSecurity->ServerContextHandle, &SignMessage, 0, 0 ); } *Security = *TmpSecurity; RtlLeaveCriticalSection (&SecurityCriticalSection); return SecStatus; } #endif // defined(REMOTE_BOOT_SECURITY) // ======================================================================== VOID TftpdErrorPacket( struct sockaddr * PeerAddress, char * RequestPacket, SOCKET LocalSocket, unsigned short ErrorCode, char * ErrorMessage OPTIONAL ) /*++ Routine Description: This sends an error packet back to the person who sent the request. The RequestPacket is used to select an appropriate error code. Arguments: PeerAddress - The remote address RequestPacket - packet making the request LocalSocket - socket to send error from Return Value: None Error? --*/ { char ErrorPacket[MAX_TFTP_DATAGRAM]; int err; int errorLength; ((unsigned short *) ErrorPacket)[0] = htons(TFTPD_ERROR); ((unsigned short *) ErrorPacket)[1] = htons(ErrorCode); DbgPrint("TftpdError: Sending error packet Error Code: %d",ErrorCode); if ( ErrorMessage != NULL ) { strcpy(&ErrorPacket[4], ErrorMessage); errorLength = strlen(ErrorMessage); } else { if (ErrorCode >= NUM_TFTP_ERROR_CODES) { DbgPrint("TftpdErrorPacket: Unknown ErrorCode=%d.\n", ErrorCode ); ErrorCode = 0; } strcpy(&ErrorPacket[4], ErrorString[ErrorCode]); errorLength = strlen(ErrorString[ErrorCode]); } err = sendto( LocalSocket, ErrorPacket, 5 + errorLength, 0, PeerAddress, sizeof(struct sockaddr_in)); if( SOCKET_ERROR == err ){ DbgPrint("TftpdErrorPacket: sendto failed=%d\n", WSAGetLastError() ); } return; } #if defined(REMOTE_BOOT_SECURITY) int TftpdProcessOptionsPhase1( PTFTP_REQUEST Request, PUCHAR Options, int Opcode ) { int i; // // Assume default values. // Request->SecurityHandle = 0; // // Walk through the remainder of the request packet, looking for options // that we need to process in phase 1. // while ( *Options != 0 ) { if ( _stricmp(Options, "security") == 0 ) { Options += sizeof("security"); if ( Opcode == TFTPD_RRQ ) { Request->SecurityHandle = atoi(Options); } Options += strlen(Options) + 1; } else if ( _stricmp(Options, "sign") == 0 ) { Options += sizeof("sign"); if ( Opcode == TFTPD_RRQ ) { for (i = 0; i < NTLMSSP_MESSAGE_SIGNATURE_SIZE; i++) { Request->Sign[i] = TftpdHexDigitToChar(&Options[i*2]); } } Options += strlen(Options) + 1; } else { // // Unrecognized option. Skip the option ID and the value. // Options += strlen(Options) + 1; if ( *Options != 0 ) { Options += strlen(Options) + 1; } } } return 0; } #endif // defined(REMOTE_BOOT_SECURITY) int TftpdProcessOptionsPhase2( PTFTP_REQUEST Request, PUCHAR Options, int Opcode, int *OackLength, char *PacketBuffer, BOOL *ReceivedTimeoutOption ) { PUCHAR oack; // // Assume default values. // Request->BlockSize = MAX_OACK_PACKET_LENGTH - 4; // Save an alloc mem by default to the current packet size. Request->Timeout = 10; *ReceivedTimeoutOption=FALSE; // // Build the OACK header. // memset( PacketBuffer, 0, MAX_OACK_PACKET_LENGTH ); ((unsigned short *)PacketBuffer)[0] = htons(TFTPD_OACK); oack = &PacketBuffer[2]; // // Walk through the remainder of the request packet, looking for options // that we understand. // while ( *Options != 0 ) { if ( _stricmp(Options, "blksize") == 0 ) { strcpy( oack, Options ); oack += sizeof("blksize"); Options += sizeof("blksize"); Request->BlockSize = atoi(Options); if ( (Request->BlockSize < 8) || (Request->BlockSize > 65464) ) { DbgPrint("TftpdProcessOptionsPhase2: invalid blksize=%s\n", Options ); TftpdErrorPacket( (struct sockaddr *)&Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_OPTION_NEGOT_FAILED, NULL); return -1; } // // Workaround for problem in .98 version of ROM, which // doesn't like our OACK response. If the requested blksize is // 1456, pretend that the option wasn't specified. In the case // of the ROM's TFTP layer, this is the only option specified, // so ignoring it will mean that we don't send an OACK, and the // ROM will deign to talk to us. Note that our TFTP code uses // a blksize of 1432, so this workaround won't affect us. // if ( Request->BlockSize == 1456 ) { Request->BlockSize = MAX_OACK_PACKET_LENGTH - 4; oack -= sizeof("blksize"); Options += strlen(Options) + 1; continue; } if ( Request->BlockSize > MAX_TFTP_DATA ) { Request->BlockSize = MAX_TFTP_DATA; } _itoa( Request->BlockSize, oack, 10 ); oack += strlen(oack) + 1; Options += strlen(Options) + 1; } else if ( _stricmp(Options, "timeout") == 0 ) { strcpy( oack, Options ); oack += sizeof("timeout"); Options += sizeof("timeout"); Request->Timeout = atoi(Options); if ( (Request->Timeout < 1) || (Request->Timeout > 255) ) { DbgPrint("TftpdProcessOptionsPhase2: invalid timeout=%s\n", Options ); TftpdErrorPacket( (struct sockaddr *)&Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_OPTION_NEGOT_FAILED, NULL); return -1; } *ReceivedTimeoutOption = TRUE; strcpy( oack, Options ); oack += strlen(Options) + 1; Options += strlen(Options) + 1; } else if ( _stricmp(Options, "tsize") == 0 ) { strcpy( oack, Options ); oack += sizeof("tsize"); Options += sizeof("tsize"); if ( Opcode == TFTPD_WRQ ) { strcpy( oack, Options ); oack += strlen(Options) + 1; Options += strlen(Options) + 1; } else { _itoa( Request->FileSize, oack, 10 ); oack += strlen(oack) + 1; Options += strlen(Options) + 1; } #if defined(REMOTE_BOOT_SECURITY) } else if ( _stricmp(Options, "security") == 0 ) { // // We process this just so that we can copy it to the OACK. // // Should really copy over Request->Security, in case // it has since become 0, to show the client we reject the // security option for some reason. // strcpy( oack, Options ); oack += sizeof("security"); Options += sizeof("security"); if ( Opcode == TFTPD_RRQ ) { strcpy( oack, Options ); oack += strlen(Options) + 1; } Options += strlen(Options) + 1; #endif //defined (REMOTE_BOOT_SECURITY) } else { // // Unrecognized option. Skip the option ID and the value. // Options += strlen(Options) + 1; if ( *Options != 0 ) { Options += strlen(Options) + 1; } } } *OackLength =(int)(oack - PacketBuffer); if ( *OackLength == 2 ) { *OackLength = 0; } return 0; } #define IS_SEPARATOR(c) (((c) == '\\') || ((c) == '/')) BOOL TftpdCanonicalizeFileName( IN OUT PUCHAR FileName ) { PUCHAR destination; PUCHAR source; PUCHAR lastComponent; // // The canonicalization is done in place. Initialize the source and // destination pointers to point to the same place. // source = FileName; destination = FileName; // // The lastComponent variable is used as a placeholder when // backtracking over trailing blanks and dots. It points to the // first character after the last directory separator or the // beginning of the pathname. // lastComponent = FileName; // // Get rid of leading directory separators. // while ( (*source != 0) && IS_SEPARATOR(*source) ) { source++; } // // Walk through the pathname until we reach the zero terminator. At // the start of this loop, Input points to the first charaecter // after a directory separator or the first character of the // pathname. // while ( *source != 0 ) { if ( *source == '.' ) { // // If we see a dot, look at the next character. // if ( IS_SEPARATOR(*(source+1)) ) { // // If the next character is a directory separator, // advance the source pointer to the directory // separator. // source++; } else if ( (*(source+1) == '.') && IS_SEPARATOR(*(source+2)) ) { // // If the following characters are ".\", we have a "..\". // Advance the source pointer to the "\". // source += 2; // // Move the destination pointer to the character before the // last directory separator in order to prepare for backing // up. This may move the pointer before the beginning of // the name pointer. // destination -= 2; // // If destination points before the beginning of the name // pointer, fail because the user is attempting to go // to a higher directory than the TFTPD root. This is // the equivalent of a leading "..\", but may result from // a case like "dir\..\..\file". // if ( destination <= FileName ) { return FALSE; } // // Back up the destination pointer to after the last // directory separator or to the beginning of the pathname. // Backup to the beginning of the pathname will occur // in a case like "dir\..\file". // while ( destination >= FileName && !IS_SEPARATOR(*destination) ) { destination--; } // // destination points to \ or character before name; we // want it to point to character after last \. // destination++; } else { // // The characters after the dot are not "\" or ".\", so // so just copy source to destination until we reach a // directory separator character. This will occur in // a case like ".file" (filename starts with a dot). // do { *destination++ = *source++; } while ( (*source != 0) && !IS_SEPARATOR(*source) ); } } else { // if ( *source == '.' ) // // source does not point to a dot, so copy source to // destination until we get to a directory separator. // while ( (*source != 0) && !IS_SEPARATOR(*source) ) { *destination++ = *source++; } } // // Truncate trailing blanks. destination should point to the last // character before the directory separator, so back up over blanks. // while ( (destination > lastComponent) && (*(destination-1) == ' ') ) { destination--; } // // At this point, source points to a directory separator or to // a zero terminator. If it is a directory separator, put one // in the destination. // if ( IS_SEPARATOR(*source) ) { // // If we haven't put the directory separator in the path name, // put it in. // if ( (destination != FileName) && !IS_SEPARATOR(*(destination-1)) ) { *destination++ = '\\'; } // // It is legal to have multiple directory separators, so get // rid of them here. Example: "dir\\\\\\\\file". // do { source++; } while ( (source != 0) && IS_SEPARATOR(*source) ); // // Make lastComponent point to the character after the directory // separator. // lastComponent = destination; } } // // We're just about done. If there was a trailing .. (example: // "file\.."), trailing . ("file\."), or multiple trailing // separators ("file\\\\"), then back up one since separators are // illegal at the end of a pathname. // if ( (destination != FileName) && IS_SEPARATOR(*(destination-1)) ) { destination--; } // // Terminate the destination string. // *destination = L'\0'; return TRUE; } BOOL TftpdPrependStringToFileName( IN OUT PUCHAR FileName, IN ULONG FileNameLength, IN PCHAR Prefix ) { BOOL prefixHasSeparator; BOOL currentFileNameHasSeparator; ULONG prefixLength; ULONG separatorLength; ULONG currentFileNameLength; prefixLength = strlen( Prefix ); currentFileNameLength = strlen( FileName ); prefixHasSeparator = (BOOL)(Prefix[prefixLength - 1] == '\\'); currentFileNameHasSeparator = (BOOL)(FileName[0] == '\\'); if ( prefixHasSeparator || currentFileNameHasSeparator ) { separatorLength = 0; if ( prefixHasSeparator && currentFileNameHasSeparator ) { prefixLength--; } } else { separatorLength = 1; } if ( (prefixLength + separatorLength + currentFileNameLength) > (FileNameLength - 1) ) { return FALSE; } // // Move the existing string down to make room for the prefix. // memmove( FileName + prefixLength + separatorLength, FileName, currentFileNameLength + 1 ); // // Move the prefix into place. // memcpy( FileName, Prefix, prefixLength ); // // If necessary, insert a backslash between the prefix and the file name. // if ( separatorLength != 0 ) { FileName[prefixLength] = '\\'; } return TRUE; } BOOL TftpdGetNextReadPacket( PTFTP_READ_CONTEXT Context, PTFTP_REQUEST Request ) /*++ Routine Description: Arguments: Return Value: TRUE: got next packet into Context-Packet FALSE : error packet in Request->Packet2 --*/ { #if defined(REMOTE_BOOT_SECURITY) SECURITY_STATUS SecStatus; #endif //defined(REMOTE_BOOT_SECURITY) if ( Context->oackLength != 0 ) { // // The first "data packet" sent will really be the OACK. // Context->packetLength = Context->oackLength; Context->oackLength = 0; Context->BlockNumber = 0; Context->BytesRead = Request->BlockSize; // to prevent exit condition from being true } else { ((unsigned short *) Context->Packet)[0] = htons(TFTPD_DATA); ((unsigned short *) Context->Packet)[1] = htons(Context->BlockNumber); #if defined(REMOTE_BOOT_SECURITY) if (Request->SecurityHandle) { if (Context->EncryptBytesSent == 0) { // // Read the file before sending the first data packet. // Context->BytesRead = _read( Context->fd, Context->EncryptFileBuffer + NTLMSSP_MESSAGE_SIGNATURE_SIZE, Request->FileSize); if (Context->BytesRead != Request->FileSize) { DbgPrint("TftpdHandleRead: Could not read EncryptFileBuffer=%d.\n", errno); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } // // We have it in memory, so encrypt it. // Context->SigBuffers[0].pvBuffer = Context->EncryptFileBuffer + NTLMSSP_MESSAGE_SIGNATURE_SIZE; Context->SigBuffers[0].cbBuffer = Request->FileSize; Context->SigBuffers[0].BufferType = SECBUFFER_DATA; Context->SigBuffers[1].pvBuffer = Context->EncryptFileBuffer; Context->SigBuffers[1].cbBuffer = NTLMSSP_MESSAGE_SIGNATURE_SIZE; Context->SigBuffers[1].BufferType = SECBUFFER_TOKEN; Context->SignMessage.pBuffers = Context->SigBuffers; Context->SignMessage.cBuffers = 2; Context->SignMessage.ulVersion = 0; SecStatus = SealMessage( &Context->Security.ServerContextHandle, 0, &Context->SignMessage, 0 ); if (SecStatus != STATUS_SUCCESS) { DbgPrint("TftpdHandleRead: Could not seal message=%d.\n", SecStatus); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Encryption error"); goto cleanup; } } if ((Context->EncryptBytesSent + Request->BlockSize) <= (int)(Request->FileSize + NTLMSSP_MESSAGE_SIGNATURE_SIZE)) { Context->BytesRead = Request->BlockSize; } else { Context->BytesRead = (Request->FileSize + NTLMSSP_MESSAGE_SIGNATURE_SIZE) - Context->EncryptBytesSent; } memcpy( &Context->Packet[4], Context->EncryptFileBuffer + Context->EncryptBytesSent, Context->BytesRead); Context->EncryptBytesSent += Context->BytesRead; } else #endif //defined(REMOTE_BOOT_SECURITY) { // // read BlockSize bytes (or whatever's left) // Context->BytesRead = _read( Context->fd, &Context->Packet[4], Request->BlockSize); if( Context->BytesRead == -1 ){ DbgPrint("TftpdHandleRead: read failed=%d\n", errno ); SetLastError( errno ); goto cleanup; } if (Context->BytesRead != Request->BlockSize) { DbgPrint("GetNextReadPacket read %d bytes\n",Context->BytesRead); } } Context->packetLength = 4 + Context->BytesRead; } return TRUE; cleanup: return FALSE; } DWORD TftpdAddContextToList(PLIST_ENTRY pEntry) { EnterCriticalSection(&Globals.Lock); InsertHeadList(&Globals.WorkList,pEntry); DbgPrint("Adding 0x%X to global list\n", pEntry); LeaveCriticalSection(&Globals.Lock); return TRUE; } PVOID TftpdFindContextInList(SOCKET Sock) /*++ Routine Description: Look for context based upon Socket descriptor. If found, return pointer to context with lock held You must release the lock via a call to TftpdReleaseContextLock(). For now, simple linked list walk. Move to hash table if time permits. Arguments: Argument - socket Return Value: NULL, failed to find context --*/ { PLIST_ENTRY pEntry; PTFTP_CONTEXT_HEADER Context; EnterCriticalSection(&Globals.Lock); for ( pEntry = Globals.WorkList.Flink; pEntry != &Globals.WorkList; pEntry = pEntry->Flink) { Context=CONTAINING_RECORD(pEntry, TFTP_CONTEXT_HEADER, ContextLinkage); if (Context->Sock == Sock) { // Found it EnterCriticalSection(&Context->Lock); if (!Context->Closing) { Context->RefCount++; } else { LeaveCriticalSection(&Context->Lock); Context = NULL; } LeaveCriticalSection(&Globals.Lock); return (Context); } } LeaveCriticalSection(&Globals.Lock); return(NULL); } void TftpdReleaseContextLock( PTFTP_CONTEXT_HEADER Context ) /*++ Routine Description: Used to leave any context critical section entered via TftpdFindContextInList(). Arguments: Argument - Context Return Value: None. --*/ { assert(Context->RefCount > 0); Context->RefCount--; if (Context->Closing && (Context->RefCount == 0)) { Context->IdleCount=0; LeaveCriticalSection(&Context->Lock); TftpdRemoveContextFromList((PTFTP_CONTEXT_HEADER)Context); } else { LeaveCriticalSection(&Context->Lock); } } VOID TftpdRemoveContextFromList(PTFTP_CONTEXT_HEADER Context) /*++ Routine Description: Look for context. If found, remove it, free all resources For now, simple linked list walk. Move to hash table if time permits. Arguments: Argument - socket Return Value: --*/ { PLIST_ENTRY pEntry; PLIST_ENTRY pNextEntry; PTFTP_CONTEXT_HEADER LocalContext; EnterCriticalSection(&Globals.Lock); pEntry=Globals.WorkList.Flink; while (pEntry != &Globals.WorkList) { LocalContext=CONTAINING_RECORD(pEntry, TFTP_CONTEXT_HEADER, ContextLinkage); pNextEntry=pEntry->Flink; if (Context == LocalContext) { // Found it assert(Context->Closing); assert(Context->RefCount == 0); RemoveEntryList(pEntry); DbgPrint("Removing 0x%X from global list\n", pEntry); LeaveCriticalSection(&Globals.Lock); DbgPrint("Removing connection to port %d\n",htons(Context->ForeignAddress.sin_port)); TftpdFreeContext(Context); return; } pEntry=pNextEntry; } LeaveCriticalSection(&Globals.Lock); } VOID TftpdFreeGeneralContextFields(PTFTP_CONTEXT_HEADER Context) { if (Context->Sock != INVALID_SOCKET) { closesocket(Context->Sock); DbgPrint("TftpdFreeGeneralContextFields: Close Socket %d\n",Context->Sock); } if (Context->TimerHandle) { RtlDeleteTimer(Globals.TimerQueueHandle,Context->TimerHandle,NULL); } Context->TimerHandle = 0; if (Context->Packet != NULL) { free(Context->Packet); Context->Packet = NULL; } RtlDeregisterWaitEx(Context->WaitEvent,NULL); CloseHandle(Context->SocketEvent); DeleteCriticalSection(&Context->Lock); } VOID TftpdFreeReadContext(PTFTP_READ_CONTEXT Context) { #if defined(REMOTE_BOOT_SECURITY) if (Context->EncryptFileBuffer) { free(Context->EncryptFileBuffer); } #endif //defined(REMOTE_BOOT_SECURITY) if (Context->fd != -1) { _close(Context->fd); } free(Context); } VOID TftpdFreeWriteContext(PTFTP_WRITE_CONTEXT Context) { if (Context->fd != -1) { _close(Context->fd); } free(Context); } VOID TftpdFreeLoginContext(PTFTP_LOGIN_CONTEXT Context) { } VOID TftpdFreeKeyContext(PTFTP_KEY_CONTEXT Context) { } VOID TftpdFreeContext(PTFTP_CONTEXT_HEADER Context) { if (Context == NULL) { DbgPrint("TftpdFreeContext: Called with Null context"); return; } TftpdFreeGeneralContextFields(Context); switch (Context->ContextType) { case READ_CONTEXT: TftpdFreeReadContext((PTFTP_READ_CONTEXT)Context); break; case WRITE_CONTEXT: TftpdFreeWriteContext((PTFTP_WRITE_CONTEXT)Context); break; case LOGIN_CONTEXT: TftpdFreeLoginContext((PTFTP_LOGIN_CONTEXT)Context); break; case KEY_CONTEXT: TftpdFreeKeyContext((PTFTP_KEY_CONTEXT)Context); break; } } VOID TftpdReaper(PVOID ReaperContext, BOOLEAN Flag) /*++ Routine Description: Walk WorkList looking for inactive Contexts Arguments: Return Value: --*/ { PLIST_ENTRY pEntry; PLIST_ENTRY pNextEntry; PTFTP_CONTEXT_HEADER Context; EnterCriticalSection(&Globals.Lock); pEntry = Globals.WorkList.Flink; while (pEntry != &Globals.WorkList) { Context=CONTAINING_RECORD(pEntry, TFTP_CONTEXT_HEADER, ContextLinkage); EnterCriticalSection(&Context->Lock); pNextEntry=pEntry->Flink; Context->IdleCount++; if ((Context->IdleCount >= DEAD_CONTEXT_COUNT) && !Context->Closing && (Context->RefCount == 0)) { // Context is dead. Context->Closing = TRUE; DbgPrint("Reaping connection to port %d",htons(Context->ForeignAddress.sin_port)); LeaveCriticalSection(&Context->Lock); LeaveCriticalSection(&Globals.Lock); TftpdRemoveContextFromList((PTFTP_CONTEXT_HEADER)Context); EnterCriticalSection(&Globals.Lock); } else { LeaveCriticalSection(&Context->Lock); } pEntry=pNextEntry; } LeaveCriticalSection(&Globals.Lock); TftpdCleanHeap(); } VOID TftpdRetransmit(PVOID RetransContext, BOOLEAN Flag) { PTFTP_READ_WRITE_CONTEXT_HEADER Context; BOOL Status; NTSTATUS ntStatus; Context=(PTFTP_READ_WRITE_CONTEXT_HEADER)TftpdFindContextInList((SOCKET)RetransContext); if (Context == NULL) { DbgPrint("TftpdRetransmit: Unable to find context\n"); return; } if (Context->RetransmissionCount < MAX_TFTPD_RETRIES) { if (Context->RetransmissionCount > 5) { SYSTEMTIME _st; GetLocalTime(&_st); DbgPrint("%2d-%02d: %02d:%02d:%02d TftpdRetransmit: Socket %d DstPort %d Count %d BlkNum %d\n", _st.wMonth,_st.wDay,_st.wHour,_st.wMinute,_st.wSecond, (DWORD)((DWORD_PTR)RetransContext), ntohs(Context->ForeignAddress.sin_port), Context->RetransmissionCount, htons(((unsigned short*)(Context->Packet))[1])); } Status = sendto( Context->Sock, Context->Packet, Context->packetLength, 0, (struct sockaddr *) &Context->ForeignAddress, sizeof(struct sockaddr_in)); if( SOCKET_ERROR == Status ){ DbgPrint("TftpdHandleRead: sendto failed=%d\n", WSAGetLastError() ); } Context->RetransmissionCount++; Context->IdleCount = 0; // don't accidently reap this connection during retransmit tries. if (Context->TimerHandle) { if (!Context->FixedTimer) { Context->DueTime *= 2; if (Context->DueTime > (TFTPD_MAX_TIMEOUT * 1000)) { Context->DueTime = (TFTPD_MAX_TIMEOUT * 1000); } } ntStatus=RtlUpdateTimer(Globals.TimerQueueHandle, Context->TimerHandle, Context->DueTime, Context->DueTime); if (ntStatus != STATUS_SUCCESS) { DbgPrint("TftpdRetransmit: UpdateTimerFailed %d",GetLastError()); } } } else { //Send timeout TftpdErrorPacket((struct sockaddr *) &Context->ForeignAddress, NULL, Context->Sock, TFTPD_ERROR_UNDEFINED, "Timeout" ); Context->Closing = TRUE; } TftpdReleaseContextLock((PTFTP_CONTEXT_HEADER)Context); } DWORD TftpdResumeRead( PTFTP_READ_CONTEXT Context, PTFTP_REQUEST Request ) /*++ Routine Description: Resumes processing of existing read request. Context lock held when function is called. Arguments: Argument - buffer containing the read request datagram Return Value: Exit status 0 == success 1 == failure N >0 failure s--*/ { BOOL Acked=FALSE; BOOL Status=FALSE; int SendStatus=0; BOOL Retrans=FALSE; NTSTATUS Stat; // // Parse the request // DbgPrint("TftpdResumeRead BlockNum %d\n",Context->BlockNumber); Request->BlockSize=Context->BlockSize; if (CHECK_ACK(Request->Packet1, TFTPD_ACK, Context->BlockNumber)) { Acked = TRUE; Context->RetransmissionCount=0; } else { DbgPrint("Ack failed: Expect Blk %d Received Blk %d OpCode %d", Context->BlockNumber, ntohs((((unsigned short *)Request->Packet1)[1])), htons(*((unsigned short *) (Request->Packet1)))); if (CHECK_ACK(Request->Packet1, TFTPD_ACK, Context->BlockNumber-1)) { Retrans=TRUE; } } if (Acked) { if (Context->Done) { Context->Closing = TRUE; return 0; } if (++Context->BlockNumber == 0) Context->BlockNumber = 1; // 32 MB file roll-over. Status=TftpdGetNextReadPacket(Context,Request); if (!Status) { DbgPrint("GetNextPacketFailed %d",ntohs(Request->ForeignAddress.sin_port)); return 0; } Context->RetransmissionCount=0; Context->IdleCount=0; if (!Context->FixedTimer) { // received new packet, reset timer Context->DueTime=TFTPD_INITIAL_TIMEOUT*1000; } if (Context->TimerHandle) { Stat=RtlUpdateTimer(Globals.TimerQueueHandle, Context->TimerHandle, Context->DueTime, Context->DueTime); if (!NT_SUCCESS(Stat)) { DbgPrint("Failed to Update Timer"); } } // // If we've sent the whole file, exit the loop. Note that we // don't send an error packet if there is a timeout on the last // data packet, because the receiver might have only sent the // ACK once, then forgotten about this transfer. // if (Context->BytesRead < Request->BlockSize) { SOCKET Sock; DbgPrint("We're done with %d\n",ntohs(Request->ForeignAddress.sin_port)); Context->Done=TRUE; } } if (Status) { // Got a valid packet to send DbgPrint("TftpdResumeRead: Sending data BlkNumber %d Socket %d PeerPort %d Size %d\n",Context->BlockNumber, Context->Sock, ntohs(Request->ForeignAddress.sin_port), Context->packetLength); SendStatus = sendto( Context->Sock, Context->Packet, Context->packetLength, 0, (struct sockaddr *) &Request->ForeignAddress, sizeof(struct sockaddr_in)); if( SOCKET_ERROR == SendStatus ){ DbgPrint("TftpdHandleRead: sendto failed=%d\n", WSAGetLastError() ); goto cleanup; } } return 0; cleanup: return 1; } DWORD TftpdResumeWrite( PTFTP_WRITE_CONTEXT Context, PTFTP_REQUEST Request ) /*++ Routine Description: Resumes processing of existing write request. Context lock held when function is called. Arguments: Argument - buffer containing the write request datagram Return Value: Exit status 0 == success 1 == failure N >0 failure --*/ { BOOL NewData=FALSE; char State; int BytesWritten; int Status; NTSTATUS Stat; DbgPrint("Request Blocksize %d Context Blocksize %d\n",Request->BlockSize,Context->BlockSize); Request->BlockSize=Context->BlockSize; DbgPrint("TftpdResumeWrite: PktNum %d DataSize %d\n",Context->BlockNumber+1,Request->DataSize-4); if (CHECK_ACK(Request->Packet1, TFTPD_DATA, Context->BlockNumber+1)) { NewData = TRUE; Context->BlockNumber++; Context->IdleCount=0; } else { if (CHECK_ACK(Request->Packet1, TFTPD_DATA, Context->BlockNumber)) { // resend ack ((unsigned short *) Context->Packet)[0] = htons(TFTPD_ACK); ((unsigned short *) Context->Packet)[1] = htons(Context->BlockNumber); Status = sendto( Context->Sock, Context->Packet, 4, 0, (struct sockaddr *) &Request->ForeignAddress, sizeof(struct sockaddr_in)); DbgPrint("TftpdResumeWrite: Resending Ack %d\n",Context->BlockNumber); if( SOCKET_ERROR == Status ){ DbgPrint("TftpdHandleWrite: sendto failed=%d\n", WSAGetLastError() ); } return 0; } } State = '\0'; if (NewData) { BytesWritten = TftpdDoWrite(Context->fd, &Request->Packet1[4], Request->DataSize - 4, Context->FileMode, &State); } if (!NewData) { DbgPrint("TftpdHandleWrite: Timed out waiting for ack\n"); /* TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Timeout"); */ goto cleanup; } else if (BytesWritten < 0) { DbgPrint("TftpdHandleWrite: disk full?\n"); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_DISK_FULL, NULL); goto cleanup; } else if (Request->DataSize - 4 <= Request->BlockSize ) { // // Ack the last packet // ((unsigned short *) Context->Packet)[0] = htons(TFTPD_ACK); ((unsigned short *) Context->Packet)[1] = htons(Context->BlockNumber); Status = sendto( Context->Sock, Context->Packet, 4, 0, (struct sockaddr *) &Request->ForeignAddress, sizeof(struct sockaddr_in)); DbgPrint("TftpdResumeWrite: Sending Ack %d\n",Context->BlockNumber); if (Context->TimerHandle) { Context->RetransmissionCount=0; if (!Context->FixedTimer) { // received new packet, reset timer Context->DueTime=TFTPD_INITIAL_TIMEOUT*1000; } Stat=RtlUpdateTimer(Globals.TimerQueueHandle, Context->TimerHandle, Context->DueTime, Context->DueTime); if (!NT_SUCCESS(Stat)) { DbgPrint("Failed to Update Timer"); } } if( SOCKET_ERROR == Status ){ DbgPrint("TftpdHandleWrite: sendto failed=%d\n", WSAGetLastError() ); } if (Request->DataSize - 4 < Request->BlockSize ) { // we're done. flag for speedy cleanup Context->Closing = TRUE; } } return 0; cleanup: return 1; } DWORD TftpdResumeLogin( PTFTP_LOGIN_CONTEXT Context, PTFTP_REQUEST Request ) /*++ Routine Description: Resumes processing of existing login request. Context lock held when function is called. Lock released upon exiting. Arguments: Argument - buffer containing the login request datagram Return Value: Exit status 0 == success 1 == failure N >0 failure --*/ { return 0; } DWORD TftpdResumeKey( PTFTP_KEY_CONTEXT Context, PTFTP_REQUEST Request ) /*++ Routine Description: Resumes processing of existing key request. Context lock held when function is called. Lock released upon exiting. Arguments: Argument - buffer containing the key request datagram Return Value: Exit status 0 == success 1 == failure N >0 failure --*/ { return 0; } VOID TftpdResumeProcessing(PVOID Argument) /*++ Routine Description: Resume work, if possible Arguments: Argument - buffer containing the incoming datagram Return Value: --*/ { PTFTP_REQUEST Request=(PTFTP_REQUEST)Argument; PTFTP_CONTEXT_HEADER Context; DWORD Status; Context=(PTFTP_CONTEXT_HEADER)TftpdFindContextInList(Request->TftpdPort); if (Context == NULL) { DbgPrint("Invalid request on port %d", Request->TftpdPort); return; } if ((Context->ForeignAddress.sin_family != Request->ForeignAddress.sin_family) || (Context->ForeignAddress.sin_addr.s_addr != Request->ForeignAddress.sin_addr.s_addr) || (Context->ForeignAddress.sin_port != Request->ForeignAddress.sin_port)) { TftpdReleaseContextLock(Context); DbgPrint("Invalid request on port %d", Request->TftpdPort); return; } switch (Context->ContextType) { case READ_CONTEXT: Status=TftpdResumeRead((PTFTP_READ_CONTEXT)Context, Request); break; case WRITE_CONTEXT: TftpdResumeWrite((PTFTP_WRITE_CONTEXT)Context, Request); break; case LOGIN_CONTEXT: TftpdResumeLogin((PTFTP_LOGIN_CONTEXT)Context, Request); break; case KEY_CONTEXT: TftpdResumeKey((PTFTP_KEY_CONTEXT)Context, Request); break; } TftpdReleaseContextLock(Context); return; } /* Make sure incoming name is null terminated */ BOOL IsFileNameValid(char* FileName, DWORD MaxLen) { DWORD i; // Make sure Filename has null terminator for (i=0; i < MaxLen; i++) { if (FileName[i] == (char)0 ) { return TRUE; } } return FALSE; } // ======================================================================== DWORD TftpdHandleRead( PVOID Argument ) /*++ Routine Description: This handles an incoming read file request. Arguments: Argument - buffer containing the read request datagram Return Value: Exit status 0 == success 1 == failure N >0 failure --*/ { BOOL Acked; int AddressLength; int BytesAck; int BytesRead; char * CharPtr; struct fd_set exceptfds; int FileMode; char * FileName; char * ReadMode; char * NewPacket; struct sockaddr_in ReadAddress; struct fd_set readfds; SOCKET ReadPort = INVALID_SOCKET; PTFTP_REQUEST Request; int Status, err; struct timeval timeval; char * client_ipaddr; short client_port; BOOL LockHeld=FALSE; BOOL AddedContext=FALSE; int length; #if defined(REMOTE_BOOT_SECURITY) SECURITY_STATUS SecStatus; #endif //REMOTE_BOOT_SECURITY) PTFTP_READ_CONTEXT Context = NULL; NTSTATUS ntStatus; // // Parse the request // DbgPrint("Entered Handle read\n"); Request = (PTFTP_REQUEST) Argument; FileName = &Request->Packet1[2]; if (!IsFileNameValid(FileName,MAX_TFTP_DATAGRAM-2)) { goto cleanup; } ReadMode = FileName + (length = strlen(FileName)) + 1; // Make sure ReadMode is NUL terminated. if (!IsFileNameValid(ReadMode, MAX_TFTP_DATAGRAM - (length + 1))) { DbgPrint("TftpdHandleRead: invalid ReadMode\n"); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_ILLEGAL_OPERATION, NULL); goto cleanup; } // Set up context. Context=(PTFTP_READ_CONTEXT)malloc(sizeof(TFTP_READ_CONTEXT)); if (Context == NULL) { goto cleanup; } memset(Context,0,sizeof(TFTP_READ_CONTEXT)); Context->Packet = (char *)malloc(MAX_OACK_PACKET_LENGTH); if (Context->Packet == NULL) { goto cleanup; } // // Profile data. // client_ipaddr = inet_ntoa( Request->ForeignAddress.sin_addr ); if (client_ipaddr == NULL) client_ipaddr = ""; client_port = htons( Request->ForeignAddress.sin_port ); DbgPrint("TftpdHandleRead: FileName=%s, ReadMode=%s, from=%s:%d.\n", FileName, ReadMode, client_ipaddr, client_port ); // // Convert the mode to all lower case for comparison // for (CharPtr = ReadMode; *CharPtr; CharPtr++) { *CharPtr = (char)tolower(*CharPtr); } if (strcmp(ReadMode, "netascii") == 0) { FileMode = O_TEXT; DbgPrint("TftpdHandleRead: netascii mode.\n"); } else if (strcmp(ReadMode, "octet") == 0) { FileMode = O_BINARY; DbgPrint("TftpdHandleRead: binary mode.\n"); } else { DbgPrint("TftpdHandleRead: invalid ReadMode=%s?\n", ReadMode ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_ILLEGAL_OPERATION, NULL); goto cleanup; } #if defined(REMOTE_BOOT_SECURITY) err = TftpdProcessOptionsPhase1( Request, CharPtr + 1, TFTPD_RRQ ); if ( err != 0 ) { goto cleanup; } if (Request->SecurityHandle) { // // This returns TRUE (and the security entry) if the sign // for this file is valid. // SecStatus = TftpdVerifyFileSignature( (USHORT)(Request->SecurityHandle >> 16), // index (USHORT)(Request->SecurityHandle & 0xffff), // validation &Context->Security, FileName, Request->Sign, client_port); // // This error code is known to mean an invalid security handle. // if ( SecStatus == (SECURITY_STATUS)STATUS_INVALID_HANDLE ) { DbgPrint("TftpdHandleRead: SecurityHandle %x is invalid.\n", Request->SecurityHandle); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Invalid security handle"); goto cleanup; } else if ( SecStatus != SEC_E_OK ) { DbgPrint("TftpdHandleRead: sign is invalid.\n"); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Invalid sign"); goto cleanup; } } #endif // defined(REMOTE_BOOT_SECURITY) // // Canonicalize the file name. // DbgPrint("TftpdHandleRead: Canonicalizing name.\n"); strcpy( Request->Packet3, FileName ); if ( !TftpdCanonicalizeFileName(Request->Packet3) ) { DbgPrint("TftpdHandleRead: invalid FileName=%s\n", FileName ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Malformed file name"); goto cleanup; } // // Check whether this access is permitted. // if( !( match( ValidClients, client_ipaddr ) || match( ValidMasters, client_ipaddr ) ) || !match( ValidReadFiles, Request->Packet3 ) ){ DbgPrint("TftpdHandleRead: cannot open file=%s, errno=%d.\n" " client %s:%d,\n" " ValidReadFiles=%s, ValidClients=%s, ValidMasters=%s,\n" , Request->Packet3, errno, client_ipaddr, client_port, ValidReadFiles, ValidClients, ValidMasters ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_ACCESS_VIOLATION, NULL); goto cleanup; } // // Prepend the start directory to the file name. // DbgPrint("TftpdHandleRead: Prepending directory name.\n"); if ( !TftpdPrependStringToFileName( Request->Packet3, sizeof(Request->Packet3), StartDirectory) ) { DbgPrint("TftpdHandleRead: too long FileName=%s\n", FileName ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "File name too long"); goto cleanup; } // // Open the file. // DbgPrint("TftpdHandleRead: opening file <%s>\n", Request->Packet3 ); Context->fd = _open(Request->Packet3, O_RDONLY | O_BINARY); if (Context->fd == -1) { SetLastError( errno ); DbgPrint("TftpdHandleRead: cannot open file %s, errno=%d.\n", Request->Packet3, errno ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_FILE_NOT_FOUND, NULL); goto cleanup; } err = _lseek(Context->fd, 0, SEEK_END); if ( err != -1 ) { Request->FileSize = err; err = _lseek(Context->fd, 0, SEEK_SET); } if( err == -1 ){ DbgPrint("TftpdHandleRead: lseek failed, errno=%d\n", errno ); SetLastError( errno ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } // // Open a new socket for this request // ReadPort = socket( AF_INET, SOCK_DGRAM, 0); DbgPrint("TftpdHandleRead: New Socket %d\n",ReadPort); if (ReadPort == INVALID_SOCKET) { DbgPrint("TftpdHandleRead: cannot open socket, Error=%d\n", WSAGetLastError() ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } // // Bind to a random address // ReadAddress.sin_family = AF_INET; ReadAddress.sin_port = 0; ReadAddress.sin_addr.s_addr = Request->MyAddr; Status = bind( ReadPort, (struct sockaddr *) &ReadAddress, sizeof(ReadAddress) ); if (Status) { DbgPrint("TftpdHandleRead: cannot bind socket, error=%d.\n", WSAGetLastError() ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } Request->TftpdPort = ReadPort; // Enter Context into list now that we know the port InitializeCriticalSection(&Context->Lock); Context->Sock=ReadPort; memcpy(&Context->ForeignAddress,&Request->ForeignAddress,sizeof(struct sockaddr_in)); Context->SocketEvent=CreateEvent(NULL,FALSE,FALSE,NULL); if (Context->SocketEvent == NULL) { DbgPrint("Failed to create socket event %d",GetLastError()); goto cleanup; } Context->WaitEvent=RegisterSocket(ReadPort,Context->SocketEvent,REG_CONTINUE_SOCKET); if (Context->WaitEvent == NULL) { DbgPrint("Failed to create socket event %d",GetLastError()); goto cleanup; } // Insert Context TftpdAddContextToList(&Context->ContextLinkage); AddedContext=TRUE; Context=(PTFTP_READ_CONTEXT)TftpdFindContextInList(ReadPort); if (Context == NULL) { DbgPrint("Failed to Lookup ReadContext"); goto cleanup; } LockHeld=TRUE; err = TftpdProcessOptionsPhase2( Request, CharPtr + 1, TFTPD_RRQ, &Context->oackLength,Context->Packet, &Context->FixedTimer); if ( err != 0 ) { goto cleanup; } // Start retransmission timer if (Context->FixedTimer) { Context->DueTime=Request->Timeout*1000; } else { Context->DueTime=TFTPD_INITIAL_TIMEOUT*1000; } DbgPrint("TftpdHandleRead: Timer Interval %d msecs",Context->DueTime); ntStatus=RtlCreateTimer(Globals.TimerQueueHandle, &Context->TimerHandle, TftpdRetransmit, (PVOID)Context->Sock, Context->DueTime, Context->DueTime, 0); if (!NT_SUCCESS(ntStatus)) { DbgPrint("Failed to Arm Timer %d",ntStatus); } Context->ContextType=READ_CONTEXT; Context->BlockSize=Request->BlockSize; if (Context->BlockSize > MAX_OACK_PACKET_LENGTH - 4) { DbgPrint("TftpdHandleRead: Reallocating packet.\n"); NewPacket = (char *)realloc(Context->Packet, Context->BlockSize + 4); if (NewPacket == NULL) { goto cleanup; } Context->Packet = NewPacket; } #if defined(REMOTE_BOOT_SECURITY) if (Request->SecurityHandle) { // // For secure mode, we read the whole file in at once so // we can encrypt it. For large files like ntoskrnl.exe, // will this work? If we get errors here, we could // just change the oack to say "security 0" and then send // down the unencrypted file -- maybe we should also do this // for files beyond a certain size. // Context->EncryptFileBuffer = malloc(Request->FileSize + NTLMSSP_MESSAGE_SIGNATURE_SIZE); if (Context->EncryptFileBuffer == NULL) { DbgPrint("TftpdHandleRead: Could not allocate EncryptFileBuffer length %d.\n", Request->FileSize + NTLMSSP_MESSAGE_SIGNATURE_SIZE); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } // // We don't actually read/seal until later -- this is so we can // send the OACK out right now to prevent more threads from // being spawned if he resends the initial request. // Context->EncryptBytesSent = 0; } #endif //defined(REMOTE_BOOT_SECURITY) // // Ready to read and send file in blocks. // Context->BlockNumber = 1; Status=TftpdGetNextReadPacket(Context,Request); Context->RetransmissionCount=0; if (Status) { // Got a valid packet to send Status = sendto( ReadPort, Context->Packet, Context->packetLength, 0, (struct sockaddr *) &Request->ForeignAddress, sizeof(struct sockaddr_in)); if (Context->BytesRead < Request->BlockSize) { Context->Done=TRUE; } } else { // send error packet Status = sendto( ReadPort, Request->Packet2, Context->packetLength, 0, (struct sockaddr *) &Request->ForeignAddress, sizeof(struct sockaddr_in)); } if( SOCKET_ERROR == Status ){ DbgPrint("TftpdHandleRead: sendto failed=%d\n", WSAGetLastError() ); goto cleanup; } cleanup: if (Context != NULL) { if (LockHeld) { TftpdReleaseContextLock((PTFTP_CONTEXT_HEADER)Context); } if (!AddedContext) { free(Context); } } return 0; } // ======================================================================== /*++ Routine Description: This handles an incoming write file request. Arguments: Argument - buffer containing the write request Return Value: Exit status 0 == success >0 == failure --*/ DWORD TftpdHandleWrite( PVOID Argument ) { int AddressLength; int BytesRead; int BytesWritten; char * CharPtr; struct fd_set exceptfds; char * FileName; char * NewPacket; BOOL NewData; struct sockaddr_in ReadAddress; struct fd_set readfds; SOCKET ReadPort = INVALID_SOCKET; int Retry; char State; int Status, err; struct timeval timeval; char * WriteMode; PTFTP_REQUEST Request; int oackLength; int packetLength; char * client_ipaddr; short client_port; BOOL LockHeld=FALSE; BOOL AddedContext=FALSE; int length; PTFTP_WRITE_CONTEXT Context = NULL; NTSTATUS ntStatus; // Set up context. Context=(PTFTP_WRITE_CONTEXT)malloc(sizeof(TFTP_WRITE_CONTEXT)); if (Context == NULL) { goto cleanup; } memset(Context,0,sizeof(TFTP_WRITE_CONTEXT)); Context->Packet = (char *)malloc(MAX_OACK_PACKET_LENGTH); if (Context->Packet == NULL) { goto cleanup; } // // Parse the request // Request = (PTFTP_REQUEST) Argument; FileName = &Request->Packet1[2]; if (!IsFileNameValid(FileName,MAX_TFTP_DATAGRAM-2)) { goto cleanup; } WriteMode = FileName + (length = strlen(FileName)) + 1; // Make sure WriteMode is NUL terminated. if (!IsFileNameValid(WriteMode, MAX_TFTP_DATAGRAM - (length + 1))) { DbgPrint("TftpdHandleWrite: invalid WriteMode\n"); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_ILLEGAL_OPERATION, NULL); goto cleanup; } // // Profile data. // client_ipaddr = inet_ntoa( Request->ForeignAddress.sin_addr ); if (client_ipaddr == NULL) client_ipaddr = ""; client_port = htons( Request->ForeignAddress.sin_port ); DbgPrint("TftpdHandleWrite: FileName=%s, WriteMode=%s, from=%s:%d.\n", FileName, WriteMode, client_ipaddr, client_port ); for (CharPtr = WriteMode; *CharPtr; CharPtr ++) { *CharPtr = isupper(*CharPtr) ? tolower(*CharPtr) : *CharPtr; } if (strcmp(WriteMode, "netascii") == 0) { Context->FileMode = O_TEXT; } else if (strcmp(WriteMode, "octet") == 0) { Context->FileMode = O_BINARY; } else { DbgPrint("TftpdHandleWrite: invalid WriteMode=%s\n", WriteMode ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_ILLEGAL_OPERATION, NULL); goto cleanup; } #if defined(REMOTE_BOOT_SECURITY) err = TftpdProcessOptionsPhase1( Request, CharPtr + 1, TFTPD_WRQ ); if ( err != 0 ) { goto cleanup; } #endif //defined(REMOTE_BOOT_SECURITY) // // Canonicalize the file name. // strcpy( Request->Packet3, FileName ); if ( !TftpdCanonicalizeFileName(Request->Packet3) ) { DbgPrint("TftpdHandleWrite: invalid FileName=%s\n", FileName ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Malformed file name"); goto cleanup; } // // Check whether this access is permitted. // if( !match( ValidMasters, client_ipaddr ) || !match( ValidWriteFiles, FileName ) ){ DbgPrint("TftpdHandleWrite: cannot open file=%s, errno=%d.\n" " client %s:%d,\n" " ValidWriteFiles=%s, ValidClients=%s, ValidMasters=%s,\n" , Request->Packet3, errno, client_ipaddr, client_port, ValidWriteFiles, ValidClients, ValidMasters ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_ACCESS_VIOLATION, NULL); goto cleanup; } // // Prepend the start directory to the file name. // if ( !TftpdPrependStringToFileName( Request->Packet3, sizeof(Request->Packet3), StartDirectory) ) { DbgPrint("TftpdHandleWrite: too long FileName=%s\n", FileName ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "File name too long"); goto cleanup; } // // Open the file. // DbgPrint("TftpdHandleWrite: opening file <%s>\n", Request->Packet3 ); Context->fd = _open(Request->Packet3, _O_WRONLY | _O_CREAT | _O_BINARY | _O_TRUNC, _S_IREAD | _S_IWRITE); if (Context->fd == -1) { DbgPrint("TftpdHandleWrite: cannot open file=%s, errno=%d.\n" " client %s:%d,\n" " ValidWriteFiles=%s, ValidClients=%s, ValidMasters=%s,\n" , FileName, errno, client_ipaddr, client_port, ValidWriteFiles, ValidClients, ValidMasters ); SetLastError( errno ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_ACCESS_VIOLATION, NULL); goto cleanup; } // // Open a new socket for this request // ReadPort = socket( AF_INET, SOCK_DGRAM, 0); if( ReadPort == INVALID_SOCKET ){ DbgPrint("TftpdHandleWrite: cannot open socket, Error=%d.\n", WSAGetLastError() ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } // // Bind to a random address // ReadAddress.sin_family = AF_INET; ReadAddress.sin_port = 0; ReadAddress.sin_addr.s_addr = Request->MyAddr; Status = bind( ReadPort, (struct sockaddr *) &ReadAddress, sizeof(ReadAddress)); if (Status) { DbgPrint("TftpdHandleWrite: cannot bind socket, Error=%d.\n", WSAGetLastError() ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } Request->TftpdPort = ReadPort; err = TftpdProcessOptionsPhase2( Request, CharPtr + 1, TFTPD_WRQ, &Context->oackLength,Context->Packet, &Context->FixedTimer); if ( err != 0 ) { goto cleanup; } State = '\0'; // Enter Context into list now that we know the port InitializeCriticalSection(&Context->Lock); Context->Sock=ReadPort; memcpy(&Context->ForeignAddress,&Request->ForeignAddress,sizeof(struct sockaddr_in)); Context->SocketEvent=CreateEvent(NULL,FALSE,FALSE,NULL); if (Context->SocketEvent == NULL) { DbgPrint("Failed to create socket event %d",GetLastError()); goto cleanup; } Context->WaitEvent=RegisterSocket(ReadPort,Context->SocketEvent,REG_CONTINUE_SOCKET); if (Context->WaitEvent == NULL) { DbgPrint("Failed to create socket event %d",GetLastError()); goto cleanup; } // Insert Context TftpdAddContextToList(&Context->ContextLinkage); AddedContext=TRUE; Context=(PTFTP_WRITE_CONTEXT)TftpdFindContextInList(ReadPort); if (Context == NULL) { DbgPrint("Failed to Lookup ReadContext"); goto cleanup; } LockHeld=TRUE; // Start retransmission timer if (Context->FixedTimer) { Context->DueTime=Request->Timeout*1000; } else { Context->DueTime=TFTPD_INITIAL_TIMEOUT*1000; } DbgPrint("TftpdHandleWrite: Timer Interval %d msecs\n",Context->DueTime); ntStatus=RtlCreateTimer(Globals.TimerQueueHandle, &Context->TimerHandle, TftpdRetransmit, (PVOID)Context->Sock, Context->DueTime, Context->DueTime, 0); if (!NT_SUCCESS(ntStatus)) { DbgPrint("Failed to Arm Timer %d",ntStatus); } Context->ContextType=WRITE_CONTEXT; Context->BlockSize=Request->BlockSize; if (Context->BlockSize > MAX_OACK_PACKET_LENGTH - 4) { NewPacket = (char *)realloc(Context->Packet, Context->BlockSize + 4); if (NewPacket == NULL) { goto cleanup; } Context->Packet = NewPacket; } if ( Context->oackLength != 0 ) { Context->packetLength = Context->oackLength; Context->oackLength = 0; } else { ((unsigned short *) Context->Packet)[0] = htons(TFTPD_ACK); ((unsigned short *) Context->Packet)[1] = htons(Context->BlockNumber); Context->packetLength = 4; } Status = sendto( ReadPort, Context->Packet, Context->packetLength, 0, (struct sockaddr *) &Request->ForeignAddress, sizeof(struct sockaddr_in) ); if( SOCKET_ERROR == Status ){ DbgPrint("TftpdHandleWrite: sendto failed=%d\n", WSAGetLastError() ); goto cleanup; } cleanup: if (Context != NULL) { if (LockHeld) { TftpdReleaseContextLock((PTFTP_CONTEXT_HEADER)Context); } if (!AddedContext) { free(Context); } } // _chmod(Request->Packet3, _S_IWRITE); return 0; } // End function TftpdHandleWrite. // ======================================================================== #if defined(REMOTE_BOOT_SECURITY) DWORD TftpdHandleLogin( PVOID Argument ) /*++ Routine Description: This handles an incoming login request. Arguments: Argument - buffer containing the read request datagram freed when done. Return Value: Exit status 0 == success 1 == failure N >0 failure --*/ { int packetLength; int Retry; int Status, err; struct timeval timeval; struct sockaddr_in LoginAddress; BOOL Acked; int AddressLength; char * CharPtr; PTFTP_REQUEST Request; char * OperationType; char * PackageName; char * SecurityString; char * Options; SECURITY_STATUS SecStatus; PSecPkgInfo PackageInfo = NULL; USHORT Index = -1; ULONG MaxToken; TFTPD_SECURITY Security; ULONG SecurityHandle; USHORT LastMessageSequence; // sequence number of the last message sent SOCKET LoginPort = INVALID_SOCKET; char * IncomingMessage; SecBufferDesc IncomingDesc; SecBuffer IncomingBuffer; SecBufferDesc OutgoingDesc; SecBuffer OutgoingBuffer; BOOL FirstChallenge; struct fd_set exceptfds; struct fd_set loginfds; TimeStamp Lifetime; int BytesAck; // // Parse the request. The initial request should always be a // "login". // Request = (PTFTP_REQUEST) Argument; OperationType = &Request->Packet1[2]; // // Convert the operation to all lower case for comparison // for (CharPtr = OperationType; *CharPtr; CharPtr ++) { *CharPtr = (char)tolower(*CharPtr); } if (strcmp(OperationType, "login") == 0) { PackageName = OperationType + strlen(OperationType) + 1; // // Profile data. // DbgPrint("TftpdHandleLogin: OperationType=%s, Package=%s, from=%s.\n", OperationType, PackageName, inet_ntoa( Request->ForeignAddress.sin_addr ) ); // // Check that the security package is known. // SecStatus = QuerySecurityPackageInfoA( PackageName, &PackageInfo ); if (SecStatus != STATUS_SUCCESS) { DbgPrint("TftpdHandleLogin: invalid PackageName=%s?\n", PackageName ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_ILLEGAL_OPERATION, "invalid security package"); goto cleanup; } MaxToken = PackageInfo->cbMaxToken; FreeContextBuffer(PackageInfo); // // Things look OK so far, so let's find a spot in the array of // security information to store this client. // if (!TftpdAllocateSecurityEntry(&Index, &Security)) { DbgPrint("TftpdHandleLogin: could not allocate security entry\n" ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "insufficient resources"); goto cleanup; } // // Acquire a credential handle for the server side. // SecStatus = AcquireCredentialsHandleA( NULL, // New principal PackageName, // Package Name SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &(Security.CredentialsHandle), &Lifetime ); if ( SecStatus != STATUS_SUCCESS ) { DbgPrint("TftpdHandleLogin: AcquireCredentialsHandle failed %x\n", SecStatus ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "insufficient resources"); goto cleanup; } Security.CredentialsHandleValid = TRUE; // // Open a new socket for this request // LoginPort = socket( AF_INET, SOCK_DGRAM, 0); if (LoginPort == INVALID_SOCKET) { DbgPrint("TftpdHandleLogin: cannot open socket, Error=%d\n", WSAGetLastError() ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } // // Bind to a random address // LoginAddress.sin_family = AF_INET; LoginAddress.sin_port = 0; LoginAddress.sin_addr.s_addr = INADDR_ANY; Status = bind( LoginPort, (struct sockaddr *) &LoginAddress, sizeof(LoginAddress) ); if (Status) { DbgPrint("TftpdHandleLogin: cannot bind socket, error=%d.\n", WSAGetLastError() ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } Request->TftpdPort = LoginPort; Request->Timeout = 10; // let client set this in login packet? // // Ready to do exchanges until login is complete. // LastMessageSequence = (USHORT)-1; FirstChallenge = TRUE; IncomingMessage = PackageName + strlen(PackageName) + 1; while (1) { IncomingDesc.ulVersion = 0; IncomingDesc.cBuffers = 1; IncomingDesc.pBuffers = &IncomingBuffer; IncomingBuffer.cbBuffer = (ntohl)(((unsigned long UNALIGNED *)IncomingMessage)[0]); IncomingBuffer.BufferType = SECBUFFER_TOKEN | SECBUFFER_READONLY; IncomingBuffer.pvBuffer = IncomingMessage + 4; OutgoingDesc.ulVersion = 0; OutgoingDesc.cBuffers = 1; OutgoingDesc.pBuffers = &OutgoingBuffer; OutgoingBuffer.cbBuffer = MaxToken; OutgoingBuffer.BufferType = SECBUFFER_TOKEN; OutgoingBuffer.pvBuffer = Request->Packet2 + 8; // // Pass the client buffer to the security system -- the first time // we don't have a valid SecurityContextHandle, so we pass the // CredentialsHandle instead. // SecStatus = AcceptSecurityContext( FirstChallenge ? &(Security.CredentialsHandle) : NULL, FirstChallenge ? NULL : &(Security.ServerContextHandle), &IncomingDesc, FirstChallenge ? ISC_REQ_SEQUENCE_DETECT | ASC_REQ_ALLOW_NON_USER_LOGONS : ASC_REQ_ALLOW_NON_USER_LOGONS, SECURITY_NATIVE_DREP, &(Security.ServerContextHandle), &OutgoingDesc, &(Security.ContextAttributes), &Lifetime ); if (FirstChallenge) { Security.ServerContextHandleValid = TRUE; } FirstChallenge = FALSE; if (SecStatus != SEC_I_CONTINUE_NEEDED) { // // The login has been accepted or rejected. // ((unsigned short *) Request->Packet2)[0] = htons(TFTPD_LOGIN); ((unsigned short *) Request->Packet2)[1] = htons((USHORT)-1); if (SecStatus == STATUS_SUCCESS) { sprintf(Request->Packet2+4, "status %u handle %d ", SecStatus, (Index << 16) + Security.Validation); } else { sprintf(Request->Packet2+4, "status %u ", SecStatus); } packetLength = 4 + strlen(Request->Packet2+4); for (CharPtr = Request->Packet2+4; *CharPtr; CharPtr ++) { if (*CharPtr == ' ') { *CharPtr = '\0'; } } Security.LoginComplete = TRUE; Security.LoginStatus = SecStatus; Security.ForeignAddress = Request->ForeignAddress; TftpdStoreSecurityEntry(Index, &Security); LastMessageSequence = (USHORT)-1; } else if (SecStatus == SEC_I_CONTINUE_NEEDED) { // // Need to exchange with the client. Note that the response // message has already been stored at Request->Packet2 + 8. // ++LastMessageSequence; ((unsigned short *) Request->Packet2)[0] = htons(TFTPD_LOGIN); ((unsigned short *) Request->Packet2)[1] = htons(LastMessageSequence); ((unsigned long UNALIGNED *) Request->Packet2)[1] = htonl(OutgoingBuffer.cbBuffer); packetLength = 8 + OutgoingBuffer.cbBuffer; } Acked = FALSE; Retry = 0; while (!Acked && (Retry < MAX_TFTPD_RETRIES) ){ // // send the data // Status = sendto( LoginPort, Request->Packet2, packetLength, 0, (struct sockaddr *) &Request->ForeignAddress, sizeof(struct sockaddr_in)); if( SOCKET_ERROR == Status ){ DbgPrint("TftpdHandleLogin: sendto failed=%d\n", WSAGetLastError() ); goto cleanup; } // // wait for the ack // FD_ZERO( &loginfds ); FD_ZERO( &exceptfds ); FD_SET( LoginPort, &loginfds ); FD_SET( LoginPort, &exceptfds ); timeval.tv_sec = Request->Timeout; timeval.tv_usec = 0; Status = select(0, &loginfds, NULL, &exceptfds, &timeval); if( SOCKET_ERROR == Status ){ DbgPrint("TftpdHandleLogin: select failed=%d\n", WSAGetLastError() ); goto cleanup; } if ((Status > 0) && (FD_ISSET(LoginPort, &loginfds))) { // // Got response, maybe // AddressLength = sizeof(LoginAddress); BytesAck = recvfrom( LoginPort, Request->Packet1, sizeof(Request->Packet1), 0, (struct sockaddr *) &LoginAddress, &AddressLength); if( SOCKET_ERROR == BytesAck ){ DbgPrint("TftpdHandleLogin: recvfrom failed=%d\n", WSAGetLastError() ); goto cleanup; } if (CHECK_ACK(Request->Packet1, TFTPD_LOGIN, LastMessageSequence)) { Acked = TRUE; } } Retry ++; } // end while. if (!Acked) { DbgPrint("TftpdHandleLogin: Timed out waiting for ack\n"); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Timeout"); goto cleanup; } if (LastMessageSequence == (USHORT)-1) { // // If we got an ack for the last sequence number, then // break. // break; } else { // // Loop back and process this message. // IncomingMessage = Request->Packet1 + 4; } } // end while 1. } else if (strcmp(OperationType, "logoff") == 0) { PackageName = OperationType + strlen(OperationType) + 1; // // Don't bother checking the package name. // SecurityString = PackageName + strlen(PackageName) + 1; for (CharPtr = SecurityString; *CharPtr; CharPtr ++) { *CharPtr = (char)tolower(*CharPtr); } if (strcmp(SecurityString, "security") != 0) { DbgPrint("TftpdHandleLogin: invalid logoff handle %s\n", SecurityString ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_ILLEGAL_OPERATION, "invalid security handle"); goto cleanup; } // // Open a new socket for this request // LoginPort = socket( AF_INET, SOCK_DGRAM, 0); if (LoginPort == INVALID_SOCKET) { DbgPrint("TftpdHandleLogin: cannot open socket, Error=%d\n", WSAGetLastError() ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } // // Bind to a random address // LoginAddress.sin_family = AF_INET; LoginAddress.sin_port = 0; LoginAddress.sin_addr.s_addr = INADDR_ANY; Status = bind( LoginPort, (struct sockaddr *) &LoginAddress, sizeof(LoginAddress) ); if (Status) { DbgPrint("TftpdHandleLogin: cannot bind socket, error=%d.\n", WSAGetLastError() ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } // // Start to prepare the response. // ((unsigned short *) Request->Packet2)[0] = htons(TFTPD_LOGIN); ((unsigned short *) Request->Packet2)[1] = htons((USHORT)-1); // // Now get the handle and delete the security entry if it is valid. // Options = SecurityString + strlen(SecurityString) + 1; SecurityHandle = atoi(Options); TftpdGetSecurityEntry((USHORT)(SecurityHandle >> 16), &Security); if (Security.Validation == ((SecurityHandle) & 0xffff)) { TftpdFreeSecurityEntry((USHORT)(SecurityHandle >> 16)); sprintf(Request->Packet2+4, "status %u ", 0); } else { sprintf(Request->Packet2+4, "status %u ", STATUS_INVALID_HANDLE); } packetLength = 4 + strlen(Request->Packet2+4); for (CharPtr = Request->Packet2+4; *CharPtr; CharPtr ++) { if (*CharPtr == ' ') { *CharPtr = '\0'; } } // // Wait for his ack, but not for too long. // Acked = FALSE; Retry = 0; while (!Acked && (Retry < 3) ){ // // send the data // Status = sendto( LoginPort, Request->Packet2, packetLength, 0, (struct sockaddr *) &Request->ForeignAddress, sizeof(struct sockaddr_in)); if( SOCKET_ERROR == Status ){ DbgPrint("TftpdHandleLogin: sendto failed=%d\n", WSAGetLastError() ); goto cleanup; } // // wait for the ack // FD_ZERO( &loginfds ); FD_ZERO( &exceptfds ); FD_SET( LoginPort, &loginfds ); FD_SET( LoginPort, &exceptfds ); timeval.tv_sec = 2; timeval.tv_usec = 0; Status = select(0, &loginfds, NULL, &exceptfds, &timeval); if( SOCKET_ERROR == Status ){ DbgPrint("TftpdHandleLogin: select failed=%d\n", WSAGetLastError() ); goto cleanup; } if ((Status > 0) && (FD_ISSET(LoginPort, &loginfds))) { // // Got response, maybe // AddressLength = sizeof(LoginAddress); BytesAck = recvfrom( LoginPort, Request->Packet1, sizeof(Request->Packet1), 0, (struct sockaddr *) &LoginAddress, &AddressLength); if( SOCKET_ERROR == BytesAck ){ DbgPrint("TftpdHandleLogin: recvfrom failed=%d\n", WSAGetLastError() ); goto cleanup; } if (CHECK_ACK(Request->Packet1, TFTPD_LOGIN, (USHORT)-1)) { Acked = TRUE; } } Retry ++; } // end while. // // If the ack timed out, don't worry about it. // } else { DbgPrint("TftpdHandleLogin: invalid OperationType=%s?\n", OperationType ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_ILLEGAL_OPERATION, NULL); goto cleanup; } cleanup: if (LoginPort != INVALID_SOCKET) { closesocket(LoginPort); } // free(Request); return 0; } #endif //defined(REMOTE_BOOT_SECURITY) #if defined(REMOTE_BOOT_SECURITY) DWORD TftpdHandleKey( PVOID Argument ) /*++ Routine Description: This handles an incoming key. Arguments: Argument - buffer containing the read request datagram freed when done. Return Value: Exit status 0 == success 1 == failure N >0 failure --*/ { int Status, err; int packetLength; struct timeval timeval; struct sockaddr_in LoginAddress; BOOL Acked; int AddressLength; char * CharPtr; PTFTP_REQUEST Request; char * OperationType; char * SpiString; char * SecurityString; ULONG SpiValue; ULONG KeyValue; ULONG SecurityHandle; SOCKET LoginPort = INVALID_SOCKET; HANDLE IpsecHandle = INVALID_HANDLE_VALUE; BOOL IOStatus; char PolicyBuffer[sizeof(IPSEC_SET_POLICY) + sizeof(IPSEC_POLICY_INFO)]; PIPSEC_SET_POLICY SetPolicy = (PIPSEC_SET_POLICY)PolicyBuffer; IPSEC_FILTER OutboundFilter; IPSEC_FILTER InboundFilter; IPSEC_GET_SPI GetSpi; char SaBuffer[sizeof(IPSEC_ADD_UPDATE_SA) + (6 * sizeof(ULONG))]; PIPSEC_ADD_UPDATE_SA AddUpdateSa; IPSEC_DELETE_POLICY DeletePolicy; // char EnumPolicyBuffer[(UINT)(FIELD_OFFSET(IPSEC_ENUM_POLICY, pInfo[0]))]; char EnumPolicyBuffer[2 * sizeof(DWORD)]; PIPSEC_ENUM_POLICY EnumPolicy; DWORD EnumPolicySize; char MyName[80]; PHOSTENT Host; DWORD BytesReturned; DWORD i; LARGE_INTEGER SystemTime; TFTPD_SECURITY Security; // // Parse the request. The initial request should always be a // "spi". // Request = (PTFTP_REQUEST) Argument; OperationType = &Request->Packet1[4]; // // Convert the operation to all lower case for comparison // for (CharPtr = OperationType; *CharPtr; CharPtr ++) { *CharPtr = (char)tolower(*CharPtr); } if (strcmp(OperationType, "spi") == 0) { SpiString = OperationType + sizeof("spi"); SpiValue = atoi(SpiString); OperationType = SpiString + strlen(SpiString) + 1; // // See if the client request encryption of the key. // if (strcmp(OperationType, "security") == 0) { SecurityString = OperationType + sizeof("security"); SecurityHandle = atoi(SecurityString); // // High 16 bits of handle is index, low 16 bits is validation. // TftpdGenerateKeyForSecurityEntry((USHORT)(SecurityHandle >> 16), &Security); if ((Security.Validation != ((Request->SecurityHandle) & 0xffff)) || (!Security.GeneratedKey)) { DbgPrint("TftpdHandleRead: SecurityHandle %x is invalid.\n", Request->SecurityHandle); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Invalid security handle"); goto cleanup; } KeyValue = Security.Key; DbgPrint("TftpdHandleKey: SPI %lx, retrieved secure key %lx\n", SpiValue, KeyValue); } else { NtQuerySystemTime(&SystemTime); KeyValue = (ULONG)(SystemTime.QuadPart % Request->ForeignAddress.sin_addr.s_addr); SecurityHandle = 0; DbgPrint("TftpdHandleKey: SPI %lx, generated key %lx\n", SpiValue, KeyValue); } // // Open IPSEC so we can send down IOCTLS. // IpsecHandle = CreateFileW( DD_IPSEC_DOS_NAME, // IPSEC device name GENERIC_READ | GENERIC_WRITE, // access (read-write) mode 0, // share mode NULL, // pointer to security attributes OPEN_EXISTING, // how to create 0, // file attributes NULL); // handle to file with attributes to copy if (IpsecHandle == INVALID_HANDLE_VALUE) { DbgPrint("TftpdHandleKey: Could not open <%ws>\n", DD_IPSEC_DOS_NAME); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } // // See how many policies are defined. First send down a buffer // with room for no policies, to see how many there are. // EnumPolicy = (PIPSEC_ENUM_POLICY)EnumPolicyBuffer; memset(EnumPolicy, 0, sizeof(EnumPolicyBuffer)); IOStatus = DeviceIoControl( IpsecHandle, // Driver handle IOCTL_IPSEC_ENUM_POLICIES, // Control code EnumPolicy, // Input buffer sizeof(EnumPolicyBuffer), // Input buffer size EnumPolicy, // Output buffer sizeof(EnumPolicyBuffer), // Output buffer size &BytesReturned, NULL); if (!IOStatus) { if (GetLastError() != ERROR_MORE_DATA) { DbgPrint("TftpdHandleKey: IOCTL_IPSEC_ENUM_POLICY #1 failed %x\n", GetLastError()); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "IOCTL_IPSEC_ENUM_POLICIES"); goto cleanup; } EnumPolicySize = FIELD_OFFSET(IPSEC_ENUM_POLICY, pInfo[0]) + sizeof(IPSEC_POLICY_INFO) * EnumPolicy->NumEntriesPresent; EnumPolicy = malloc(EnumPolicySize); if (EnumPolicy == NULL) { DbgPrint("TftpdHandleKey: alloc ENUM_POLICIES buffer failed %x\n", GetLastError()); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "IOCTL_IPSEC_ENUM_POLICIES"); goto cleanup; } // // Re-submit the IOCTL. // memset(EnumPolicy, 0, EnumPolicySize); IOStatus = DeviceIoControl( IpsecHandle, // Driver handle IOCTL_IPSEC_ENUM_POLICIES, // Control code EnumPolicy, // Input buffer EnumPolicySize, // Input buffer size EnumPolicy, // Output buffer EnumPolicySize, // Output buffer size &BytesReturned, NULL); // // We may get MORE_DATA if someone just added a policy, but // that is OK since it won't be for this remote. // if (!IOStatus && (GetLastError() != ERROR_MORE_DATA)) { DbgPrint("TftpdHandleKey: IOCTL_IPSEC_ENUM_POLICY #2 failed %x\n", GetLastError()); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "IOCTL_IPSEC_ENUM_POLICIES"); free(EnumPolicy); goto cleanup; } // // Display all the policies. // // Delete any policies involving the remote machine. // for (i = 0; i < EnumPolicy->NumEntriesPresent; i++) { if ((EnumPolicy->pInfo[i].AssociatedFilter.SrcAddr == Request->ForeignAddress.sin_addr.s_addr) || (EnumPolicy->pInfo[i].AssociatedFilter.DestAddr == Request->ForeignAddress.sin_addr.s_addr)) { DeletePolicy.NumEntries = 1; DeletePolicy.pInfo[0] = EnumPolicy->pInfo[i]; IOStatus = DeviceIoControl( IpsecHandle, // Driver handle IOCTL_IPSEC_DELETE_POLICY, // Control code &DeletePolicy, // Input buffer sizeof(IPSEC_DELETE_POLICY), // Input buffer size NULL, // Output buffer 0, // Output buffer size &BytesReturned, NULL); if (!IOStatus) { DbgPrint("TftpdHandleKey: IOCTL_IPSEC_DELETE_POLICY(%lx, %lx) failed %x\n", EnumPolicy->pInfo[i].AssociatedFilter.SrcAddr, EnumPolicy->pInfo[i].AssociatedFilter.DestAddr, GetLastError()); } } } free(EnumPolicy); } else { // // If the call succeeds, we don't need to do anything, since // there should have been 0 policies returned. // } // // Get our local IP address. // gethostname(MyName, sizeof(MyName)); Host = gethostbyname(MyName); // // Set the policy. We need two filters, one for outbound and // one for inbound. // memset(&OutboundFilter, 0, sizeof(IPSEC_FILTER)); memset(&InboundFilter, 0, sizeof(IPSEC_FILTER)); OutboundFilter.SrcAddr = *(DWORD *)Host->h_addr; OutboundFilter.SrcMask = 0xffffffff; // OutboundFilter.SrcPort = 0x8B; // netbios session port OutboundFilter.DestAddr = Request->ForeignAddress.sin_addr.s_addr; OutboundFilter.DestMask = 0xffffffff; OutboundFilter.Protocol = 0x6; // TCP InboundFilter.SrcAddr = Request->ForeignAddress.sin_addr.s_addr; InboundFilter.SrcMask = 0xffffffff; InboundFilter.DestAddr = *(DWORD *)Host->h_addr; InboundFilter.DestMask = 0xffffffff; // InboundFilter.DestPort = 0x8B; // netbios session port InboundFilter.Protocol = 0x6; // TCP memset(SetPolicy, 0, sizeof(PolicyBuffer)); SetPolicy->NumEntries = 2; SetPolicy->pInfo[0].Index = 1; SetPolicy->pInfo[0].AssociatedFilter = OutboundFilter; SetPolicy->pInfo[1].Index = 2; SetPolicy->pInfo[1].AssociatedFilter = InboundFilter; IOStatus = DeviceIoControl( IpsecHandle, // Driver handle IOCTL_IPSEC_SET_POLICY, // Control code SetPolicy, // Input buffer sizeof(PolicyBuffer), // Input buffer size NULL, // Output buffer 0, // Output buffer size &BytesReturned, NULL); if (!IOStatus) { DbgPrint("TftpdHandleKey: IOCTL_IPSEC_SET_POLICY failed %x\n", GetLastError()); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "IOCTL_IPSEC_SET_POLICY"); goto cleanup; } // // Now get an SPI to give to the remote. // GetSpi.Context = 0; GetSpi.InstantiatedFilter = InboundFilter; IOStatus = DeviceIoControl( IpsecHandle, // Driver handle IOCTL_IPSEC_GET_SPI, // Control code &GetSpi, // Input buffer sizeof(IPSEC_GET_SPI), // Input buffer size &GetSpi, // Output buffer sizeof(IPSEC_GET_SPI), // Output buffer size &BytesReturned, NULL); if (!IOStatus) { DbgPrint("TftpdHandleKey: IOCTL_IPSEC_GET_SPI failed %x\n", GetLastError()); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "IOCTL_IPSEC_GET_SPI"); goto cleanup; } // // Set up the security association for the outbound // connection. // AddUpdateSa = (PIPSEC_ADD_UPDATE_SA)SaBuffer; memset(AddUpdateSa, 0, sizeof(SaBuffer)); AddUpdateSa->SAInfo.Context = GetSpi.Context; AddUpdateSa->SAInfo.NumSAs = 1; AddUpdateSa->SAInfo.InstantiatedFilter = OutboundFilter; AddUpdateSa->SAInfo.SecAssoc[0].Operation = Encrypt; AddUpdateSa->SAInfo.SecAssoc[0].SPI = SpiValue; AddUpdateSa->SAInfo.SecAssoc[0].IntegrityAlgo.algoIdentifier = IPSEC_AH_MD5; AddUpdateSa->SAInfo.SecAssoc[0].IntegrityAlgo.algoKeylen = 4 * sizeof(ULONG); AddUpdateSa->SAInfo.SecAssoc[0].ConfAlgo.algoIdentifier = IPSEC_ESP_DES; AddUpdateSa->SAInfo.SecAssoc[0].ConfAlgo.algoKeylen = 2 * sizeof(ULONG); AddUpdateSa->SAInfo.KeyLen = 6 * sizeof(ULONG); memcpy(AddUpdateSa->SAInfo.KeyMat, &KeyValue, sizeof(ULONG)); memcpy(AddUpdateSa->SAInfo.KeyMat+sizeof(ULONG), &KeyValue, sizeof(ULONG)); memcpy(AddUpdateSa->SAInfo.KeyMat+(2*sizeof(ULONG)), &KeyValue, sizeof(ULONG)); memcpy(AddUpdateSa->SAInfo.KeyMat+(3*sizeof(ULONG)), &KeyValue, sizeof(ULONG)); memcpy(AddUpdateSa->SAInfo.KeyMat+(4*sizeof(ULONG)), &KeyValue, sizeof(ULONG)); memcpy(AddUpdateSa->SAInfo.KeyMat+(5*sizeof(ULONG)), &KeyValue, sizeof(ULONG)); IOStatus = DeviceIoControl( IpsecHandle, // Driver handle IOCTL_IPSEC_ADD_SA, // Control code AddUpdateSa, // Input buffer FIELD_OFFSET(IPSEC_ADD_UPDATE_SA, SAInfo.KeyMat[0]) + AddUpdateSa->SAInfo.KeyLen, // Input buffer size NULL, // Output buffer 0, // Output buffer size &BytesReturned, NULL); if (!IOStatus) { DbgPrint("TftpdHandleKey: IOCTL_IPSEC_ADD_SA failed %x\n", GetLastError()); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "IOCTL_IPSEC_ADD_SA"); goto cleanup; } // // Set up the security association for the inbound connection. // If our Operation is "None", then IPSEC does this for us. // if (AddUpdateSa->SAInfo.SecAssoc[0].Operation != None) { AddUpdateSa->SAInfo.SecAssoc[0].SPI = GetSpi.SPI; AddUpdateSa->SAInfo.InstantiatedFilter = InboundFilter; IOStatus = DeviceIoControl( IpsecHandle, // Driver handle IOCTL_IPSEC_UPDATE_SA, // Control code AddUpdateSa, // Input buffer FIELD_OFFSET(IPSEC_ADD_UPDATE_SA, SAInfo.KeyMat[0]) + AddUpdateSa->SAInfo.KeyLen, // Input buffer size NULL, // Output buffer 0, // Output buffer size &BytesReturned, NULL); if (!IOStatus) { DbgPrint("TftpdHandleKey: IOCTL_IPSEC_UPDATE_SA failed %x\n", GetLastError()); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "IOCTL_IPSEC_UPDATE_SA"); goto cleanup; } } // // Open a new socket for this request // LoginPort = socket( AF_INET, SOCK_DGRAM, 0); if (LoginPort == INVALID_SOCKET) { DbgPrint("TftpdHandleKey: cannot open socket, Error=%d\n", WSAGetLastError() ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } // // Bind to a random address // LoginAddress.sin_family = AF_INET; LoginAddress.sin_port = 0; LoginAddress.sin_addr.s_addr = INADDR_ANY; Status = bind( LoginPort, (struct sockaddr *) &LoginAddress, sizeof(LoginAddress) ); if (Status) { DbgPrint("TftpdHandleKey: cannot bind socket, error=%d.\n", WSAGetLastError() ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_UNDEFINED, "Insufficient resources"); goto cleanup; } // // Generate the response for the client. // ((unsigned short *) Request->Packet2)[0] = htons(TFTPD_KEY); Request->Packet2[2] = Request->Packet1[2]; // copy sequence number Request->Packet2[3] = Request->Packet1[3]; // // They key is sent as hex digits since it might be longer // than four bytes. // if (SecurityHandle == 0) { // // No security, send key in the clear. // sprintf(Request->Packet2+4, "spi %d key %2.2x%2.2x%2.2x%2.2x", GetSpi.SPI, ((PUCHAR)(&KeyValue))[0], ((PUCHAR)(&KeyValue))[1], ((PUCHAR)(&KeyValue))[2], ((PUCHAR)(&KeyValue))[3]); packetLength = 4 + strlen(Request->Packet2+4); } else { PCHAR SignLoc; ULONG i; // // Security requested, so send the encrypted key and the sign. // sprintf(Request->Packet2+4, "spi %d security %d sign ", GetSpi.SPI, SecurityHandle); packetLength = 4 + strlen(Request->Packet2+4); SignLoc = Request->Packet2 + packetLength; for (i = 0; i < NTLMSSP_MESSAGE_SIGNATURE_SIZE; i++) { sprintf(SignLoc, "%2.2x", Security.Sign[i]); SignLoc += 2; packetLength += 2; } sprintf(Request->Packet2+packetLength, " key %2.2x%2.2x%2.2x%2.2x", Security.SignedKey[0], Security.SignedKey[1], Security.SignedKey[2], Security.SignedKey[3]); packetLength += strlen(" key ") + (2 * sizeof(Security.SignedKey)); } for (CharPtr = Request->Packet2+4; *CharPtr; CharPtr ++) { if (*CharPtr == ' ') { *CharPtr = '\0'; } } // // Send the response back to the client. // Status = sendto( LoginPort, Request->Packet2, packetLength, 0, (struct sockaddr *) &Request->ForeignAddress, sizeof(struct sockaddr_in)); if( SOCKET_ERROR == Status ){ DbgPrint("TftpdHandleKey: sendto failed=%d\n", WSAGetLastError() ); goto cleanup; } } else { DbgPrint("TftpdHandleKey: invalid OperationType=%s?\n", OperationType ); TftpdErrorPacket( (struct sockaddr *) &Request->ForeignAddress, Request->Packet2, Request->TftpdPort, TFTPD_ERROR_ILLEGAL_OPERATION, NULL); goto cleanup; } cleanup: if (IpsecHandle != INVALID_HANDLE_VALUE) { CloseHandle(IpsecHandle); } if (LoginPort != INVALID_SOCKET) { closesocket(LoginPort); } // free(Request); return 0; } #endif //defined(REMOTE_BOOT_SECURITY) // ======================================================================== int TftpdDoRead( int ReadFd, char * Buffer, int BufferSize, int ReadMode) /*++ Routine Description: This does a read with the appropriate conversions for netascii or octet modes. Arguments: ReadFd - file to read from Buffer - Buffer to read into BufferSize - size of buffer ReadMode - O_TEXT or O_BINARY O_TEXT means the netascii conversions must be done O_BINARY means octet mode Return Value: BytesRead Error? --*/ { int BytesRead; int BytesWritten; int BytesUsed; char NextChar; char State; char LocalBuffer[MAX_TFTP_DATA]; int err; if (ReadMode == O_BINARY) { BytesRead = _read(ReadFd, Buffer, BufferSize); if( BytesRead == -1 ){ DbgPrint("TftpdDoRead: read failed, errno=%d\n", errno ); SetLastError( errno ); } return(BytesRead); } else { // // Do those cr/lf conversions. A \r not followed by a \n must // be followed by a \0. // BytesWritten = 0; BytesUsed = 0; State = '\0'; BytesRead = _read(ReadFd, LocalBuffer, sizeof(LocalBuffer)); if( BytesRead == -1 ){ DbgPrint("TftpdDoRead: read failed, errno=%d\n", errno ); SetLastError( errno ); return -1; } while ((BytesUsed < BytesRead) && (BytesWritten < BufferSize)) { NextChar = LocalBuffer[BytesUsed++]; if (State == '\r') { if (NextChar == '\n') { Buffer[BytesWritten++] = NextChar; State = '\0'; } else { Buffer[BytesWritten++] = '\0'; Buffer[BytesWritten++] = NextChar; State = '\0'; } } else { Buffer[BytesWritten++] = NextChar; State = '\0'; } if (NextChar == '\r') { State = '\r'; } } err = _lseek(ReadFd, BytesUsed - BytesRead, SEEK_CUR); if( err == -1 ){ DbgPrint("TftpdDoRead: lseek failed, errno=%d\n", errno ); SetLastError( errno ); return -1; } return(BytesWritten); } } // End function TftpdDoRead. // ======================================================================== int TftpdDoWrite( int WriteFd, char * Buffer, int BufferSize, int WriteMode, char * State) /*++ Routine Description: This does a write with the appropriate conversions for netascii or octet modes. Arguments: WriteFd - file to write to Buffer - Buffer to read into BufferSize - size of buffer WriteMode - O_TEXT or O_BINARY O_TEXT means the netascii conversions must be done O_BINARY means octet mode State - pointer to the current output state. If the last character in the buffer is a '\r', that fact must be remembered. Return Value: BytesWritten Error? --*/ { int BytesWritten; int i; char OutputBuffer[MAX_TFTP_DATA*2]; int OutputPointer; if (WriteMode == O_BINARY) { BytesWritten = _write(WriteFd, Buffer, BufferSize); if( BytesWritten == -1 ){ DbgPrint("TftpdDoWrite: write failed=%d\n", errno ); SetLastError( errno ); } } else { // // Do those cr/lf conversions. If a '\r' followed by a '\0' is // followed by a '\0', the '\0' is stripped. // OutputPointer = 0; for (i=0; i