Klaus Demo bjoern / ceee959
Fix wrong http-parser usage (#133) * Fix wrong http-parser usage * remove noop from _Unicode_Concat * make _set_or_append_header function from macro * remove unused unused attribute from _Unicode_Concat * last_was_data renamed to last_call_was_header_value * _free_and_unset_if_set replaced and comments added Szabi authored 2 years ago Jonas Haag committed 2 years ago
3 changed file(s) with 74 addition(s) and 81 deletion(s). Raw diff Collapse all Expand all
1515 #define _Bytes_Resize(obj, len) _PyBytes_Resize(obj, len)
1616 #define _FromLong(n) PyLong_FromLong(n)
1717 #define _Unicode_EncodeLatin1(u) PyUnicode_AsLatin1String(u)
18 #define _Unicode_Concat(a, b) PyUnicode_Concat(a, b)
1819
1920 #else
2021
2930 #define _Bytes_Resize(obj, len) _PyString_Resize(obj, len)
3031 #define _FromLong(n) PyInt_FromLong(n)
3132 #define _Unicode_EncodeLatin1(u) (Py_INCREF(u),u)
33
34 static PyObject *_Unicode_Concat(PyObject *l, PyObject *r) {
35 PyObject *ret = l;
36
37 Py_INCREF(l); /* reference to old left will be stolen */
38 PyString_Concat(&ret, r);
39
40 return ret;
41 }
3242 #endif
3343
3444 #endif /* _PY2PY3_H */
44 #include "py2py3.h"
55
66 static inline void PyDict_ReplaceKey(PyObject* dict, PyObject* k1, PyObject* k2);
7 static PyObject* wsgi_http_header(string header);
87 static http_parser_settings parser_settings;
98 static PyObject* wsgi_base_dict = NULL;
109
2625 return request;
2726 }
2827
28 /* should not be called without `Request_clean` when `request->parser.field` can contain an object */
2929 void Request_reset(Request* request)
3030 {
3131 memset(&request->state, 0, sizeof(Request) - (size_t)&((Request*)NULL)->state);
3232 request->state.response_length_unknown = true;
33 request->parser.body = (string){NULL, 0};
33 request->parser.last_call_was_header_value = true;
34 request->parser.invalid_header = false;
35 request->parser.field = NULL;
3436 }
3537
3638 void Request_free(Request* request)
4042 free(request);
4143 }
4244
45 /* Request_reset should be called after this, to reset request->parser.field to NULL */
4346 void Request_clean(Request* request)
4447 {
4548 if(request->iterable) {
5861 Py_XDECREF(request->iterator);
5962 Py_XDECREF(request->headers);
6063 Py_XDECREF(request->status);
64 Py_XDECREF(request->parser.field);
6165 }
6266
6367 /* Parse stuff */
7377
7478 #define REQUEST ((Request*)parser->data)
7579 #define PARSER ((bj_parser*)parser)
76 #define UPDATE_LENGTH(name) \
77 /* Update the len of a header field/value.
78 *
79 * Short explaination of the pointer arithmetics fun used here:
80 *
81 * [old header data ] ...stuff... [ new header data ]
82 * ^-------------- A -------------^--------B--------^
83 *
84 * A = XXX- PARSER->XXX.data
85 * B = len
86 * A + B = old header start to new header end
87 */ \
88 do { PARSER->name.len = (name - PARSER->name.data) + len; } while(0)
8980
9081 #define _set_header(k, v) PyDict_SetItem(REQUEST->headers, k, v);
9182 /* PyDict_SetItem() increases the ref-count for value */
9687 Py_DECREF(val); \
9788 } while(0)
9889
99
100 #define _set_header_from_http_header() \
101 do { \
102 PyObject* key = wsgi_http_header(PARSER->field); \
103 if (key) { \
104 _set_header_free_value(key, _Unicode_FromStringAndSize(PARSER->value.data, PARSER->value.len)); \
105 Py_DECREF(key); \
106 } \
107 } while(0) \
90 static void
91 _set_or_append_header(PyObject* headers, PyObject* k, const char* val, size_t len) {
92 PyObject *py_val = _Unicode_FromStringAndSize(val, len);
93 PyObject *py_val_old = PyDict_GetItem(headers, k);
94
95 if (py_val_old) {
96 PyObject *py_val_new = _Unicode_Concat(py_val_old, py_val);
97 PyDict_SetItem(headers, k, py_val_new);
98 Py_DECREF(py_val_new);
99 } else {
100 PyDict_SetItem(headers, k, py_val);
101 }
102 Py_DECREF(py_val);
103 }
108104
109105 static int
110106 on_message_begin(http_parser* parser)
111107 {
112108 REQUEST->headers = PyDict_New();
113 PARSER->field = (string){NULL, 0};
114 PARSER->value = (string){NULL, 0};
115109 return 0;
116110 }
117111
120114 {
121115 if(!(len = unquote_url_inplace((char*)path, len)))
122116 return 1;
123 _set_header_free_value(_PATH_INFO, _Unicode_FromStringAndSize(path, len));
117 _set_or_append_header(REQUEST->headers, _PATH_INFO, path, len);
124118 return 0;
125119 }
126120
127121 static int
128122 on_query_string(http_parser* parser, const char* query, size_t len)
129123 {
130 _set_header_free_value(_QUERY_STRING, _Unicode_FromStringAndSize(query, len));
124 _set_or_append_header(REQUEST->headers, _QUERY_STRING, query, len);
131125 return 0;
132126 }
133127
134128 static int
135129 on_header_field(http_parser* parser, const char* field, size_t len)
136130 {
137 if(PARSER->value.data) {
138 /* Store previous header and start a new one */
139 _set_header_from_http_header();
140 } else if(PARSER->field.data) {
141 UPDATE_LENGTH(field);
131 if(PARSER->last_call_was_header_value) {
132 /* We are starting a new header */
133 Py_Clear(PARSER->field);
134 PARSER->field = _Unicode_FromStringAndSize("HTTP_", 5);
135 PARSER->last_call_was_header_value = false;
136 PARSER->invalid_header = false;
137 }
138
139 /* Ignore invalid header */
140 if(PARSER->invalid_header) {
142141 return 0;
143142 }
144 PARSER->field = (string){(char*)field, len};
145 PARSER->value = (string){NULL, 0};
143
144 char field_processed[len];
145 for(size_t i=0; i<len; i++) {
146 char c = field[i];
147 if(c == '_') {
148 // CVE-2015-0219
149 PARSER->invalid_header = true;
150 return 0;
151 } else if (c == '-') {
152 field_processed[i] = '_';
153 } else if(c >= 'a' && c <= 'z') {
154 field_processed[i] = c - ('a'-'A');
155 } else {
156 field_processed[i] = c;
157 }
158 }
159
160 /* Append field name to the part we got from previous call */
161 PyObject *field_old = PARSER->field;
162 PyObject *field_new = _Unicode_FromStringAndSize(field_processed, len);
163 PARSER->field = _Unicode_Concat(field_old, field_new);
164 Py_DECREF(field_old);
165 Py_DECREF(field_new);
166
146167 return 0;
147168 }
148169
149170 static int
150171 on_header_value(http_parser* parser, const char* value, size_t len)
151172 {
152 if(PARSER->value.data) {
153 UPDATE_LENGTH(value);
154 } else {
155 /* Start a new value */
156 PARSER->value = (string){(char*)value, len};
157 }
158 return 0;
159 }
160
161 static int
162 on_headers_complete(http_parser* parser)
163 {
164 if(PARSER->field.data) {
165 _set_header_from_http_header();
173 PARSER->last_call_was_header_value = true;
174 if(!PARSER->invalid_header) {
175 /* Set header, or append data to header if this is not the first call */
176 _set_or_append_header(REQUEST->headers, PARSER->field, value, len);
166177 }
167178 return 0;
168179 }
231242 return 0;
232243 }
233244
234
235 static PyObject*
236 wsgi_http_header(string header)
237 {
238 const char *http_ = "HTTP_";
239 int size = header.len+strlen(http_);
240 char dest[size];
241 int i = 5;
242
243 memcpy(dest, http_, i);
244
245 while(header.len--) {
246 char c = *header.data++;
247 if (c == '_') {
248 // CVE-2015-0219
249 return NULL;
250 }
251 else if(c == '-')
252 dest[i++] = '_';
253 else if(c >= 'a' && c <= 'z')
254 dest[i++] = c - ('a'-'A');
255 else
256 dest[i++] = c;
257 }
258
259 return (PyObject *)_Unicode_FromStringAndSize(dest, size);
260 }
261
262245 static inline void
263246 PyDict_ReplaceKey(PyObject* dict, PyObject* old_key, PyObject* new_key)
264247 {
275258 static http_parser_settings
276259 parser_settings = {
277260 on_message_begin, on_path, on_query_string, NULL, NULL, on_header_field,
278 on_header_value, on_headers_complete, on_body, on_message_complete
261 on_header_value, NULL, on_body, on_message_complete
279262 };
280263
281264 void _initialize_request_module(ServerInfo* server_info)
1919
2020 typedef struct {
2121 http_parser parser;
22 string field;
23 string value;
24 string body;
22 PyObject* field;
23 int last_call_was_header_value;
24 int invalid_header;
2525 } bj_parser;
2626
2727 typedef struct {