Line 0
Link Here
|
|
|
1 |
# $FreeBSD$ |
2 |
|
3 |
# CVE-2005-3183 |
4 |
|
5 |
--- Library/src/HTBound.c.orig 1999-02-22 22:10:10 UTC |
6 |
+++ Library/src/HTBound.c |
7 |
@@ -11,9 +11,12 @@ |
8 |
** |
9 |
** Authors |
10 |
** HF Henrik Frystyk <frystyk@w3.org> |
11 |
+** SV Sam Varshavchik <mrsam@courier-mta.com> |
12 |
** |
13 |
** History: |
14 |
** Nov 95 Written from scratch |
15 |
+** SV Jun 05 Rewrote HTBoundary_put_block. Fixed many bugs+segfaults. |
16 |
+** SV Jul 05 Fix double-counting of processed bytes. |
17 |
** |
18 |
*/ |
19 |
|
20 |
@@ -23,104 +26,395 @@ |
21 |
#include "WWWCore.h" |
22 |
#include "HTMerge.h" |
23 |
#include "HTReqMan.h" |
24 |
+#include "HTNetMan.h" |
25 |
+#include "HTChannl.h" |
26 |
#include "HTBound.h" /* Implemented here */ |
27 |
|
28 |
-#define PUTBLOCK(b, l) (*me->target->isa->put_block)(me->target, b, l) |
29 |
+#define PUTBLOCK(b, l) (me->target ? (*me->target->isa->put_block)(me->target, b, l):HT_OK) |
30 |
+ |
31 |
#define PUTDEBUG(b, l) (*me->debug->isa->put_block)(me->debug, b, l) |
32 |
#define FREE_TARGET (*me->target->isa->_free)(me->target) |
33 |
|
34 |
struct _HTStream { |
35 |
const HTStreamClass * isa; |
36 |
+ HTNet * net; |
37 |
HTStream * target; |
38 |
HTStream * orig_target; |
39 |
HTFormat format; |
40 |
HTStream * debug; /* For preamble and epilog */ |
41 |
HTRequest * request; |
42 |
- BOOL body; /* Body or preamble|epilog */ |
43 |
- HTEOLState state; |
44 |
- int dash; /* Number of dashes */ |
45 |
char * boundary; |
46 |
- char * bpos; |
47 |
+ |
48 |
+ BOOL keptcrlf; |
49 |
+ int (*state)(HTStream *, const char *, int); |
50 |
+ |
51 |
+ char *boundary_ptr; |
52 |
+ |
53 |
}; |
54 |
|
55 |
+PRIVATE int HTBoundary_flush (HTStream * me); |
56 |
+ |
57 |
/* ------------------------------------------------------------------------- */ |
58 |
|
59 |
+PRIVATE int start_of_line (HTStream * me, const char * b, int l); |
60 |
+PRIVATE int seen_dash (HTStream * me, const char * b, int l); |
61 |
+PRIVATE int seen_doubledash (HTStream * me, const char * b, int l); |
62 |
+PRIVATE int seen_delimiter_nonterminal(HTStream * me, const char * b, int l); |
63 |
+PRIVATE int seen_delimiter_nonterminal_CR(HTStream * me, const char * b, int l); |
64 |
+PRIVATE int seen_delimiter_dash(HTStream * me, const char * b, int l); |
65 |
+PRIVATE int seen_delimiter_terminal(HTStream * me, const char * b, int l); |
66 |
+PRIVATE int seen_delimiter_terminal_CR(HTStream * me, const char * b, int l); |
67 |
+PRIVATE int not_delimiter(HTStream * me, const char * b, int l, int extra); |
68 |
+PRIVATE int seen_nothing(HTStream * me, const char * b, int l); |
69 |
+PRIVATE int seen_cr(HTStream * me, const char * b, int l); |
70 |
+PRIVATE void process_boundary(HTStream *me, int isterminal); |
71 |
+ |
72 |
+#define UNUSED(l) (l=l) /* Shut up about unused variables */ |
73 |
+ |
74 |
PRIVATE int HTBoundary_put_block (HTStream * me, const char * b, int l) |
75 |
{ |
76 |
- const char *start = b; |
77 |
- const char *end = b; |
78 |
- while (l-- > 0) { |
79 |
- if (me->state == EOL_FCR) { |
80 |
- me->state = (*b == LF) ? EOL_FLF : EOL_BEGIN; |
81 |
- } else if (me->state == EOL_FLF) { |
82 |
- if (me->dash == 2) { |
83 |
- while (l>0 && *me->bpos && *me->bpos==*b) l--, me->bpos++, b++; |
84 |
- if (!*me->bpos) { |
85 |
- HTTRACE(STREAM_TRACE, "Boundary.... `%s\' found\n" _ me->boundary); |
86 |
- me->bpos = me->boundary; |
87 |
- me->body = YES; |
88 |
- me->state = EOL_DOT; |
89 |
- } else if (l>0) { |
90 |
- me->dash = 0; |
91 |
- me->bpos = me->boundary; |
92 |
- me->state = EOL_BEGIN; |
93 |
- } |
94 |
- } |
95 |
- if (*b == '-') { |
96 |
- me->dash++; |
97 |
- } else if (*b != CR && *b != LF) { |
98 |
- me->dash = 0; |
99 |
- me->state = EOL_BEGIN; |
100 |
- } |
101 |
- } else if (me->state == EOL_SLF) { /* Look for closing '--' */ |
102 |
- if (me->dash == 4) { |
103 |
- if (end > start) { |
104 |
- int status = PUTBLOCK(start, end-start); |
105 |
- if (status != HT_OK) return status; |
106 |
+ /* |
107 |
+ ** The HTBoundary object gets attached downstream of HTMime. |
108 |
+ ** The HTBoundary object creates another HTMime object downstream of |
109 |
+ ** the HTBoundary object. |
110 |
+ ** |
111 |
+ ** When we push data downstream to the second HTBoundary object, it |
112 |
+ ** updates the bytes read count in the HTNet object. |
113 |
+ ** |
114 |
+ ** When we return to the parent HTMime object, itupdates the |
115 |
+ ** bytes read count in the HTNet object again. Oops. |
116 |
+ ** |
117 |
+ ** Same thing happens with the consumed byte count. We can prevent |
118 |
+ ** the consumed byte counts from being updated by temporary setting |
119 |
+ ** the input channel stream pointer to NULL, but for the byte counts |
120 |
+ ** we have to save them and restore them before existing. |
121 |
+ ** |
122 |
+ ** This bug was discovered by chance when a multipart/partial response |
123 |
+ ** was partially received, and as a result of double-counting the |
124 |
+ ** real response got cut off (because HTMime thought that more bytes |
125 |
+ ** were processed than actually were, thus it processed only the |
126 |
+ ** partial count of the remaining bytes in the response). When the |
127 |
+ ** multipart/partial response was received all at once this bug did |
128 |
+ ** not get triggered. |
129 |
+ */ |
130 |
+ |
131 |
+ HTHost *host=HTNet_host(me->net); |
132 |
+ HTChannel *c=HTHost_channel(host); |
133 |
+ HTInputStream *i=HTChannel_input(c); |
134 |
+ |
135 |
+ long saveBytesRead=HTNet_bytesRead(me->net); |
136 |
+ long saveHeaderBytesRead=HTNet_headerBytesRead(me->net); |
137 |
+ |
138 |
+ if (i) |
139 |
+ HTChannel_setInput(c, NULL); |
140 |
+ |
141 |
+ HTTRACE(STREAM_TRACE, "Boundary: processing %d bytes\n" _ l); |
142 |
+ /* Main loop consumes all input */ |
143 |
+ |
144 |
+ while (l) |
145 |
+ { |
146 |
+ int n= (*me->state)(me, b, l); |
147 |
+ |
148 |
+ if (n == 0) |
149 |
+ return HT_ERROR; |
150 |
+ b += n; |
151 |
+ l -= n; |
152 |
+ } |
153 |
+ |
154 |
+ if (i) |
155 |
+ HTChannel_setInput(c, i); |
156 |
+ HTNet_setBytesRead(me->net, saveBytesRead); |
157 |
+ HTNet_setHeaderBytesRead(me->net, saveHeaderBytesRead); |
158 |
+ |
159 |
+ return HT_OK; |
160 |
+} |
161 |
+ |
162 |
+/* |
163 |
+** Start of line, keptcrlf=YES if we've kept the preceding CRLF from downstream |
164 |
+** and we'll pass it along if we decide that this is not a boundary delimiter. |
165 |
+*/ |
166 |
+ |
167 |
+PRIVATE int start_of_line (HTStream * me, const char * b, int l) |
168 |
+{ |
169 |
+ if (*b != '-') |
170 |
+ return not_delimiter(me, b, l, 0); |
171 |
+ |
172 |
+ HTTRACE(STREAM_TRACE, "Boundary: start of line: input '-'\n"); |
173 |
+ |
174 |
+ me->state= seen_dash; |
175 |
+ |
176 |
+ return 1; |
177 |
+} |
178 |
+ |
179 |
+/* |
180 |
+** Line: - |
181 |
+*/ |
182 |
+ |
183 |
+PRIVATE int seen_dash (HTStream * me, const char * b, int l) |
184 |
+{ |
185 |
+ if (*b != '-') |
186 |
+ return not_delimiter(me, b, l, 1); |
187 |
+ |
188 |
+ HTTRACE(STREAM_TRACE, "Boundary: start of line: input '--'\n"); |
189 |
+ |
190 |
+ me->state= seen_doubledash; |
191 |
+ me->boundary_ptr=me->boundary; |
192 |
+ return 1; |
193 |
+} |
194 |
+ |
195 |
+/* |
196 |
+** Line: -- |
197 |
+*/ |
198 |
+ |
199 |
+PRIVATE int seen_doubledash (HTStream * me, const char * b, int l) |
200 |
+{ |
201 |
+ me->state=seen_doubledash; |
202 |
+ |
203 |
+ if (*me->boundary_ptr) |
204 |
+ { |
205 |
+ if (*b != *me->boundary_ptr) |
206 |
+ { |
207 |
+ return not_delimiter(me, b, l, |
208 |
+ me->boundary_ptr - me->boundary |
209 |
+ + 2); |
210 |
} |
211 |
- HTTRACE(STREAM_TRACE, "Boundary.... Ending\n"); |
212 |
- start = b; |
213 |
- me->dash = 0; |
214 |
- me->state = EOL_BEGIN; |
215 |
- } |
216 |
- if (*b == '-') { |
217 |
- me->dash++; |
218 |
- } else if (*b != CR && *b != LF) { |
219 |
- me->dash = 0; |
220 |
- me->state = EOL_BEGIN; |
221 |
+ ++me->boundary_ptr; |
222 |
+ return 1; |
223 |
} |
224 |
- me->body = NO; |
225 |
- } else if (me->state == EOL_DOT) { |
226 |
- int status; |
227 |
- if (me->body) { |
228 |
- if (me->target) FREE_TARGET; |
229 |
+ |
230 |
+ /* |
231 |
+ ** Line: --delimiter |
232 |
+ */ |
233 |
+ |
234 |
+ if (*b == '-') |
235 |
+ { |
236 |
+ HTTRACE(STREAM_TRACE, |
237 |
+ "Boundary: start of line: input '--%s-'\n" |
238 |
+ _ me->boundary); |
239 |
+ |
240 |
+ me->state=seen_delimiter_dash; |
241 |
+ return 1; |
242 |
+ } |
243 |
+ |
244 |
+ HTTRACE(STREAM_TRACE, |
245 |
+ "Boundary: Found: '--%s'\n" _ me->boundary); |
246 |
+ |
247 |
+ return seen_delimiter_nonterminal(me, b, l); |
248 |
+} |
249 |
+ |
250 |
+/* |
251 |
+** Line: --delimiter |
252 |
+** |
253 |
+** Waiting for CRLF. |
254 |
+*/ |
255 |
+ |
256 |
+ |
257 |
+PRIVATE int seen_delimiter_nonterminal(HTStream * me, const char * b, int l) |
258 |
+{ |
259 |
+ UNUSED(l); |
260 |
+ |
261 |
+ me->state=seen_delimiter_nonterminal; |
262 |
+ if (*b == CR) |
263 |
+ me->state=seen_delimiter_nonterminal_CR; |
264 |
+ |
265 |
+ return 1; |
266 |
+} |
267 |
+ |
268 |
+/* |
269 |
+** Line: --delimiter<CR> |
270 |
+*/ |
271 |
+ |
272 |
+PRIVATE int seen_delimiter_nonterminal_CR(HTStream * me, const char * b, int l) |
273 |
+{ |
274 |
+ HTTRACE(STREAM_TRACE, |
275 |
+ "Boundary: Found: '--%s<CR>'\n" _ me->boundary); |
276 |
+ |
277 |
+ if (*b != LF) |
278 |
+ return seen_delimiter_nonterminal(me, b, l); |
279 |
+ |
280 |
+ HTTRACE(STREAM_TRACE, |
281 |
+ "Boundary: Found: '--%s<CR><LF>'\n" _ me->boundary); |
282 |
+ |
283 |
+ process_boundary(me, NO); |
284 |
+ return 1; |
285 |
+} |
286 |
+ |
287 |
+/* |
288 |
+** Line: --delimiter- |
289 |
+*/ |
290 |
+ |
291 |
+PRIVATE int seen_delimiter_dash(HTStream * me, const char * b, int l) |
292 |
+{ |
293 |
+ if (*b != '-') |
294 |
+ return seen_delimiter_nonterminal(me, b, l); |
295 |
+ |
296 |
+ HTTRACE(STREAM_TRACE, |
297 |
+ "Boundary: start of line: input '--%s--'\n" |
298 |
+ _ me->boundary); |
299 |
+ |
300 |
+ me->state=seen_delimiter_terminal; |
301 |
+ return 1; |
302 |
+} |
303 |
+ |
304 |
+/* |
305 |
+** Line: --delimiter-- |
306 |
+*/ |
307 |
+ |
308 |
+PRIVATE int seen_delimiter_terminal(HTStream * me, const char * b, int l) |
309 |
+{ |
310 |
+ UNUSED(l); |
311 |
+ |
312 |
+ me->state=seen_delimiter_terminal; |
313 |
+ |
314 |
+ if (*b == CR) |
315 |
+ me->state=seen_delimiter_terminal_CR; |
316 |
+ return 1; |
317 |
+} |
318 |
+/* |
319 |
+** Line: --delimiter--<CR> |
320 |
+*/ |
321 |
+ |
322 |
+PRIVATE int seen_delimiter_terminal_CR(HTStream * me, const char * b, int l) |
323 |
+{ |
324 |
+ HTTRACE(STREAM_TRACE, |
325 |
+ "Boundary: Found '--%s--<CR>'\n" |
326 |
+ _ me->boundary); |
327 |
+ |
328 |
+ if (*b != LF) |
329 |
+ return seen_delimiter_terminal(me, b, l); |
330 |
+ HTTRACE(STREAM_TRACE, |
331 |
+ "Boundary: Found '--%s--<CR><LF>'\n" |
332 |
+ _ me->boundary); |
333 |
+ |
334 |
+ process_boundary(me, YES); |
335 |
+ return 1; |
336 |
+} |
337 |
+ |
338 |
+/* |
339 |
+** Beginning of the line does not contain a delimiter. |
340 |
+** |
341 |
+** |
342 |
+** extra: Count of characters in a partially matched delimiter. Since it's |
343 |
+** not a delimiter this is content that needs to go downstream. |
344 |
+*/ |
345 |
+ |
346 |
+PRIVATE int not_delimiter(HTStream * me, const char * b, int l, int extra) |
347 |
+{ |
348 |
+ HTTRACE(STREAM_TRACE, "Boundary: not a delimiter line\n"); |
349 |
+ |
350 |
+ if (me->keptcrlf) |
351 |
+ { |
352 |
+ HTTRACE(STREAM_TRACE, "Boundary: Sending previous line's <CR><LF>\n"); |
353 |
+ /* |
354 |
+ ** Did not process CRLF from previous line, because prev CRLF |
355 |
+ ** is considered a part of the delimiter. See MIME RFC. |
356 |
+ */ |
357 |
+ |
358 |
+ me->keptcrlf=NO; |
359 |
+ if (PUTBLOCK("\r\n", 2) != HT_OK) |
360 |
+ return 0; |
361 |
+ } |
362 |
+ |
363 |
+ /* |
364 |
+ ** Potentially matched some of: --DELIMITER |
365 |
+ */ |
366 |
+ |
367 |
+ if (extra) |
368 |
+ { |
369 |
+ HTTRACE(STREAM_TRACE, "Boundary: Sending partially-matched %d characters\n" _ extra); |
370 |
+ |
371 |
+ if (PUTBLOCK("--", extra > 2 ? 2:extra) != HT_OK) |
372 |
+ return 0; |
373 |
+ |
374 |
+ if (extra > 2) |
375 |
+ if (PUTBLOCK(me->boundary, extra-2) != HT_OK) |
376 |
+ return 0; |
377 |
+ } |
378 |
+ return seen_nothing(me, b, l); |
379 |
+} |
380 |
+ |
381 |
+/* |
382 |
+** We're not looking for a delimiter. Look for the next line of input |
383 |
+** in the data that could potentially be a delimiter. |
384 |
+*/ |
385 |
+ |
386 |
+PRIVATE int seen_nothing(HTStream * me, const char * b, int l) |
387 |
+{ |
388 |
+ int i; |
389 |
+ |
390 |
+ me->state=seen_nothing; |
391 |
+ |
392 |
+ for (i=0; i<l; i++) |
393 |
+ { |
394 |
+ if (b[i] != CR) |
395 |
+ continue; |
396 |
+ |
397 |
+ /* |
398 |
+ ** If we have at least four more characters in unconsumed |
399 |
+ ** input, and they're not \r\n--, we can safely skip over |
400 |
+ ** them. |
401 |
+ */ |
402 |
+ |
403 |
+ if (l-i > 4 && |
404 |
+ strncmp(b+i, "\r\n--", 4)) |
405 |
+ continue; |
406 |
+ break; |
407 |
+ } |
408 |
+ |
409 |
+ if (i == 0) |
410 |
+ { |
411 |
+ /* Could only be a CR here. */ |
412 |
+ |
413 |
+ me->state=seen_cr; |
414 |
+ return 1; |
415 |
+ } |
416 |
+ |
417 |
+ HTTRACE(STREAM_TRACE, "Boundary: Processed %d (out of %d) bytes\n" |
418 |
+ _ i _ l); |
419 |
+ |
420 |
+ if (PUTBLOCK(b, i) != HT_OK) |
421 |
+ return 0; |
422 |
+ |
423 |
+ return i; |
424 |
+} |
425 |
+ |
426 |
+/* |
427 |
+** State: seen a CR |
428 |
+*/ |
429 |
+ |
430 |
+PRIVATE int seen_cr(HTStream * me, const char * b, int l) |
431 |
+{ |
432 |
+ HTTRACE(STREAM_TRACE, "Boundary: Processed <CR>\n"); |
433 |
+ |
434 |
+ if (*b != LF) |
435 |
+ { |
436 |
+ HTTRACE(STREAM_TRACE, "Boundary: ... <LF> didn't follow\n"); |
437 |
+ if (PUTBLOCK("\r", 1) != HT_OK) |
438 |
+ return 0; |
439 |
+ return seen_nothing(me, b, l); |
440 |
+ } |
441 |
+ |
442 |
+ HTTRACE(STREAM_TRACE, "Boundary: Processed <CR><LF>\n"); |
443 |
+ me->state=start_of_line; |
444 |
+ me->keptcrlf=YES; |
445 |
+ return 1; |
446 |
+} |
447 |
+ |
448 |
+PRIVATE void process_boundary(HTStream *me, int isterminal) |
449 |
+{ |
450 |
+ HTBoundary_flush(me); |
451 |
+ if (me->target) FREE_TARGET; |
452 |
+ me->target=NULL; |
453 |
+ me->state=start_of_line; |
454 |
+ me->keptcrlf=NO; |
455 |
+ |
456 |
+ if (!isterminal) |
457 |
me->target = HTStreamStack(WWW_MIME,me->format, |
458 |
HTMerge(me->orig_target, 2), |
459 |
me->request, YES); |
460 |
- if (end > start) { |
461 |
- if ((status = PUTBLOCK(start, end-start)) != HT_OK) |
462 |
- return status; |
463 |
- } |
464 |
- } else { |
465 |
- if (me->debug) |
466 |
- if ((status = PUTDEBUG(start, end-start)) != HT_OK) |
467 |
- return status; |
468 |
- } |
469 |
- start = b; |
470 |
- if (*b == '-') me->dash++; |
471 |
- me->state = EOL_SLF; |
472 |
- } else if (*b == CR) { |
473 |
- me->state = EOL_FCR; |
474 |
- end = b; |
475 |
- } else if (*b == LF) { |
476 |
- if (me->state != EOL_FCR) end = b; |
477 |
- me->state = EOL_FLF; |
478 |
- } |
479 |
- b++; |
480 |
- } |
481 |
- return (start<b && me->body) ? PUTBLOCK(start, b-start) : HT_OK; |
482 |
} |
483 |
|
484 |
+ |
485 |
PRIVATE int HTBoundary_put_string (HTStream * me, const char * s) |
486 |
{ |
487 |
return HTBoundary_put_block(me, s, (int) strlen(s)); |
488 |
@@ -133,6 +427,8 @@ PRIVATE int HTBoundary_put_character (HT |
489 |
|
490 |
PRIVATE int HTBoundary_flush (HTStream * me) |
491 |
{ |
492 |
+ if (me->target == NULL) |
493 |
+ return HT_OK; |
494 |
return (*me->target->isa->flush)(me->target); |
495 |
} |
496 |
|
497 |
@@ -182,18 +478,26 @@ PUBLIC HTStream * HTBoundary (HTReques |
498 |
HTResponse_formatParam(response) : |
499 |
HTAnchor_formatParam(anchor); |
500 |
char * boundary = HTAssocList_findObject(type_param, "boundary"); |
501 |
+ |
502 |
+ UNUSED(param); |
503 |
+ UNUSED(input_format); |
504 |
+ |
505 |
if (boundary) { |
506 |
HTStream * me; |
507 |
if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL) |
508 |
HT_OUTOFMEM("HTBoundary"); |
509 |
me->isa = &HTBoundaryClass; |
510 |
+ me->net = HTRequest_net(request); |
511 |
me->request = request; |
512 |
me->format = output_format; |
513 |
me->orig_target = output_stream; |
514 |
me->debug = HTRequest_debugStream(request); |
515 |
- me->state = EOL_FLF; |
516 |
+ |
517 |
+ me->state = start_of_line; |
518 |
+ me->keptcrlf=NO; |
519 |
+ |
520 |
StrAllocCopy(me->boundary, boundary); /* Local copy */ |
521 |
- me->bpos = me->boundary; |
522 |
+ |
523 |
HTTRACE(STREAM_TRACE, "Boundary.... Stream created with boundary '%s\'\n" _ me->boundary); |
524 |
return me; |
525 |
} else { |