Allow '<' and '>' in MIME multipart boundaries.

The spec doesn't actually seem to permit these, but Seth had a (private)
pcap showing them used in the wild (and the HTTP/MIME analyzer failed to
parse content as a result).
This commit is contained in:
Jon Siwek 2015-05-22 11:46:50 -05:00
parent c870fefbef
commit 08822e0dd4
3 changed files with 42 additions and 38 deletions

View file

@ -1025,8 +1025,11 @@ void HTTP_Analyzer::DeliverStream(int len, const u_char* data, bool is_orig)
} }
else else
{ {
ProtocolViolation("not a http reply line"); if ( line != end_of_line )
reply_state = EXPECT_REPLY_NOTHING; {
ProtocolViolation("not a http reply line");
reply_state = EXPECT_REPLY_NOTHING;
}
} }
break; break;

View file

@ -246,11 +246,16 @@ int MIME_get_field_name(int len, const char* data, data_chunk_t* name)
} }
// See RFC 2045, page 12. // See RFC 2045, page 12.
int MIME_is_tspecial (char ch) int MIME_is_tspecial (char ch, bool is_boundary = false)
{ {
return ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '@' || if ( is_boundary )
ch == ',' || ch == ';' || ch == ':' || ch == '\\' || ch == '"' || return ch == '(' || ch == ')' || ch == '@' ||
ch == '/' || ch == '[' || ch == ']' || ch == '?' || ch == '='; ch == ',' || ch == ';' || ch == ':' || ch == '\\' || ch == '"' ||
ch == '/' || ch == '[' || ch == ']' || ch == '?' || ch == '=';
else
return ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '@' ||
ch == ',' || ch == ';' || ch == ':' || ch == '\\' || ch == '"' ||
ch == '/' || ch == '[' || ch == ']' || ch == '?' || ch == '=';
} }
int MIME_is_field_name_char (char ch) int MIME_is_field_name_char (char ch)
@ -258,26 +263,27 @@ int MIME_is_field_name_char (char ch)
return ch >= 33 && ch <= 126 && ch != ':'; return ch >= 33 && ch <= 126 && ch != ':';
} }
int MIME_is_token_char (char ch) int MIME_is_token_char (char ch, bool is_boundary = false)
{ {
return ch >= 33 && ch <= 126 && ! MIME_is_tspecial(ch); return ch >= 33 && ch <= 126 && ! MIME_is_tspecial(ch, is_boundary);
} }
// See RFC 2045, page 12. // See RFC 2045, page 12.
// A token is composed of characters that are not SPACE, CTLs or tspecials // A token is composed of characters that are not SPACE, CTLs or tspecials
int MIME_get_token(int len, const char* data, data_chunk_t* token) int MIME_get_token(int len, const char* data, data_chunk_t* token,
bool is_boundary)
{ {
int i = MIME_skip_lws_comments(len, data); int i = MIME_skip_lws_comments(len, data);
while ( i < len ) while ( i < len )
{ {
int j; int j;
if ( MIME_is_token_char(data[i]) ) if ( MIME_is_token_char(data[i], is_boundary) )
{ {
token->data = (data + i); token->data = (data + i);
for ( j = i; j < len; ++j ) for ( j = i; j < len; ++j )
{ {
if ( ! MIME_is_token_char(data[j]) ) if ( ! MIME_is_token_char(data[j], is_boundary) )
break; break;
} }
@ -359,7 +365,7 @@ int MIME_get_quoted_string(int len, const char* data, data_chunk_t* str)
return -1; return -1;
} }
int MIME_get_value(int len, const char* data, BroString*& buf) int MIME_get_value(int len, const char* data, BroString*& buf, bool is_boundary)
{ {
int offset = MIME_skip_lws_comments(len, data); int offset = MIME_skip_lws_comments(len, data);
@ -380,7 +386,7 @@ int MIME_get_value(int len, const char* data, BroString*& buf)
else else
{ {
data_chunk_t str; data_chunk_t str;
int end = MIME_get_token(len, data, &str); int end = MIME_get_token(len, data, &str, is_boundary);
if ( end < 0 ) if ( end < 0 )
return -1; return -1;
@ -863,8 +869,22 @@ int MIME_Entity::ParseFieldParameters(int len, const char* data)
len -= offset; len -= offset;
BroString* val = 0; BroString* val = 0;
// token or quoted-string
offset = MIME_get_value(len, data, val); if ( current_field_type == MIME_CONTENT_TYPE &&
content_type == CONTENT_TYPE_MULTIPART &&
strcasecmp_n(attr, "boundary") == 0 )
{
// token or quoted-string (and some lenience for characters
// not explicitly allowed by the RFC, but encountered in the wild)
offset = MIME_get_value(len, data, val, true);
data_chunk_t vd = get_data_chunk(val);
multipart_boundary = new BroString((const u_char*)vd.data,
vd.length, 1);
}
else
// token or quoted-string
offset = MIME_get_value(len, data, val);
if ( offset < 0 ) if ( offset < 0 )
{ {
IllegalFormat("value not found in parameter specification"); IllegalFormat("value not found in parameter specification");
@ -874,8 +894,6 @@ int MIME_Entity::ParseFieldParameters(int len, const char* data)
data += offset; data += offset;
len -= offset; len -= offset;
ParseParameter(attr, get_data_chunk(val));
delete val; delete val;
} }
@ -920,24 +938,6 @@ void MIME_Entity::ParseContentEncoding(data_chunk_t encoding_mechanism)
content_encoding = i; content_encoding = i;
} }
void MIME_Entity::ParseParameter(data_chunk_t attr, data_chunk_t val)
{
switch ( current_field_type ) {
case MIME_CONTENT_TYPE:
if ( content_type == CONTENT_TYPE_MULTIPART &&
strcasecmp_n(attr, "boundary") == 0 )
multipart_boundary = new BroString((const u_char*)val.data, val.length, 1);
break;
case MIME_CONTENT_TRANSFER_ENCODING:
break;
default:
break;
}
}
int MIME_Entity::CheckBoundaryDelimiter(int len, const char* data) int MIME_Entity::CheckBoundaryDelimiter(int len, const char* data)
{ {
if ( ! multipart_boundary ) if ( ! multipart_boundary )

View file

@ -117,7 +117,6 @@ protected:
void ParseContentType(data_chunk_t type, data_chunk_t sub_type); void ParseContentType(data_chunk_t type, data_chunk_t sub_type);
void ParseContentEncoding(data_chunk_t encoding_mechanism); void ParseContentEncoding(data_chunk_t encoding_mechanism);
void ParseParameter(data_chunk_t attr, data_chunk_t val);
void BeginBody(); void BeginBody();
void NewDataLine(int len, const char* data, int trailing_CRLF); void NewDataLine(int len, const char* data, int trailing_CRLF);
@ -275,9 +274,11 @@ extern int MIME_count_leading_lws(int len, const char* data);
extern int MIME_count_trailing_lws(int len, const char* data); extern int MIME_count_trailing_lws(int len, const char* data);
extern int MIME_skip_comments(int len, const char* data); extern int MIME_skip_comments(int len, const char* data);
extern int MIME_skip_lws_comments(int len, const char* data); extern int MIME_skip_lws_comments(int len, const char* data);
extern int MIME_get_token(int len, const char* data, data_chunk_t* token); extern int MIME_get_token(int len, const char* data, data_chunk_t* token,
bool is_boundary = false);
extern int MIME_get_slash_token_pair(int len, const char* data, data_chunk_t* first, data_chunk_t* second); extern int MIME_get_slash_token_pair(int len, const char* data, data_chunk_t* first, data_chunk_t* second);
extern int MIME_get_value(int len, const char* data, BroString*& buf); extern int MIME_get_value(int len, const char* data, BroString*& buf,
bool is_boundary = false);
extern int MIME_get_field_name(int len, const char* data, data_chunk_t* name); extern int MIME_get_field_name(int len, const char* data, data_chunk_t* name);
extern BroString* MIME_decode_quoted_pairs(data_chunk_t buf); extern BroString* MIME_decode_quoted_pairs(data_chunk_t buf);