Browse Source

be more robust: convert all header-keys to lowercase. don't throw on invalid header lines but skip them instead.

afl-fuzzing
Roker 3 years ago
parent
commit
f4cca84a08
8 changed files with 47 additions and 41 deletions
  1. +4
    -4
      src/Makefile
  2. +4
    -15
      src/bodyparser.cc
  3. +11
    -11
      src/headerparser.cc
  4. +4
    -2
      src/pEpMIME.cc
  5. +12
    -0
      src/pEpMIME_internal.cc
  6. +5
    -1
      src/pEpMIME_internal.hh
  7. +6
    -7
      src/parse_address.cc
  8. +1
    -1
      src/to_utf8.cc

+ 4
- 4
src/Makefile View File

@ -3,7 +3,7 @@
#CXX=c++ -Wall -O0 -std=c++14 -g -glldb -fstack-protector-all
# for AFL fuzzingtests:
CXX=afl-clang++ -Wall -O0 -std=c++14 -g -glldb \
CXX=afl-clang++ -Wall -O1 -std=c++14 -g -glldb \
-fstack-protector-all -fsanitize=address \
-fno-omit-frame-pointer -fno-optimize-sibling-calls
@ -26,10 +26,10 @@ unittests: unittest_mime.o unittest_nfc.o unittest_timestamp.o \
${CXX} -L${HOME}/local/lib/ -o $@ $^ -lpEpAdapter -lpEpEngine -lpthread -liconv
gtest-all.o: $(GTEST_DIR)/src/gtest-all.cc
$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -isystem $(GTEST_DIR)/include -o $@ -c $<
${CXX} $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -isystem $(GTEST_DIR)/include -o $@ -c $<
gtest_main.o: $(GTEST_DIR)/src/gtest_main.cc
$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -isystem $(GTEST_DIR)/include -o $@ -c $<
${CXX} $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -isystem $(GTEST_DIR)/include -o $@ -c $<
unittest_%.o : unittest_%.cc
${CXX} -I${HOME}/local/include/ -I/usr/local/include -isystem $(GTEST_DIR)/include -o $@ -c $<
@ -46,4 +46,4 @@ fuzz.o: fuzz.cc
clean:
rm -vf *.o \
*.a \
unittests
unittests fuzz

+ 4
- 15
src/bodyparser.cc View File

