forked from owasp-modsecurity/ModSecurity
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmultipart.h
More file actions
263 lines (207 loc) · 6.95 KB
/
multipart.h
File metadata and controls
263 lines (207 loc) · 6.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/*
* ModSecurity, http://www.modsecurity.org/
* Copyright (c) 2015 - 2021 Trustwave Holdings, Inc. (http://www.trustwave.com/)
*
* You may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* If any of the files related to licensing are missing or if you have any
* other questions related to licensing please contact Trustwave Holdings, Inc.
* directly using the email address security@modsecurity.org.
*
*/
#include <cstddef>
#include <memory>
#include <string>
#include <iostream>
#include <list>
#include <unordered_map>
#include <utility>
#include <vector>
#ifndef SRC_REQUEST_BODY_PROCESSOR_MULTIPART_H_
#define SRC_REQUEST_BODY_PROCESSOR_MULTIPART_H_
#include "modsecurity/transaction.h"
namespace modsecurity {
namespace RequestBodyProcessor {
#define MULTIPART_BUF_SIZE 4096
#define MULTIPART_FORMDATA 1
#define MULTIPART_FILE 2
struct MyHash {
size_t operator()(const std::string& Keyval) const {
size_t h = 0;
std::for_each(Keyval.begin(), Keyval.end(), [&](char c) {
h += tolower(c);
});
return h;
}
};
struct MyEqual {
bool operator()(const std::string& Left, const std::string& Right) const {
return Left.size() == Right.size()
&& std::equal(Left.begin(), Left.end(), Right.begin(),
[](char a, char b) {
return tolower(a) == tolower(b);
});
}
};
class MultipartPartTmpFile {
public:
explicit MultipartPartTmpFile(Transaction *transaction)
: m_transaction(transaction),
m_tmp_file_fd(0),
m_delete(false)
{ }
~MultipartPartTmpFile();
// forbid copying
MultipartPartTmpFile(const MultipartPartTmpFile&) = delete;
MultipartPartTmpFile& operator=(const MultipartPartTmpFile&) = delete;
int getFd() const {return m_tmp_file_fd;}
void setFd(int fd) {m_tmp_file_fd = fd;}
const std::string& getFilename() const {return m_tmp_file_name;}
void setDelete() {m_delete = true;}
bool isValid() const {return ((m_tmp_file_fd != 0) && (!m_tmp_file_name.empty()));}
void Open();
void Close();
private:
Transaction *m_transaction;
int m_tmp_file_fd;
std::string m_tmp_file_name;
bool m_delete; // whether to delete when transaction is done
};
class MultipartPart {
public:
MultipartPart()
: m_type(MULTIPART_FORMDATA),
m_name(""),
m_nameOffset(0),
m_value(""),
m_valueOffset(0),
m_value_parts(),
m_tmp_file_size(),
m_filename(""),
m_filenameOffset(0),
m_last_header_name(""),
m_headers(),
m_offset(0),
m_length(0) {
m_tmp_file_size.first = 0;
m_tmp_file_size.second = 0;
}
~MultipartPart() {
m_headers.clear();
m_value_parts.clear();
}
/* part type, can be MULTIPART_FORMDATA or MULTIPART_FILE */
int m_type;
/* the name */
std::string m_name;
size_t m_nameOffset;
/* variables only, variable value */
std::string m_value;
size_t m_valueOffset;
std::list<std::pair<std::string, int>> m_value_parts;
/* files only, the content type (where available) */
/* std::string m_content_type; */
/* files only, the name of the temporary file holding data */
std::shared_ptr<RequestBodyProcessor::MultipartPartTmpFile> m_tmp_file;
std::pair<size_t, size_t> m_tmp_file_size;
/* files only, filename as supplied by the browser */
std::string m_filename;
size_t m_filenameOffset;
std::string m_last_header_name;
std::unordered_map<std::string, std::pair<size_t, std::string>,
MyHash, MyEqual> m_headers;
std::string m_last_header_line;
std::vector<std::pair<size_t, std::string>> m_header_lines;
unsigned int m_offset;
unsigned int m_length;
};
class Multipart {
public:
Multipart(const std::string &header, Transaction *transaction);
Multipart(const Multipart&) = delete;
Multipart& operator=(const Multipart&) = delete;
~Multipart();
bool init(std::string *err);
static int boundary_characters_valid(const char *boundary);
static int count_boundary_params(const std::string& str_header_value);
static int is_token_char(unsigned char c);
int multipart_complete(std::string *err);
int parse_content_disposition(const char *c_d_value, int offset);
bool process(const std::string& data, std::string *err, int offset);
int process_boundary(int last_part);
int process_part_header(std::string *error, int offset);
int process_part_data(std::string *error, size_t offset);
void validate_quotes(const char *data, char quote);
size_t m_reqbody_no_files_length;
std::list<MultipartPart *> m_parts;
/* Number of parts that are files */
int m_nfiles;
/* mime boundary used to detect when
* parts end and begin
*/
std::string m_boundary;
int m_boundary_count;
/* internal buffer and other variables
* used while parsing
*/
char m_buf[MULTIPART_BUF_SIZE + 2];
int m_buf_contains_line;
char *m_bufptr;
int m_bufleft;
unsigned int m_buf_offset;
/* line ending status seen immediately before current position.
* 0 = neither LF nor CR; 1 = prev char CR; 2 = prev char LF alone;
* 3 = prev two chars were CRLF
*/
int m_crlf_state;
/* crlf_state at end of previous buffer */
int m_crlf_state_buf_end;
/* pointer that keeps track of a part while
* it is being built
*/
MultipartPart *m_mpp;
/* part parsing state; 0 means we are reading
* headers, 1 means we are collecting data
*/
int m_mpp_state;
/* part parsing substate; if mpp_state is 1 (collecting
* data), then for this variable:
* 0 means we have not yet read any data between the
* post-headers blank line and the next boundary
* 1 means we have read at some data after that blank line
*/
int m_mpp_substate_part_data_read;
/* because of the way this parsing algorithm
* works we hold back the last two bytes of
* each data chunk so that we can discard it
* later if the next data chunk proves to be
* a boundary; the first byte is an indicator
* 0 - no content, 1 - two data bytes available
*/
char m_reserve[4];
int m_seen_data;
int m_is_complete;
int m_flag_error;
int m_flag_data_before;
int m_flag_data_after;
int m_flag_header_folding;
int m_flag_boundary_quoted;
int m_flag_lf_line;
int m_flag_crlf_line;
int m_flag_unmatched_boundary;
int m_flag_boundary_whitespace;
int m_flag_missing_semicolon;
int m_flag_invalid_quoting;
int m_flag_invalid_part;
int m_flag_invalid_header_folding;
int m_flag_file_limit_exceeded;
private:
std::string m_header;
Transaction *m_transaction;
};
} // namespace RequestBodyProcessor
} // namespace modsecurity
#endif // SRC_REQUEST_BODY_PROCESSOR_MULTIPART_H_