1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "istream.h"
5 #include "message-parser-private.h"
6 
7 static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx,
8 					 struct message_block *block_r);
9 static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
10 					    struct message_block *block_r);
11 
preparsed_parse_eof(struct message_parser_ctx * ctx ATTR_UNUSED,struct message_block * block_r ATTR_UNUSED)12 static int preparsed_parse_eof(struct message_parser_ctx *ctx ATTR_UNUSED,
13 			       struct message_block *block_r ATTR_UNUSED)
14 {
15 	return -1;
16 }
17 
preparsed_skip_to_next(struct message_parser_ctx * ctx)18 static void preparsed_skip_to_next(struct message_parser_ctx *ctx)
19 {
20 	ctx->parse_next_block = preparsed_parse_next_header_init;
21 	while (ctx->part != NULL) {
22 		if (ctx->part->next != NULL) {
23 			ctx->part = ctx->part->next;
24 			break;
25 		}
26 
27 		/* parse epilogue of multipart parent if requested */
28 		if (ctx->part->parent != NULL &&
29 		    (ctx->part->parent->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
30 		    (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0) {
31 			/* check for presence of epilogue */
32 			uoff_t part_end = ctx->part->physical_pos +
33 				ctx->part->header_size.physical_size +
34 				ctx->part->body_size.physical_size;
35 			uoff_t parent_end = ctx->part->parent->physical_pos +
36 				ctx->part->parent->header_size.physical_size +
37 				ctx->part->parent->body_size.physical_size;
38 
39 			if (parent_end > part_end) {
40 				ctx->parse_next_block = preparsed_parse_epilogue_init;
41 				break;
42 			}
43 		}
44 		ctx->part = ctx->part->parent;
45 	}
46 	if (ctx->part == NULL)
47 		ctx->parse_next_block = preparsed_parse_eof;
48 }
49 
preparsed_parse_body_finish(struct message_parser_ctx * ctx,struct message_block * block_r)50 static int preparsed_parse_body_finish(struct message_parser_ctx *ctx,
51 				       struct message_block *block_r)
52 {
53 	i_stream_skip(ctx->input, ctx->skip);
54 	ctx->skip = 0;
55 
56 	preparsed_skip_to_next(ctx);
57 	return ctx->parse_next_block(ctx, block_r);
58 }
59 
preparsed_parse_prologue_finish(struct message_parser_ctx * ctx,struct message_block * block_r)60 static int preparsed_parse_prologue_finish(struct message_parser_ctx *ctx,
61 					   struct message_block *block_r)
62 {
63 	i_stream_skip(ctx->input, ctx->skip);
64 	ctx->skip = 0;
65 
66 	ctx->parse_next_block = preparsed_parse_next_header_init;
67 	ctx->part = ctx->part->children;
68 	return ctx->parse_next_block(ctx, block_r);
69 }
70 
preparsed_parse_body_more(struct message_parser_ctx * ctx,struct message_block * block_r)71 static int preparsed_parse_body_more(struct message_parser_ctx *ctx,
72 				     struct message_block *block_r)
73 {
74 	uoff_t end_offset = ctx->part->physical_pos +
75 		ctx->part->header_size.physical_size +
76 		ctx->part->body_size.physical_size;
77 	bool full;
78 	int ret;
79 
80 	if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
81 		return ret;
82 
83 	if (ctx->input->v_offset + block_r->size >= end_offset) {
84 		block_r->size = end_offset - ctx->input->v_offset;
85 		ctx->parse_next_block = preparsed_parse_body_finish;
86 	}
87 	ctx->skip = block_r->size;
88 	return 1;
89 }
90 
preparsed_parse_prologue_more(struct message_parser_ctx * ctx,struct message_block * block_r)91 static int preparsed_parse_prologue_more(struct message_parser_ctx *ctx,
92 					 struct message_block *block_r)
93 {
94 	uoff_t boundary_min_start, end_offset;
95 	const unsigned char *cur;
96 	bool full;
97 	int ret;
98 
99 	i_assert(ctx->part->children != NULL);
100 	end_offset = ctx->part->children->physical_pos;
101 
102 	if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
103 		return ret;
104 
105 	if (ctx->input->v_offset + block_r->size >= end_offset) {
106 		/* we've got the full prologue: clip off the initial boundary */
107 		block_r->size = end_offset - ctx->input->v_offset;
108 		cur = block_r->data + block_r->size - 1;
109 
110 		/* [\r]\n--boundary[\r]\n */
111 		if (block_r->size < 5 || *cur != '\n') {
112 			ctx->broken_reason = "Prologue boundary end not at expected position";
113 			return -1;
114 		}
115 
116 		cur--;
117 		if (*cur == '\r') cur--;
118 
119 		/* find newline just before boundary */
120 		for (; cur >= block_r->data; cur--) {
121 			if (*cur == '\n') break;
122 		}
123 
124 		if (cur[0] != '\n' || cur[1] != '-' || cur[2] != '-') {
125 			ctx->broken_reason = "Prologue boundary beginning not at expected position";
126 			return -1;
127 		}
128 
129 		if (cur != block_r->data && cur[-1] == '\r') cur--;
130 
131 		/* clip boundary */
132 		block_r->size = cur - block_r->data;
133 
134 		ctx->parse_next_block = preparsed_parse_prologue_finish;
135 		ctx->skip = block_r->size;
136 		return 1;
137 	}
138 
139 	/* retain enough data in the stream buffer to contain initial boundary */
140 	if (end_offset > BOUNDARY_END_MAX_LEN)
141 		boundary_min_start = end_offset - BOUNDARY_END_MAX_LEN;
142 	else
143 		boundary_min_start = 0;
144 
145 	if (ctx->input->v_offset + block_r->size >= boundary_min_start) {
146 		if (boundary_min_start <= ctx->input->v_offset)
147 			return 0;
148 		block_r->size = boundary_min_start - ctx->input->v_offset;
149 	}
150 	ctx->skip = block_r->size;
151 	return 1;
152 }
153 
preparsed_parse_epilogue_more(struct message_parser_ctx * ctx,struct message_block * block_r)154 static int preparsed_parse_epilogue_more(struct message_parser_ctx *ctx,
155 					 struct message_block *block_r)
156 {
157 	uoff_t end_offset = ctx->part->physical_pos +
158 		ctx->part->header_size.physical_size +
159 		ctx->part->body_size.physical_size;
160 	bool full;
161 	int ret;
162 
163 	if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
164 		return ret;
165 
166 	if (ctx->input->v_offset + block_r->size >= end_offset) {
167 		block_r->size = end_offset - ctx->input->v_offset;
168 		ctx->parse_next_block = preparsed_parse_body_finish;
169 	}
170 	ctx->skip = block_r->size;
171 	return 1;
172 }
173 
preparsed_parse_epilogue_boundary(struct message_parser_ctx * ctx,struct message_block * block_r)174 static int preparsed_parse_epilogue_boundary(struct message_parser_ctx *ctx,
175 					     struct message_block *block_r)
176 {
177 	uoff_t end_offset = ctx->part->physical_pos +
178 		ctx->part->header_size.physical_size +
179 		ctx->part->body_size.physical_size;
180 	const unsigned char *data, *cur;
181 	size_t size;
182 	bool full;
183 	int ret;
184 
185 	if (end_offset - ctx->input->v_offset < 7) {
186 		ctx->broken_reason = "Epilogue position is wrong";
187 		return -1;
188 	}
189 
190 	if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
191 		return ret;
192 
193 	/* [\r]\n--boundary--[\r]\n */
194 	if (block_r->size < 7) {
195 		ctx->want_count = 7;
196 		return 0;
197 	}
198 
199 	data = block_r->data;
200 	size = block_r->size;
201 	cur = data;
202 
203 	if (*cur == '\r') cur++;
204 
205 	if (cur[0] != '\n' || cur[1] != '-' || data[2] != '-') {
206 		ctx->broken_reason = "Epilogue boundary start not at expected position";
207 		return -1;
208 	}
209 
210 	/* find the end of the line */
211 	cur += 3;
212 	if ((cur = memchr(cur, '\n', size - (cur-data))) == NULL) {
213 		if (end_offset < ctx->input->v_offset + size) {
214 			ctx->broken_reason = "Epilogue boundary end not at expected position";
215 			return -1;
216 		} else if (ctx->input->v_offset + size < end_offset &&
217 			   size < BOUNDARY_END_MAX_LEN &&
218 			   !ctx->input->eof && !full) {
219 			ctx->want_count = BOUNDARY_END_MAX_LEN;
220 			return 0;
221 		}
222 	}
223 
224 	block_r->size = 0;
225 	ctx->parse_next_block = preparsed_parse_epilogue_more;
226 	ctx->skip = cur - data + 1;
227 	return 0;
228 }
229 
preparsed_parse_body_init(struct message_parser_ctx * ctx,struct message_block * block_r)230 static int preparsed_parse_body_init(struct message_parser_ctx *ctx,
231 				     struct message_block *block_r)
232 {
233 	uoff_t offset = ctx->part->physical_pos +
234 		ctx->part->header_size.physical_size;
235 
236 	if (offset < ctx->input->v_offset) {
237 		/* header was actually larger than the cached size suggested */
238 		ctx->broken_reason = "Header larger than its cached size";
239 		return -1;
240 	}
241 	i_stream_skip(ctx->input, offset - ctx->input->v_offset);
242 
243 	/* multipart messages may begin with --boundary--, which makes them
244 	   not have any children. */
245 	if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 ||
246 	    ctx->part->children == NULL)
247 		ctx->parse_next_block = preparsed_parse_body_more;
248 	else
249 		ctx->parse_next_block = preparsed_parse_prologue_more;
250 	return ctx->parse_next_block(ctx, block_r);
251 }
252 
preparsed_parse_epilogue_init(struct message_parser_ctx * ctx,struct message_block * block_r)253 static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx,
254 					 struct message_block *block_r)
255 {
256 	uoff_t offset = ctx->part->physical_pos +
257 		ctx->part->header_size.physical_size +
258 		ctx->part->body_size.physical_size;
259 
260 	ctx->part = ctx->part->parent;
261 
262 	if (offset < ctx->input->v_offset) {
263 		/* last child was actually larger than the cached size
264 		   suggested */
265 		ctx->broken_reason = "Part larger than its cached size";
266 		return -1;
267 	}
268 	i_stream_skip(ctx->input, offset - ctx->input->v_offset);
269 
270 	ctx->parse_next_block = preparsed_parse_epilogue_boundary;
271 	return ctx->parse_next_block(ctx, block_r);
272 }
273 
preparsed_parse_finish_header(struct message_parser_ctx * ctx,struct message_block * block_r)274 static int preparsed_parse_finish_header(struct message_parser_ctx *ctx,
275 					 struct message_block *block_r)
276 {
277 	if (ctx->part->children != NULL) {
278 		if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
279 		    (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0)
280 			ctx->parse_next_block = preparsed_parse_body_init;
281 		else {
282 			ctx->parse_next_block = preparsed_parse_next_header_init;
283 			ctx->part = ctx->part->children;
284 		}
285 	} else if ((ctx->flags & MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK) == 0) {
286 		ctx->parse_next_block = preparsed_parse_body_init;
287 	} else {
288 		preparsed_skip_to_next(ctx);
289 	}
290 	return ctx->parse_next_block(ctx, block_r);
291 }
292 
preparsed_parse_next_header(struct message_parser_ctx * ctx,struct message_block * block_r)293 static int preparsed_parse_next_header(struct message_parser_ctx *ctx,
294 				       struct message_block *block_r)
295 {
296 	struct message_header_line *hdr;
297 	int ret;
298 
299 	ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr);
300 	if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0)) {
301 		ctx->want_count = i_stream_get_data_size(ctx->input) + 1;
302 		return ret;
303 	}
304 
305 	if (hdr != NULL) {
306 		block_r->hdr = hdr;
307 		block_r->size = 0;
308 		return 1;
309 	}
310 	message_parse_header_deinit(&ctx->hdr_parser_ctx);
311 
312 	ctx->parse_next_block = preparsed_parse_finish_header;
313 
314 	/* return empty block as end of headers */
315 	block_r->hdr = NULL;
316 	block_r->size = 0;
317 
318 	i_assert(ctx->skip == 0);
319 	if (ctx->input->v_offset != ctx->part->physical_pos +
320 	    ctx->part->header_size.physical_size) {
321 		ctx->broken_reason = "Cached header size mismatch";
322 		return -1;
323 	}
324 	return 1;
325 }
326 
preparsed_parse_next_header_init(struct message_parser_ctx * ctx,struct message_block * block_r)327 static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
328 					    struct message_block *block_r)
329 {
330 	struct istream *hdr_input;
331 
332 	i_assert(ctx->hdr_parser_ctx == NULL);
333 
334 	i_assert(ctx->part->physical_pos >= ctx->input->v_offset);
335 	i_stream_skip(ctx->input, ctx->part->physical_pos -
336 		      ctx->input->v_offset);
337 
338 	/* the header may become truncated by --boundaries. limit the header
339 	   stream's size to what it's supposed to be to avoid duplicating (and
340 	   keeping in sync!) all the same complicated logic as in
341 	   parse_next_header(). */
342 	hdr_input = i_stream_create_limit(ctx->input, ctx->part->header_size.physical_size);
343 	ctx->hdr_parser_ctx =
344 		message_parse_header_init(hdr_input, NULL, ctx->hdr_flags);
345 	i_stream_unref(&hdr_input);
346 
347 	ctx->parse_next_block = preparsed_parse_next_header;
348 	return preparsed_parse_next_header(ctx, block_r);
349 }
350 
351 struct message_parser_ctx *
message_parser_init_from_parts(struct message_part * parts,struct istream * input,const struct message_parser_settings * set)352 message_parser_init_from_parts(struct message_part *parts,
353 			       struct istream *input,
354 			       const struct message_parser_settings *set)
355 {
356 	struct message_parser_ctx *ctx;
357 
358 	i_assert(parts != NULL);
359 
360 	ctx = message_parser_init_int(input, set);
361 	ctx->preparsed = TRUE;
362 	ctx->parts = ctx->part = parts;
363 	ctx->parse_next_block = preparsed_parse_next_header_init;
364 	return ctx;
365 }
366