@ -72,21 +72,10 @@ BOOST_FUSION_ADAPT_STRUCT(
)
void ascii_tolower(std::string& s)
{
for(char& c : s)
{
if(c>='A' && c<='Z')
{
c += 32;
}
}
}
void ContentType::tolower()
{
ascii_tolower(type);
ascii_tolower(subtype);
pEpMIME::ascii_tolower(type);
pEpMIME::ascii_tolower(subtype);
}
qi::uint_parser<unsigned char, 16,2,2> hex_octet;
@ -275,10 +264,10 @@ void add_attachment(message* msg, const BodyLines& body, const ContentType& ct,
// parses the header and fill the parts in msg
void parse_body(message* msg, const HeaderSection& headers, const BodyLines& body)
{
if( header_value(headers, "MIME-Version") == "1.0" ) // TODO: According to RFC 2048 there can be comments in the header field value. -.-
if( header_value(headers, "mime-version") == "1.0" ) // TODO: According to RFC 2048 there can be comments in the header field value. -.-
{
// TODO: for whatever reason "string_view cts" does not work with qi::parse(). WTF!
const std::string cts = header_value(headers, "Content-Type").to_string();
const std::string cts = header_value(headers, "content-type").to_string();
ContentType ct;
auto begin = cts.cbegin();


+ 11
- 11
src/headerparser.cc View File

@ -128,19 +128,19 @@ char* copy_string(const std::string& s)
const std::map<std::string, struct ParserBase*> parsers =
{
{"Date", P(&message::recv, &parse_timestamp) },
{"Message-ID", P(&message::id, message_id, &copy_string) },
{"Subject", P(&message::shortmsg, phrase, &copy_string) },
{"From", P(&message::from, &parse_address) },
{"To", P(&message::to, &parse_address_list) },
{"Cc", P(&message::cc, &parse_address_list) },
{"Bcc", P(&message::bcc, &parse_address_list) },
{"Reply-To", P(&message::reply_to, &parse_address_list) },
{"date", P(&message::recv, &parse_timestamp) },
{"message-ID", P(&message::id, message_id, &copy_string) },
{"subject", P(&message::shortmsg, phrase, &copy_string) },
{"from", P(&message::from, &parse_address) },
{"to", P(&message::to, &parse_address_list) },
{"cc", P(&message::cc, &parse_address_list) },
{"bcc", P(&message::bcc, &parse_address_list) },
{"reply-to", P(&message::reply_to, &parse_address_list) },
{"In-Reply-To", P<DS, stringlist_t*>(&message::in_reply_to, message_id_list, &create_stringlist) },
{"References" , P<std::vector<std::string>, stringlist_t*>(&message::references, message_id_list, &create_stringlist) },
{"in-reply-to", P<DS, stringlist_t*>(&message::in_reply_to, message_id_list, &create_stringlist) },
{"references" , P<std::vector<std::string>, stringlist_t*>(&message::references, message_id_list, &create_stringlist) },
{"Received", new Discard },
{"received", new Discard },
};
// parses the header and fill the parts in msg


+ 4
- 2
src/pEpMIME.cc View File

@ -134,7 +134,9 @@ message* parse_message2(const char* begin, const char* const end)
LOG << "Line #" << i << ": \"" << line << "\". type=" << lineType << ". \n";
switch(lineType)
{
case -2: throw std::runtime_error("Cannot handle line \"" + std::string(line) + "\""); return nullptr; // TODO: C'mon, don't give up, yet! :-)
case -2: // line is bogus. But be robust: just skip that line and hope for the best...
//throw std::runtime_error("Cannot handle line \"" + std::string(line) + "\""); return nullptr; // TODO: C'mon, don't give up, yet! :-)
continue;
case -1: nv.value += std::string(line);
break;
default:
@ -146,6 +148,7 @@ message* parse_message2(const char* begin, const char* const end)
const int firstBody = lineType + 1; // 1st index _after_ the ':'
const char firstBodyChar = line.at(firstBody); // if SPACE or TAB char: chop this char, too. but only the 1st!
nv.name = std::string( line.substr(0, lineType) );
ascii_tolower(nv.name);
nv.value = std::string( line.substr( firstBody + ( (firstBodyChar==' '|| firstBodyChar=='\t') ? 1 : 0) ));
}
}
@ -201,4 +204,3 @@ char* generate_message(const message* msg, bool omit_fields)
} // end of namespace pEpMIME

+ 12
- 0
src/pEpMIME_internal.cc View File

@ -26,6 +26,18 @@ namespace pEpMIME
{
using namespace std;
void ascii_tolower(std::string& s)
{
for(char& c : s)
{
if(c>='A' && c<='Z')
{
c += 32;
}
}
}
// return empty string if there is no header field with that name in the HeaderSection
sv header_value(const HeaderSection& hs, sv name)
{


+ 5
- 1
src/pEpMIME_internal.hh View File

@ -26,13 +26,17 @@
namespace pEpMIME
{
void ascii_tolower(std::string& s);
struct NameValue
{
NameValue() = default;
NameValue(const std::string& n, const std::string& v)
: name(n), value(v)
{}
{
ascii_tolower(name);
}
std::string name, value;
};


+ 6
- 7
src/parse_address.cc View File

@ -1,4 +1,5 @@
#include "parse_address.hh"
#include "pEpMIME_internal.hh"
#include "nfc.hh"
#include "rules.hh"
#include <boost/spirit/include/qi.hpp>
@ -37,7 +38,7 @@ const Rule addr_spec = local_part >> qi::char_('@') >> domain;
const Rule angle_addr = qi::omit[-fws] >> '<' >> addr_spec >> '>' >> qi::omit[-fws];
const TRule<Identity> name_addr = -phrase >> angle_addr;
const TRule<Identity> name_addr = -(phrase|+(qi::char_-qi::char_(" <,"))) >> angle_addr;
const TRule<Identity> mailbox = qi::hold[ name_addr ] | (qi::attr("") >> addr_spec);
const TRule<Identity> identity_parser = qi::omit[-fws] >> mailbox >> qi::omit[-fws] /* | group */; // Group? ewww... unsupported.
@ -64,9 +65,8 @@ pEp_identity* parse_address(const std::string& s)
if(!okay)
{
std::stringstream ss;
ss << "Cannot parse “" << s << "” as a valid address. Error at position " << (begin - s.begin()) << ": \"" << *begin << "\" ";
throw std::runtime_error( ss.str() );
LOG << "Cannot parse “" << s << "” as a valid address. Error at position " << (begin - s.begin()) << ": \"" << *begin << "\" ";
return nullptr;
}
// LOG << " ===\n";
@ -83,9 +83,8 @@ identity_list* parse_address_list(const std::string& s)
if(!okay)
{
std::stringstream ss;
ss << "Cannot parse “" << s << "” as a valid address list. Error at position " << (begin - s.begin()) << ": \"" << *begin << "\" ";
throw std::runtime_error( ss.str() );
LOG << "Cannot parse “" << s << "” as a valid address list. Error at position " << (begin - s.begin()) << ": \"" << *begin << "\" ";
return nullptr;
}
// LOG << "ADDR_LIST: I found " << vi.size() << " elements in address list “" << s << "” \n";


+ 1
- 1
src/to_utf8.cc View File

@ -30,7 +30,7 @@ namespace
return x;
const Enc* f = std::lower_bound(b, e, x);
return (f == e || f->from != x) ? x : f->to;
return (f == e || f->from != x) ? x : f->to;
}
const uint32_t min_element;


Loading…
Cancel
Save