Try to prevent (as much as possible) transfering and, more important, storing, any other files than avatars as png, gif and jpg. The headers of the files are checked whether they are valid for the corresponding format. Files without gif/png/jpg header are never stored on the server, and thus cannot be requested by any client.

This commit is contained in:
lotodore
2007-10-18 23:00:55 +00:00
parent 8cd93ced8c
commit 6a297effc1
3 changed files with 70 additions and 11 deletions
+3
View File
@@ -29,6 +29,7 @@
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#define MIN_AVATAR_FILE_SIZE 32
#define MAX_AVATAR_FILE_SIZE 30720 #define MAX_AVATAR_FILE_SIZE 30720
struct AvatarFileState; struct AvatarFileState;
@@ -52,6 +53,8 @@ public:
bool HasAvatar(const MD5Buf &md5buf) const; bool HasAvatar(const MD5Buf &md5buf) const;
bool StoreAvatarInCache(const MD5Buf &md5buf, AvatarFileType avatarFileType, const unsigned char *data, unsigned size); bool StoreAvatarInCache(const MD5Buf &md5buf, AvatarFileType avatarFileType, const unsigned char *data, unsigned size);
static bool IsValidAvatarFileType(AvatarFileType avatarFileType, const unsigned char *fileHeader, unsigned fileHeaderSize);
protected: protected:
typedef std::map<MD5Buf, std::string> AvatarMap; typedef std::map<MD5Buf, std::string> AvatarMap;
+66 -10
View File
@@ -27,6 +27,15 @@
#include <fstream> #include <fstream>
#include <cstring> #include <cstring>
#define PNG_HEADER "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"
#define PNG_HEADER_SIZE (sizeof(PNG_HEADER) - 1)
#define JPG_HEADER "\xff\xd8"
#define JPG_HEADER_SIZE (sizeof(JPG_HEADER) - 1)
#define GIF_HEADER_1 "GIF87a"
#define GIF_HEADER_2 "GIF89a"
#define GIF_HEADER_SIZE (sizeof(GIF_HEADER_1) - 1)
#define MAX_HEADER_SIZE PNG_HEADER_SIZE
// Not using boost::algorithm here because of STL issues. // Not using boost::algorithm here because of STL issues.
#ifdef _MSC_VER #ifdef _MSC_VER
#define STRCASECMP _stricmp #define STRCASECMP _stricmp
@@ -103,8 +112,16 @@ AvatarManager::OpenAvatarFileForChunkRead(const std::string &fileName, unsigned
fileState->inputStream.seekg(0, ios_base::beg); fileState->inputStream.seekg(0, ios_base::beg);
std::streamoff posDiff(endPos - startPos); std::streamoff posDiff(endPos - startPos);
outFileSize = (unsigned)posDiff; outFileSize = (unsigned)posDiff;
if (outFileSize <= MAX_AVATAR_FILE_SIZE) if (outFileSize >= MIN_AVATAR_FILE_SIZE && outFileSize <= MAX_AVATAR_FILE_SIZE)
retVal = fileState; {
// Validate type of file by verifying image header.
unsigned char fileHeader[MAX_HEADER_SIZE];
fileState->inputStream.read((char *)fileHeader, sizeof(fileHeader));
fileState->inputStream.seekg(0, ios_base::beg);
if (IsValidAvatarFileType(outFileType, fileHeader, sizeof(fileHeader)))
retVal = fileState;
}
} }
} catch (...) } catch (...)
{ {
@@ -288,16 +305,20 @@ AvatarManager::StoreAvatarInCache(const MD5Buf &md5buf, AvatarFileType avatarFil
} }
if (!ext.empty()) if (!ext.empty())
{ {
path tmpPath(m_cacheDir); // Check header before storing file.
tmpPath /= (md5buf.ToString() + ext); if (IsValidAvatarFileType(avatarFileType, data, size))
string fileName(tmpPath.file_string());
ofstream o(fileName.c_str(), ios_base::out | ios_base::binary);
o.write((const char *)data, size);
{ {
boost::mutex::scoped_lock lock(m_cachedAvatarsMutex); path tmpPath(m_cacheDir);
m_cachedAvatars.insert(AvatarMap::value_type(md5buf, fileName)); tmpPath /= (md5buf.ToString() + ext);
string fileName(tmpPath.file_string());
ofstream o(fileName.c_str(), ios_base::out | ios_base::binary);
o.write((const char *)data, size);
{
boost::mutex::scoped_lock lock(m_cachedAvatarsMutex);
m_cachedAvatars.insert(AvatarMap::value_type(md5buf, fileName));
}
retVal = true;
} }
retVal = true;
} }
} catch (...) } catch (...)
{ {
@@ -305,6 +326,41 @@ AvatarManager::StoreAvatarInCache(const MD5Buf &md5buf, AvatarFileType avatarFil
return retVal; return retVal;
} }
bool
AvatarManager::IsValidAvatarFileType(AvatarFileType avatarFileType, const unsigned char *fileHeader, unsigned fileHeaderSize)
{
bool validType = false;
switch (avatarFileType)
{
case AVATAR_FILE_TYPE_PNG:
if (fileHeaderSize >= PNG_HEADER_SIZE
&& memcmp(fileHeader, PNG_HEADER, PNG_HEADER_SIZE) == 0)
{
validType = true;
}
break;
case AVATAR_FILE_TYPE_JPG:
if (fileHeaderSize >= JPG_HEADER_SIZE
&& memcmp(fileHeader, JPG_HEADER, JPG_HEADER_SIZE) == 0)
{
validType = true;
}
break;
case AVATAR_FILE_TYPE_GIF:
if (fileHeaderSize >= GIF_HEADER_SIZE
&& (memcmp(fileHeader, GIF_HEADER_1, GIF_HEADER_SIZE) == 0
|| memcmp(fileHeader, GIF_HEADER_2, GIF_HEADER_SIZE) == 0))
{
validType = true;
}
break;
case AVATAR_FILE_TYPE_UNKNOWN:
break;
}
return validType;
}
bool bool
AvatarManager::InternalReadDirectory(const std::string &dir, AvatarMap &avatars) AvatarManager::InternalReadDirectory(const std::string &dir, AvatarMap &avatars)
{ {
+1 -1
View File
@@ -417,7 +417,7 @@ ServerLobbyThread::HandleNetPacketAvatarHeader(SessionWrapper session, const Net
NetPacketAvatarHeader::Data headerData; NetPacketAvatarHeader::Data headerData;
tmpPacket.GetData(headerData); tmpPacket.GetData(headerData);
if (headerData.avatarFileSize && headerData.avatarFileSize <= MAX_AVATAR_FILE_SIZE) if (headerData.avatarFileSize >= MIN_AVATAR_FILE_SIZE && headerData.avatarFileSize <= MAX_AVATAR_FILE_SIZE)
{ {
boost::shared_ptr<AvatarData> tmpAvatarData(new AvatarData); boost::shared_ptr<AvatarData> tmpAvatarData(new AvatarData);
tmpAvatarData->fileData.reserve(headerData.avatarFileSize); tmpAvatarData->fileData.reserve(headerData.avatarFileSize);