File aria2-0.16.0-CVE-2010-1512.patch of Package aria2
diff -ru aria2c-0.16.0-orig/src/array_fun.h aria2c-0.16.0/src/array_fun.h
--- aria2c-0.16.0-orig/src/array_fun.h 2008-10-06 10:38:51.000000000 +0200
+++ aria2c-0.16.0/src/array_fun.h 2010-05-26 23:20:56.000000000 +0200
@@ -142,6 +142,29 @@
typedef R result_type;
};
+template<typename T, size_t N>
+class array_wrapper {
+private:
+ T _array[N];
+public:
+ array_wrapper() {}
+
+ operator T*()
+ {
+ return _array;
+ }
+
+ operator const T*() const
+ {
+ return _array;
+ }
+
+ size_t size() const
+ {
+ return N;
+ }
+};
+
template<typename R, typename A>
array_fun<R>
array_negate(A a)
diff -ru aria2c-0.16.0-orig/src/DHTRoutingTableDeserializer.cc aria2c-0.16.0/src/DHTRoutingTableDeserializer.cc
--- aria2c-0.16.0-orig/src/DHTRoutingTableDeserializer.cc 2008-10-06 10:38:50.000000000 +0200
+++ aria2c-0.16.0/src/DHTRoutingTableDeserializer.cc 2010-05-26 23:55:08.000000000 +0200
@@ -41,10 +41,12 @@
#include "a2netcompat.h"
#include "StringFormat.h"
#include "Util.h"
+#include "array_fun.h"
#include <cerrno>
#include <cstring>
#include <istream>
#include <utility>
+#include <cassert>
namespace aria2 {
@@ -57,11 +59,30 @@
return _localNode;
}
+static void readBytes(unsigned char* buf, size_t buflen,
+ std::istream& in, size_t readlen)
+{
+ assert(readlen <= buflen);
+ in.read(reinterpret_cast<char*>(buf), readlen);
+}
+
const std::deque<SharedHandle<DHTNode> >& DHTRoutingTableDeserializer::getNodes() const
{
return _nodes;
}
+#define CHECK_STREAM(in, length) \
+ if(in.gcount() != length) { \
+ throw DlAbortEx \
+ (StringFormat("Failed to load DHT routing table. cause:%s", \
+ "Unexpected EOF").str()); \
+ } \
+ if(!in) { \
+ throw DlAbortEx \
+ (StringFormat("Failed to load DHT routing table. cause:%s", \
+ strerror(errno)).str()); \
+ }
+
void DHTRoutingTableDeserializer::deserialize(std::istream& in)
{
try {
@@ -91,9 +112,12 @@
memset(zero, 0, sizeof(zero));
int version;
- char buf[26];
+ // If you change the code to read more than the size of buf, then
+ // expand the buf size here.
+ array_wrapper<unsigned char, 255> buf;
+
// header
- in.read(buf, 8);
+ readBytes(buf, buf.size(), in, 8);
if(memcmp(header, buf, 8) == 0) {
version = 3;
} else if(memcmp(headerCompat, buf, 8) == 0) {
@@ -105,31 +129,46 @@
}
// time
if(version == 2) {
- in.read(buf, 4);
- _serializedTime.setTimeInSec(ntohl(*reinterpret_cast<uint32_t*>(buf)));
+ uint32_t temp32;
+ // time
+ in.read(reinterpret_cast<char*>(&temp32), sizeof(temp32));
+ CHECK_STREAM(in, sizeof(temp32));
+ _serializedTime.setTimeInSec(ntohl(temp32));
// 4bytes reserved
- in.read(buf, 4);
+ readBytes(buf, buf.size(), in, 4);
+ CHECK_STREAM(in, 4);
+
} else {
- in.read(buf, 8);
- _serializedTime.setTimeInSec(ntoh64(*reinterpret_cast<uint64_t*>(buf)));
+ uint64_t temp64;
+ // time
+ in.read(reinterpret_cast<char*>(&temp64), sizeof(temp64));
+ CHECK_STREAM(in, sizeof(temp64));
+ _serializedTime.setTimeInSec(ntoh64(temp64));
}
// localnode
// 8bytes reserved
- in.read(buf, 8);
+ readBytes(buf, buf.size(), in, 8);
+ CHECK_STREAM(in, 8);
// localnode ID
- in.read(buf, DHT_ID_LENGTH);
- SharedHandle<DHTNode> localNode(new DHTNode(reinterpret_cast<const unsigned char*>(buf)));
+ readBytes(buf, buf.size(), in, DHT_ID_LENGTH);
+ CHECK_STREAM(in, DHT_ID_LENGTH);
+ SharedHandle<DHTNode> localNode(new DHTNode(buf));
// 4bytes reserved
- in.read(buf, 4);
+ readBytes(buf, buf.size(), in, 4);
+ CHECK_STREAM(in, 4);
// number of nodes
- in.read(buf, 4);
- uint32_t numNodes = ntohl(*reinterpret_cast<uint32_t*>(buf));
+ uint32_t temp32;
+ in.read(reinterpret_cast<char*>(&temp32), sizeof(temp32));
+ CHECK_STREAM(in, sizeof(temp32));
+ uint32_t numNodes = ntohl(temp32);
// 4bytes reserved
- in.read(buf, 4);
+ readBytes(buf, buf.size(), in, 4);
+ CHECK_STREAM(in, 4);
// nodes
+ std::deque<SharedHandle<DHTNode> > nodes;
for(size_t i = 0; i < numNodes; ++i) {
// Currently, only IPv4 addresses are supported.
// 1byte compact peer info length
@@ -137,46 +176,59 @@
in >> peerInfoLen;
if(peerInfoLen != 6) {
// skip this entry
- in.read(buf, 42+7+6);
+ readBytes(buf, buf.size(), in, 42+7+6);
+ CHECK_STREAM(in, 42+7+6);
continue;
}
// 7bytes reserved
- in.read(buf, 7);
+ readBytes(buf, buf.size(), in, 7);
+ CHECK_STREAM(in, 7);
// 6bytes compact peer info
- in.read(buf, 6);
+ readBytes(buf, buf.size(), in, 6);
+ CHECK_STREAM(in, 6);
if(memcmp(zero, buf, 6) == 0) {
// skip this entry
- in.read(buf, 42);
+ readBytes(buf, buf.size(), in, 42);
+ CHECK_STREAM(in, 42);
continue;
}
std::pair<std::string, uint16_t> peer =
- PeerMessageUtil::unpackcompact(reinterpret_cast<const unsigned char*>(buf));
+ PeerMessageUtil::unpackcompact(buf);
if(peer.first.empty()) {
// skip this entry
- in.read(buf, 42);
+ readBytes(buf, buf.size(), in, 42);
+ CHECK_STREAM(in, 42);
continue;
}
// 2bytes reserved
- in.read(buf, 2);
+ readBytes(buf, buf.size(), in, 2);
+ CHECK_STREAM(in, 2);
// 16byte reserved
- in.read(buf, 16);
+ readBytes(buf, buf.size(), in, 16);
+ CHECK_STREAM(in, 16);
// localnode ID
- in.read(buf, DHT_ID_LENGTH);
+ readBytes(buf, buf.size(), in, DHT_ID_LENGTH);
+ CHECK_STREAM(in, DHT_ID_LENGTH);
- SharedHandle<DHTNode> node(new DHTNode(reinterpret_cast<const unsigned char*>(buf)));
+ SharedHandle<DHTNode> node(new DHTNode(buf));
node->setIPAddress(peer.first);
node->setPort(peer.second);
// 4bytes reserved
- in.read(buf, 4);
+ readBytes(buf, buf.size(), in, 4);
+ CHECK_STREAM(in, 4);
- _nodes.push_back(node);
+ nodes.push_back(node);
}
_localNode = localNode;
+ _nodes = nodes;
} catch(std::ios::failure const& exception) {
_nodes.clear();
throw DlAbortEx
(StringFormat("Failed to load DHT routing table. cause:%s",
strerror(errno)).str());
+ } catch(RecoverableException& e) {
+ _nodes.clear();
+ throw;
}
}
diff -ru aria2c-0.16.0-orig/src/FilesMetalinkParserState.cc aria2c-0.16.0/src/FilesMetalinkParserState.cc
--- aria2c-0.16.0-orig/src/FilesMetalinkParserState.cc 2008-10-06 10:38:51.000000000 +0200
+++ aria2c-0.16.0/src/FilesMetalinkParserState.cc 2010-05-26 23:20:56.000000000 +0200
@@ -34,6 +34,7 @@
/* copyright --> */
#include "FilesMetalinkParserState.h"
#include "MetalinkParserStateMachine.h"
+#include "Util.h"
namespace aria2 {
@@ -50,6 +51,9 @@
std::map<std::string, std::string>::const_iterator itr =
attrs.find(FilesMetalinkParserState::NAME);
if(itr != attrs.end()) {
+ if((*itr).second.empty() || Util::detectDirTraversal((*itr).second)) {
+ return;
+ }
stm->newEntryTransaction();
stm->setFileNameOfEntry((*itr).second);
}
diff -ru aria2c-0.16.0-orig/src/MetalinkParserController.cc aria2c-0.16.0/src/MetalinkParserController.cc
--- aria2c-0.16.0-orig/src/MetalinkParserController.cc 2008-10-06 10:38:51.000000000 +0200
+++ aria2c-0.16.0/src/MetalinkParserController.cc 2010-05-26 23:20:56.000000000 +0200
@@ -46,6 +46,7 @@
#endif // ENABLE_MESSAGE_DIGEST
#include "Signature.h"
#include <algorithm>
+#include "Util.h"
namespace aria2 {
@@ -78,9 +79,9 @@
return;
}
if(_tEntry->file.isNull()) {
- _tEntry->file.reset(new FileEntry(filename, 0, 0));
+ _tEntry->file.reset(new FileEntry(Util::escapePath(filename), 0, 0));
} else {
- _tEntry->file->setPath(filename);
+ _tEntry->file->setPath(Util::escapePath(filename));
}
}
diff -ru aria2c-0.16.0-orig/src/Util.cc aria2c-0.16.0/src/Util.cc
--- aria2c-0.16.0-orig/src/Util.cc 2008-10-06 10:38:51.000000000 +0200
+++ aria2c-0.16.0/src/Util.cc 2010-05-26 23:20:56.000000000 +0200
@@ -901,4 +901,55 @@
return std::pair<std::string, uint16_t>(host, atoi(service)); // TODO
}
+bool Util::detectDirTraversal(const std::string& s)
+{
+ for(std::string::const_iterator i = s.begin(), eoi = s.end(); i != eoi; ++i) {
+ if(0x00 <= (*i) && (*i) <= 0x1f) {
+ return true;
+ }
+ }
+ return s == A2STR::DOT_C ||
+ s == ".." ||
+ Util::startsWith(s, A2STR::SLASH_C) ||
+ Util::startsWith(s, "./") ||
+ Util::startsWith(s, "../") ||
+ s.find("/../") != std::string::npos ||
+ s.find("/./") != std::string::npos ||
+ Util::endsWith(s, "/") ||
+ Util::endsWith(s, "/.") ||
+ Util::endsWith(s, "/..");
+}
+
+namespace {
+class EscapePath {
+private:
+ char _repChar;
+public:
+ EscapePath(const char& repChar):_repChar(repChar) {}
+
+ char operator()(const char& c) {
+ if(0x00 <= c && c <=0x1f) {
+ return _repChar;
+ }
+#ifdef __MINGW32__
+ // We don't escape '/' because we use it as a path separator.
+ static const char WIN_INVALID_PATH_CHARS[] =
+ { '"', '*', ':', '<', '>', '?', '\\', '|' };
+ if(std::find(vbegin(WIN_INVALID_PATH_CHARS), vend(WIN_INVALID_PATH_CHARS),
+ c) != vend(WIN_INVALID_PATH_CHARS)) {
+ return _repChar;
+ }
+#endif // __MINGW32__
+ return c;
+ }
+};
+}
+
+std::string Util::escapePath(const std::string& s)
+{
+ std::string d = s;
+ std::transform(d.begin(), d.end(), d.begin(), EscapePath('_'));
+ return d;
+}
+
} // namespace aria2
diff -ru aria2c-0.16.0-orig/src/Util.h aria2c-0.16.0/src/Util.h
--- aria2c-0.16.0-orig/src/Util.h 2008-10-06 10:38:50.000000000 +0200
+++ aria2c-0.16.0/src/Util.h 2010-05-26 23:20:56.000000000 +0200
@@ -267,6 +267,16 @@
static std::pair<std::string, uint16_t>
getNumericNameInfo(const struct sockaddr* sockaddr, socklen_t len);
+
+ // Returns true if s contains directory traversal path component such
+ // as '..' or it contains null or control character which may fool
+ // user.
+ static bool detectDirTraversal(const std::string& s);
+
+ // Replaces null(0x00) and control character(0x01-0x1f) with '_'. If
+ // __MINGW32__ is defined, following characters are also replaced with
+ // '_': '"', '*', ':', '<', '>', '?', '\', '|'.
+ static std::string escapePath(const std::string& s);
};
} // namespace aria2