- Addded support for multi-line values in INI files, so you can't maliciously inject stray

newline characters into the config file using ACS's SetCVarString.

SVN r4326 (trunk)
This commit is contained in:
Randy Heit 2013-06-04 02:06:40 +00:00
parent c4b7335312
commit 5e7ee8f33e
2 changed files with 109 additions and 2 deletions

View file

@ -38,9 +38,12 @@
#include "doomtype.h" #include "doomtype.h"
#include "configfile.h" #include "configfile.h"
#include "m_random.h"
#define READBUFFERSIZE 256 #define READBUFFERSIZE 256
static FRandom pr_endtag;
//==================================================================== //====================================================================
// //
// FConfigFile Constructor // FConfigFile Constructor
@ -679,13 +682,68 @@ bool FConfigFile::ReadConfig (void *file)
whiteprobe++; whiteprobe++;
} }
*(whiteprobe - 1) = 0; *(whiteprobe - 1) = 0;
// Check for multi-line value
if (whiteprobe[0] == '<' && whiteprobe[1] == '<' && whiteprobe[2] == '<' && whiteprobe[3] != '\0')
{
ReadMultiLineValue (file, section, start, whiteprobe + 3);
}
else
{
NewConfigEntry (section, start, whiteprobe); NewConfigEntry (section, start, whiteprobe);
} }
} }
} }
}
return true; return true;
} }
//====================================================================
//
// FConfigFile :: ReadMultiLineValue
//
// Reads a multi-line value, with format as follows:
//
// key=<<<ENDTAG
// ... blah blah blah ...
// >>>ENDTAG
//
// The final ENDTAG must be on a line all by itself.
//
//====================================================================
FConfigFile::FConfigEntry *FConfigFile::ReadMultiLineValue(void *file, FConfigSection *section, const char *key, const char *endtag)
{
char readbuf[READBUFFERSIZE];
FString value;
size_t endlen = strlen(endtag);
// Keep on reading lines until we reach a line that matches >>>endtag
while (ReadLine(readbuf, READBUFFERSIZE, file) != NULL)
{
// Does the start of this line match the endtag?
if (readbuf[0] == '>' && readbuf[1] == '>' && readbuf[2] == '>' &&
strncmp(readbuf + 3, endtag, endlen) == 0)
{ // Is there nothing but line break characters after the match?
size_t i;
for (i = endlen + 3; readbuf[i] != '\0'; ++i)
{
if (readbuf[i] != '\n' && readbuf[i] != '\r')
{ // Not a line break character
break;
}
}
if (readbuf[i] == '\0')
{ // We're done; strip the previous line's line breaks, since it's not part of the value.
value.StripRight("\n\r");
}
break;
}
// Append this line to the value.
value << readbuf;
}
return NewConfigEntry(section, key, value);
}
//==================================================================== //====================================================================
// //
// FConfigFile :: ReadLine // FConfigFile :: ReadLine
@ -732,7 +790,16 @@ bool FConfigFile::WriteConfigFile () const
fprintf (file, "[%s]\n", section->Name); fprintf (file, "[%s]\n", section->Name);
while (entry != NULL) while (entry != NULL)
{ {
if (strpbrk(entry->Value, "\r\n") == NULL)
{ // Single-line value
fprintf (file, "%s=%s\n", entry->Key, entry->Value); fprintf (file, "%s=%s\n", entry->Key, entry->Value);
}
else
{ // Multi-line value
const char *endtag = GenerateEndTag(entry->Value);
fprintf (file, "%s=<<<%s\n%s\n>>>%s\n", entry->Key,
endtag, entry->Value, endtag);
}
entry = entry->Next; entry = entry->Next;
} }
section = section->Next; section = section->Next;
@ -742,6 +809,44 @@ bool FConfigFile::WriteConfigFile () const
return true; return true;
} }
//====================================================================
//
// FConfigFile :: GenerateEndTag
//
// Generates a terminator sequence for multi-line values that does
// not appear anywhere in the value.
//
//====================================================================
const char *FConfigFile::GenerateEndTag(const char *value)
{
static const char Base64Table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
static char EndTag[25] = "EOV-";
// Try different 20-character sequences until we find one that
// isn't in the value. We create the sequences by generating two
// 64-bit random numbers and Base64 encoding the first 15 bytes
// from them.
union { QWORD rand_num[2]; BYTE rand_bytes[16]; };
do
{
rand_num[0] = pr_endtag.GenRand64();
rand_num[1] = pr_endtag.GenRand64();
for (int i = 0; i < 5; ++i)
{
DWORD three_bytes = (rand_bytes[i*3] << 16) | (rand_bytes[i*3+1] << 8) | (rand_bytes[i*3+2]);
EndTag[4+i*4 ] = Base64Table[rand_bytes[i*3] >> 2];
EndTag[4+i*4+1] = Base64Table[((rand_bytes[i*3] & 3) << 4) | (rand_bytes[i*3+1] >> 4)];
EndTag[4+i*4+2] = Base64Table[((rand_bytes[i*3+1] & 15) << 2) | (rand_bytes[i*3+2] >> 6)];
EndTag[4+i*4+3] = Base64Table[rand_bytes[i*3+2] & 63];
}
}
while (strstr(value, EndTag) != NULL);
return EndTag;
}
//==================================================================== //====================================================================
// //
// FConfigFile :: WriteCommentHeader // FConfigFile :: WriteCommentHeader

View file

@ -78,6 +78,7 @@ protected:
virtual char *ReadLine (char *string, int n, void *file) const; virtual char *ReadLine (char *string, int n, void *file) const;
bool ReadConfig (void *file); bool ReadConfig (void *file);
static const char *GenerateEndTag(const char *value);
bool OkayToWrite; bool OkayToWrite;
bool FileExisted; bool FileExisted;
@ -110,6 +111,7 @@ private:
FConfigEntry *FindEntry (FConfigSection *section, const char *key) const; FConfigEntry *FindEntry (FConfigSection *section, const char *key) const;
FConfigSection *NewConfigSection (const char *name); FConfigSection *NewConfigSection (const char *name);
FConfigEntry *NewConfigEntry (FConfigSection *section, const char *key, const char *value); FConfigEntry *NewConfigEntry (FConfigSection *section, const char *key, const char *value);
FConfigEntry *ReadMultiLineValue (void *file, FConfigSection *section, const char *key, const char *terminator);
void SetSectionNote (FConfigSection *section, const char *note); void SetSectionNote (FConfigSection *section, const char *note);
public: public: