Skip to content

Commit 299e351

Browse files
authored
Merge pull request #95 from CESNET/packet_parsing_overflow
Packet parsing buffer overflow
2 parents 98f1ca0 + eaf6fb3 commit 299e351

File tree

7 files changed

+198
-33
lines changed

7 files changed

+198
-33
lines changed

Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ ipfixprobe_process_src=\
121121
process/stats.cpp \
122122
process/stats.hpp \
123123
process/md5.hpp \
124-
process/md5.cpp
124+
process/md5.cpp \
125+
process/common.hpp
125126

126127
if WITH_QUIC
127128
ipfixprobe_process_src+=\

process/common.hpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* \file common.hpp
3+
* \brief Common function for processing modules
4+
* \author Pavel Siska <[email protected]>
5+
* \date 2022
6+
*/
7+
/*
8+
* Copyright (C) 2022 CESNET
9+
*
10+
* LICENSE TERMS
11+
*
12+
* Redistribution and use in source and binary forms, with or without
13+
* modification, are permitted provided that the following conditions
14+
* are met:
15+
* 1. Redistributions of source code must retain the above copyright
16+
* notice, this list of conditions and the following disclaimer.
17+
* 2. Redistributions in binary form must reproduce the above copyright
18+
* notice, this list of conditions and the following disclaimer in
19+
* the documentation and/or other materials provided with the
20+
* distribution.
21+
* 3. Neither the name of the Company nor the names of its contributors
22+
* may be used to endorse or promote products derived from this
23+
* software without specific prior written permission.
24+
*
25+
* ALTERNATIVELY, provided that this notice is retained in full, this
26+
* product may be distributed under the terms of the GNU General Public
27+
* License (GPL) version 2 or later, in which case the provisions
28+
* of the GPL apply INSTEAD OF those given above.
29+
*
30+
* This software is provided ``as is'', and any express or implied
31+
* warranties, including, but not limited to, the implied warranties of
32+
* merchantability and fitness for a particular purpose are disclaimed.
33+
* In no event shall the company or contributors be liable for any
34+
* direct, indirect, incidental, special, exemplary, or consequential
35+
* damages (including, but not limited to, procurement of substitute
36+
* goods or services; loss of use, data, or profits; or business
37+
* interruption) however caused and on any theory of liability, whether
38+
* in contract, strict liability, or tort (including negligence or
39+
* otherwise) arising in any way out of the use of this software, even
40+
* if advised of the possibility of such damage.
41+
*
42+
*/
43+
44+
#ifndef IPXP_PROCESS_COMMON_HPP
45+
#define IPXP_PROCESS_COMMON_HPP
46+
47+
#include <cstring>
48+
49+
namespace ipxp {
50+
51+
static inline bool check_payload_len(size_t payload_len, size_t required_len) noexcept
52+
{
53+
return payload_len < required_len;
54+
}
55+
56+
/**
57+
* \brief Returns a pointer to the first occurrence of str2 in str1,
58+
* or a null pointer if str2 is not part of str1.
59+
*
60+
* \param str1 C string to be scanned.
61+
* \param str2 C string containing the sequence of characters to match.
62+
* \param len Number of bytes to be analyzed.
63+
*
64+
* \return A pointer to the first occurrence of string in str1.
65+
* If the string is not found, the function returns a null pointer.
66+
*/
67+
static inline const char *
68+
strnstr(const char *str1, const char *str2, size_t len) noexcept
69+
{
70+
char c, sc;
71+
size_t slen;
72+
73+
if ((c = *str2++) != '\0') {
74+
slen = strlen(str2);
75+
do {
76+
do {
77+
if (len-- < 1 || (sc = *str1++) == '\0')
78+
return (NULL);
79+
} while (sc != c);
80+
if (slen > len)
81+
return (NULL);
82+
} while (strncmp(str1, str2, slen) != 0);
83+
str1--;
84+
}
85+
return ((char *)str1);
86+
}
87+
88+
}
89+
90+
#endif /* IPXP_PROCESS_COMMON_HPP */

process/http.cpp

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include <unirec/unirec.h>
5151
#endif
5252

53+
#include "common.hpp"
5354
#include "http.hpp"
5455

5556
namespace ipxp {
@@ -224,6 +225,7 @@ static uint32_t s_requests = 0, s_responses = 0;
224225
bool HTTPPlugin::parse_http_request(const char *data, int payload_len, RecordExtHTTP *rec)
225226
{
226227
char buffer[64];
228+
size_t remaining;
227229
const char *begin, *end, *keyval_delimiter;
228230

229231
total++;
@@ -247,14 +249,21 @@ bool HTTPPlugin::parse_http_request(const char *data, int payload_len, RecordExt
247249
*/
248250

249251
/* Find begin of URI. */
250-
begin = strchr(data, ' ');
252+
begin = static_cast<const char *>(memchr(data, ' ', payload_len));
251253
if (begin == nullptr) {
252254
DEBUG_MSG("Parser quits:\tnot a http request header\n");
253255
return false;
254256
}
255257

256258
/* Find end of URI. */
257-
end = strchr(begin + 1, ' ');
259+
if (check_payload_len(payload_len, (begin + 1) - data)) {
260+
DEBUG_MSG("Parser quits:\tpayload end\n");
261+
return false;
262+
}
263+
264+
remaining = payload_len - ((begin + 1) - data);
265+
end = static_cast<const char *>(memchr(begin + 1, ' ', remaining));
266+
258267
if (end == nullptr) {
259268
DEBUG_MSG("Parser quits:\trequest is fragmented\n");
260269
return false;
@@ -281,7 +290,12 @@ bool HTTPPlugin::parse_http_request(const char *data, int payload_len, RecordExt
281290
DEBUG_MSG("\tURI: %s\n", rec->uri);
282291

283292
/* Find begin of next line after request line. */
284-
begin = strstr(end, HTTP_LINE_DELIMITER);
293+
if (check_payload_len(payload_len, end - data)) {
294+
DEBUG_MSG("Parser quits:\tpayload end\n");
295+
return false;
296+
}
297+
remaining = payload_len - (end - data);
298+
begin = ipxp::strnstr(end, HTTP_LINE_DELIMITER, remaining);
285299
if (begin == nullptr) {
286300
DEBUG_MSG("Parser quits:\tNo line delim after request line\n");
287301
return false;
@@ -302,8 +316,10 @@ bool HTTPPlugin::parse_http_request(const char *data, int payload_len, RecordExt
302316
rec->referer[0] = 0;
303317
/* Process headers. */
304318
while (begin - data < payload_len) {
305-
end = strstr(begin, HTTP_LINE_DELIMITER);
306-
keyval_delimiter = strchr(begin, HTTP_KEYVAL_DELIMITER);
319+
320+
remaining = payload_len - (begin - data);
321+
end = ipxp::strnstr(begin, HTTP_LINE_DELIMITER, remaining);
322+
keyval_delimiter = static_cast<const char *>(memchr(begin, HTTP_KEYVAL_DELIMITER, remaining));
307323

308324
if (end == nullptr) {
309325
DEBUG_MSG("Parser quits:\theader is fragmented\n");
@@ -356,6 +372,7 @@ bool HTTPPlugin::parse_http_response(const char *data, int payload_len, RecordEx
356372
{
357373
char buffer[64];
358374
const char *begin, *end, *keyval_delimiter;
375+
size_t remaining;
359376
int code;
360377

361378
total++;
@@ -385,14 +402,19 @@ bool HTTPPlugin::parse_http_response(const char *data, int payload_len, RecordEx
385402
*/
386403

387404
/* Find begin of status code. */
388-
begin = strchr(data, ' ');
405+
begin = static_cast<const char *>(memchr(data, ' ', payload_len));
389406
if (begin == nullptr) {
390407
DEBUG_MSG("Parser quits:\tnot a http response header\n");
391408
return false;
392409
}
393410

394411
/* Find end of status code. */
395-
end = strchr(begin + 1, ' ');
412+
if (check_payload_len(payload_len, (begin + 1) - data)) {
413+
DEBUG_MSG("Parser quits:\tpayload end\n");
414+
return false;
415+
}
416+
remaining = payload_len - ((begin + 1) - data);
417+
end = static_cast<const char *>(memchr(begin + 1, ' ', remaining));
396418
if (end == nullptr) {
397419
DEBUG_MSG("Parser quits:\tresponse is fragmented\n");
398420
return false;
@@ -416,7 +438,12 @@ bool HTTPPlugin::parse_http_response(const char *data, int payload_len, RecordEx
416438
rec->code = code;
417439

418440
/* Find begin of next line after request line. */
419-
begin = strstr(end, HTTP_LINE_DELIMITER);
441+
if (check_payload_len(payload_len, end - data)) {
442+
DEBUG_MSG("Parser quits:\tpayload end\n");
443+
return false;
444+
}
445+
remaining = payload_len - (end - data);
446+
begin = ipxp::strnstr(end, HTTP_LINE_DELIMITER, remaining);
420447
if (begin == nullptr) {
421448
DEBUG_MSG("Parser quits:\tNo line delim after request line\n");
422449
return false;
@@ -435,8 +462,9 @@ bool HTTPPlugin::parse_http_response(const char *data, int payload_len, RecordEx
435462
rec->content_type[0] = 0;
436463
/* Process headers. */
437464
while (begin - data < payload_len) {
438-
end = strstr(begin, HTTP_LINE_DELIMITER);
439-
keyval_delimiter = strchr(begin, HTTP_KEYVAL_DELIMITER);
465+
remaining = payload_len - (begin - data);
466+
end = ipxp::strnstr(begin, HTTP_LINE_DELIMITER, remaining);
467+
keyval_delimiter = static_cast<const char *>(memchr(begin, HTTP_KEYVAL_DELIMITER, remaining));
440468

441469
if (end == nullptr) {
442470
DEBUG_MSG("Parser quits:\theader is fragmented\n");

process/rtsp.cpp

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include <unirec/unirec.h>
5050
#endif
5151

52+
#include "common.hpp"
5253
#include "rtsp.hpp"
5354

5455
namespace ipxp {
@@ -204,6 +205,7 @@ bool RTSPPlugin::parse_rtsp_request(const char *data, int payload_len, RecordExt
204205
const char *begin;
205206
const char *end;
206207
const char *keyval_delimiter;
208+
size_t remaining;
207209

208210
total++;
209211

@@ -226,14 +228,20 @@ bool RTSPPlugin::parse_rtsp_request(const char *data, int payload_len, RecordExt
226228
*/
227229

228230
/* Find begin of URI. */
229-
begin = strchr(data, ' ');
231+
begin = static_cast<const char *>(memchr(data, ' ', payload_len));
230232
if (begin == nullptr) {
231233
DEBUG_MSG("Parser quits:\tnot a rtsp request header\n");
232234
return false;
233235
}
234236

235237
/* Find end of URI. */
236-
end = strchr(begin + 1, ' ');
238+
239+
if (check_payload_len(payload_len, (begin + 1) - data)) {
240+
DEBUG_MSG("Parser quits:\tpayload end\n");
241+
return false;
242+
}
243+
remaining = payload_len - ((begin + 1) - data);
244+
end = static_cast<const char *>(memchr(begin + 1, ' ', remaining));
237245
if (end == nullptr) {
238246
DEBUG_MSG("Parser quits:\trequest is fragmented\n");
239247
return false;
@@ -260,7 +268,12 @@ bool RTSPPlugin::parse_rtsp_request(const char *data, int payload_len, RecordExt
260268
DEBUG_MSG("\tURI: %s\n", rec->uri);
261269

262270
/* Find begin of next line after request line. */
263-
begin = strchr(end, RTSP_LINE_DELIMITER);
271+
if (check_payload_len(payload_len, end - data)) {
272+
DEBUG_MSG("Parser quits:\tpayload end\n");
273+
return false;
274+
}
275+
remaining = payload_len - (end - data);
276+
begin = static_cast<const char *>(memchr(end, RTSP_LINE_DELIMITER, remaining));
264277
if (begin == nullptr) {
265278
DEBUG_MSG("Parser quits:\tNo line delim after request line\n");
266279
return false;
@@ -279,8 +292,9 @@ bool RTSPPlugin::parse_rtsp_request(const char *data, int payload_len, RecordExt
279292
rec->user_agent[0] = 0;
280293
/* Process headers. */
281294
while (begin - data < payload_len) {
282-
end = strchr(begin, RTSP_LINE_DELIMITER);
283-
keyval_delimiter = strchr(begin, RTSP_KEYVAL_DELIMITER);
295+
remaining = payload_len - (begin - data);
296+
end = static_cast<const char *>(memchr(begin, RTSP_LINE_DELIMITER, remaining));
297+
keyval_delimiter = static_cast<const char *>(memchr(begin, RTSP_KEYVAL_DELIMITER, remaining));
284298

285299
int tmp = end - begin;
286300
if (tmp == 0 || tmp == 1) { /* Check for blank line with \r\n or \n ending. */
@@ -325,6 +339,7 @@ bool RTSPPlugin::parse_rtsp_response(const char *data, int payload_len, RecordEx
325339
const char *begin;
326340
const char *end;
327341
const char *keyval_delimiter;
342+
size_t remaining;
328343
int code;
329344

330345
total++;
@@ -354,14 +369,19 @@ bool RTSPPlugin::parse_rtsp_response(const char *data, int payload_len, RecordEx
354369
*/
355370

356371
/* Find begin of status code. */
357-
begin = strchr(data, ' ');
372+
begin = static_cast<const char *>(memchr(data, ' ', payload_len));
358373
if (begin == nullptr) {
359374
DEBUG_MSG("Parser quits:\tnot a rtsp response header\n");
360375
return false;
361376
}
362377

363378
/* Find end of status code. */
364-
end = strchr(begin + 1, ' ');
379+
if (check_payload_len(payload_len, (begin + 1) - data)) {
380+
DEBUG_MSG("Parser quits:\tpayload end\n");
381+
return false;
382+
}
383+
remaining = payload_len - ((begin + 1) - data);
384+
end = static_cast<const char *>(memchr(begin + 1, ' ', remaining));
365385
if (end == nullptr) {
366386
DEBUG_MSG("Parser quits:\tresponse is fragmented\n");
367387
return false;
@@ -385,7 +405,12 @@ bool RTSPPlugin::parse_rtsp_response(const char *data, int payload_len, RecordEx
385405
rec->code = code;
386406

387407
/* Find begin of next line after request line. */
388-
begin = strchr(end, RTSP_LINE_DELIMITER);
408+
if (check_payload_len(payload_len, end - data)) {
409+
DEBUG_MSG("Parser quits:\tpayload end\n");
410+
return false;
411+
}
412+
remaining = payload_len - (end - data);
413+
begin = static_cast<const char *>(memchr(end, RTSP_LINE_DELIMITER, remaining));
389414
if (begin == nullptr) {
390415
DEBUG_MSG("Parser quits:\tNo line delim after request line\n");
391416
return false;
@@ -404,8 +429,9 @@ bool RTSPPlugin::parse_rtsp_response(const char *data, int payload_len, RecordEx
404429
rec->content_type[0] = 0;
405430
/* Process headers. */
406431
while (begin - data < payload_len) {
407-
end = strchr(begin, RTSP_LINE_DELIMITER);
408-
keyval_delimiter = strchr(begin, RTSP_KEYVAL_DELIMITER);
432+
remaining = payload_len - (begin - data);
433+
end = static_cast<const char *>(memchr(begin, RTSP_LINE_DELIMITER, remaining));
434+
keyval_delimiter = static_cast<const char *>(memchr(begin, RTSP_KEYVAL_DELIMITER, remaining));
409435

410436
int tmp = end - begin;
411437
if (tmp == 0 || tmp == 1) { /* Check for blank line with \r\n or \n ending. */

0 commit comments

Comments
 (0)