|
|
/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
util.c
Abstract:
This module contains functions to parse and construct server file paths for client requests, request option negotiation, and client access security.
Author:
Jeffrey C. Venable, Sr. (jeffv) 01-Jun-2001
Revision History:
--*/
#include "precomp.h"
#define IS_SEPARATOR(c) (((c) == '\\') || ((c) == '/'))
BOOL TftpdUtilIsValidString(char *string, unsigned int maxLength) {
UINT x;
// Make sure 'string' is null-terminated.
for (x = 0; x < maxLength; x++) if (!string[x]) return (TRUE);
return (FALSE);
} // TftpdUtilIsValidString()
BOOL TftpdUtilCanonicalizeFileName(char *filename) {
char *source, *destination, *lastComponent;
// The canonicalization is done in place. Initialize the source and
// destination pointers to point to the same place.
source = 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, source points to the first charaecter
// after a directory separator or the first character of the
// pathname.
while (*source) {
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 && !IS_SEPARATOR(*source));
} // if (IS_SEPARATOR(*(source + 1)))
} else {
// source does not point to a dot, so copy source to
// destination until we get to a directory separator.
while (*source && !IS_SEPARATOR(*source)) *destination++ = *source++;
} // if (*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 && IS_SEPARATOR(*source));
// Make lastComponent point to the character after the directory
// separator.
lastComponent = destination;
} // if (IS_SEPARATOR(*source))
} // while (*source)
// 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 = '\0';
return (TRUE);
} // TftpdUtilCanonicalizeFileName()
BOOL TftpdUtilPrependStringToFileName(char *filename, DWORD maxLength, char *prefix) {
DWORD prefixLength = strlen(prefix); DWORD filenameLength = strlen(filename); BOOL prefixHasSeparater = (prefix[prefixLength - 1] == '\\'); BOOL filenameHasSeparater = (filename[0] == '\\'); DWORD separatorLength = 0;
if (!prefixHasSeparater && !filenameHasSeparater) separatorLength = 1;
if (prefixHasSeparater && filenameHasSeparater) prefixLength--;
if ((prefixLength + separatorLength + filenameLength) > (maxLength - 1)) return (FALSE);
// Move the existing string down to make room for the prefix.
MoveMemory(filename + prefixLength + separatorLength, filename, filenameLength + 1); // Move the prefix into place.
CopyMemory(filename, prefix, prefixLength); // If necessary, insert a backslash between the prefix and the file name.
if (separatorLength) filename[prefixLength] = '\\'; // Terminate the string.
filename[prefixLength + separatorLength + filenameLength] = '\0';
return (TRUE);
} // TftpdUtilPrependStringToFileName()
BOOL TftpdUtilGetFileModeAndOptions(PTFTPD_CONTEXT context, PTFTPD_BUFFER buffer) {
DWORD remaining = (TFTPD_DEF_DATA - FIELD_OFFSET(TFTPD_BUFFER, message.data)); char *filename, *mode, *option; int length;
// Obtain and validate the requested filename.
filename = buffer->message.rrq.data; // or wrq, same thing
if (!TftpdUtilIsValidString(filename, remaining)) { TftpdIoSendErrorPacket(buffer, TFTPD_ERROR_ILLEGAL_OPERATION, "Malformed file name"); return (FALSE); } length = (strlen(filename) + 1); remaining -= length; if (!TftpdUtilCanonicalizeFileName(filename)) { TftpdIoSendErrorPacket(buffer, TFTPD_ERROR_ILLEGAL_OPERATION, "Malformed file name"); return (FALSE); }
// Obtain and validate the mode.
mode = (char *)(buffer->message.rrq.data + length); if (!TftpdUtilIsValidString(mode, remaining)) return (FALSE); length = (strlen(mode) + 1); if (!_stricmp(mode, "netascii")) context->mode = TFTPD_MODE_TEXT; else if (!_stricmp(mode, "octet")) context->mode = TFTPD_MODE_BINARY; else { TftpdIoSendErrorPacket(buffer, TFTPD_ERROR_ILLEGAL_OPERATION, "Illegal TFTP operation"); return (FALSE); } remaining -= length;
// Obtain and validate any requested options.
option = (char *)(mode + length); while (remaining && *option) {
char *value;
if (!TftpdUtilIsValidString(option, remaining)) break; length = (strlen(option) + 1); remaining -= length; value = (char *)(option + length); if (!remaining || !TftpdUtilIsValidString(value, remaining)) break; length = (strlen(value) + 1); remaining -= length;
if (!_stricmp(option, "blksize")) { if (!(context->options & TFTPD_OPTION_BLKSIZE)) { int blksize = atoi(value); // 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 (blksize != 1456) { blksize = __max(TFTPD_MIN_DATA, blksize); blksize = __min(TFTPD_MAX_DATA, blksize); context->blksize = blksize; context->options |= TFTPD_OPTION_BLKSIZE; } } } else if (!_stricmp(option, "timeout")) { if (!(context->options & TFTPD_OPTION_TIMEOUT)) { int seconds = atoi(value); if ((seconds >= 1) && (seconds <= 255)) { context->timeout = (seconds * 1000); context->options |= TFTPD_OPTION_TIMEOUT; } } } else if (!_stricmp(option, "tsize")) { if (context->mode != TFTPD_MODE_TEXT) { context->options |= TFTPD_OPTION_TSIZE; context->filesize.QuadPart = _atoi64(value); } }
// Advance over the option and its value to next option or NUL terminator.
option += (strlen(option) + 1 + length);
} // while (*option)
if (!(context->options & TFTPD_OPTION_BLKSIZE)) context->blksize = TFTPD_DEF_DATA;
// Now that we've obtained all the information we need from the buffer, we're
// free to overwrite it (reuse it) to prepend the filename with its prefix.
if (!TftpdUtilPrependStringToFileName(filename, TFTPD_DEF_BUFFER - FIELD_OFFSET(TFTPD_BUFFER, message.rrq.data), globals.parameters.rootDirectory)) { TftpdIoSendErrorPacket(buffer, TFTPD_ERROR_ILLEGAL_OPERATION, "Malformed file name"); return (FALSE); }
length = (strlen(filename) + 1); context->filename = (char *)HeapAlloc(globals.hServiceHeap, HEAP_ZERO_MEMORY, length); if (context->filename == NULL) { TftpdIoSendErrorPacket(buffer, TFTPD_ERROR_UNDEFINED, "Out of memory"); return (FALSE); } strcpy(context->filename, filename);
return (TRUE);
} // TftpdUtilGetFileModeAndOptions()
PTFTPD_BUFFER TftpdUtilSendOackPacket(PTFTPD_BUFFER buffer) {
PTFTPD_CONTEXT context = buffer->internal.context; char *oack; int length;
// Build the OACK message.
ZeroMemory(&buffer->message, buffer->internal.datasize); buffer->message.opcode = htons(TFTPD_OACK); oack = (char *)&buffer->message.oack.data; buffer->internal.io.bytes = (FIELD_OFFSET(TFTPD_BUFFER, message.oack.data) - FIELD_OFFSET(TFTPD_BUFFER, message.opcode)); if (context->options & TFTPD_OPTION_BLKSIZE) { strcpy(oack, "blksize"); oack += 8; _itoa(context->blksize, oack, 10); length = (strlen(oack) + 1); oack += length; buffer->internal.io.bytes += (8 + length); } if (context->options & TFTPD_OPTION_TIMEOUT) { strcpy(oack, "timeout"); oack += 8; _itoa((context->timeout / 1000), oack, 10); length = (strlen(oack) + 1); oack += length; buffer->internal.io.bytes += (8 + length); } if (context->options & TFTPD_OPTION_TSIZE) { strcpy(oack, "tsize"); oack += 6; _itoa((int)context->filesize.QuadPart, oack, 10); length = (strlen(oack) + 1); oack += length; buffer->internal.io.bytes += (6 + length); }
TFTPD_DEBUG((TFTPD_TRACE_IO, "TftpdUtilSendOackPacket(buffer = %p, context = %p): Issuing OACK, %d bytes. " "[blksize = %d, timeout = %d, tsize = %d]\n", buffer, context, buffer->internal.io.bytes, context->blksize, context->timeout, context->filesize));
if (!TftpdProcessComplete(buffer)) return (buffer);
return (TftpdIoSendPacket(buffer));
} // TftpdUtilSendOackPacket()
BOOL TftpdUtilMatch(const char *const p, const char *const q) {
switch (*p) {
case '\0' : return (!(*q));
case '*' : return (TftpdUtilMatch(p + 1, q) || (*q && TftpdUtilMatch(p, q + 1)));
case '?' : return (*q && TftpdUtilMatch(p + 1, q + 1));
default : return ((*p == *q) && TftpdUtilMatch(p + 1, q + 1));
} // switch (*p)
} // TftpdUtilMatch()
|