1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2  */
3 
4 #include "lib.h"
5 #include "str.h"
6 #include "str-sanitize.h"
7 #include "mempool.h"
8 #include "buffer.h"
9 #include "hash.h"
10 #include "array.h"
11 #include "ostream.h"
12 #include "eacces-error.h"
13 #include "safe-mkstemp.h"
14 
15 #include "sieve-error.h"
16 #include "sieve-extensions.h"
17 #include "sieve-code.h"
18 #include "sieve-script.h"
19 
20 #include "sieve-binary-private.h"
21 
22 /*
23  * Forward declarations
24  */
25 
26 static inline struct sieve_binary_extension_reg *
27 sieve_binary_extension_get_reg(struct sieve_binary *sbin,
28 			       const struct sieve_extension *ext, bool create);
29 
30 static inline int
31 sieve_binary_extension_register(struct sieve_binary *sbin,
32 				const struct sieve_extension *ext,
33 				struct sieve_binary_extension_reg **reg);
34 
35 /*
36  * Binary object
37  */
38 
sieve_binary_update_event(struct sieve_binary * sbin,const char * new_path)39 void sieve_binary_update_event(struct sieve_binary *sbin, const char *new_path)
40 {
41 	if (new_path != NULL) {
42 		event_set_append_log_prefix(
43 			sbin->event, t_strdup_printf("binary %s: ", new_path));
44 	} else if (sbin->path != NULL) {
45 		event_set_append_log_prefix(
46 			sbin->event,
47 			t_strdup_printf("binary %s: ", sbin->path));
48 	} else if (sbin->script != NULL) {
49 		event_set_append_log_prefix(
50 			sbin->event,
51 			t_strdup_printf("binary %s: ",
52 					sieve_script_name(sbin->script)));
53 	} else {
54 		event_set_append_log_prefix(sbin->event, "binary: ");
55 	}
56 }
57 
58 struct sieve_binary *
sieve_binary_create(struct sieve_instance * svinst,struct sieve_script * script)59 sieve_binary_create(struct sieve_instance *svinst, struct sieve_script *script)
60 {
61 	pool_t pool;
62 	struct sieve_binary *sbin;
63 	const struct sieve_extension *const *ext_preloaded;
64 	unsigned int i, ext_count;
65 
66 	pool = pool_alloconly_create("sieve_binary", 16384);
67 	sbin = p_new(pool, struct sieve_binary, 1);
68 	sbin->pool = pool;
69 	sbin->refcount = 1;
70 	sbin->svinst = svinst;
71 
72 	sbin->header.version_major = SIEVE_BINARY_VERSION_MAJOR;
73 	sbin->header.version_minor = SIEVE_BINARY_VERSION_MINOR;
74 
75 	sbin->script = script;
76 	if (script != NULL)
77 		sieve_script_ref(script);
78 
79 	sbin->event = event_create(svinst->event);
80 
81 	ext_count = sieve_extensions_get_count(svinst);
82 
83 	p_array_init(&sbin->linked_extensions, pool, ext_count);
84 	p_array_init(&sbin->extensions, pool, ext_count);
85 	p_array_init(&sbin->extension_index, pool, ext_count);
86 
87 	p_array_init(&sbin->blocks, pool, 16);
88 
89 	/* Pre-load core language features implemented as 'extensions' */
90 	ext_preloaded = sieve_extensions_get_preloaded(svinst, &ext_count);
91 	for (i = 0; i < ext_count; i++) {
92 		const struct sieve_extension_def *ext_def = ext_preloaded[i]->def;
93 
94 		if (ext_def != NULL && ext_def->binary_load != NULL)
95 			(void)ext_def->binary_load(ext_preloaded[i], sbin);
96 	}
97 
98 	return sbin;
99 }
100 
sieve_binary_create_new(struct sieve_script * script)101 struct sieve_binary *sieve_binary_create_new(struct sieve_script *script)
102 {
103 	struct sieve_binary *sbin =
104 		sieve_binary_create(sieve_script_svinst(script), script);
105 	struct sieve_binary_block *sblock;
106 	unsigned int i;
107 
108 	sieve_binary_update_event(sbin, NULL);
109 
110 	/* Create script metadata block */
111 	sblock = sieve_binary_block_create(sbin);
112 	sieve_script_binary_write_metadata(script, sblock);
113 
114 	/* Create other system blocks */
115 	for (i = 1; i < SBIN_SYSBLOCK_LAST; i++)
116 		(void)sieve_binary_block_create(sbin);
117 
118 	return sbin;
119 }
120 
sieve_binary_ref(struct sieve_binary * sbin)121 void sieve_binary_ref(struct sieve_binary *sbin)
122 {
123 	sbin->refcount++;
124 }
125 
sieve_binary_extensions_free(struct sieve_binary * sbin)126 static inline void sieve_binary_extensions_free(struct sieve_binary *sbin)
127 {
128 	struct sieve_binary_extension_reg *const *regs;
129 	unsigned int ext_count, i;
130 
131 	/* Cleanup binary extensions */
132 	regs = array_get(&sbin->extensions, &ext_count);
133 	for (i = 0; i < ext_count; i++) {
134 		const struct sieve_binary_extension *binext = regs[i]->binext;
135 
136 		if (binext != NULL && binext->binary_free != NULL) {
137 			binext->binary_free(regs[i]->extension, sbin,
138 					    regs[i]->context);
139 		}
140 	}
141 }
142 
sieve_binary_update_resource_usage(struct sieve_binary * sbin)143 static void sieve_binary_update_resource_usage(struct sieve_binary *sbin)
144 {
145 	enum sieve_error error;
146 
147 	if (sbin->rusage_updated)
148 		(void)sieve_binary_file_update_resource_usage(sbin, &error);
149 	sbin->rusage_updated = FALSE;
150 }
151 
sieve_binary_unref(struct sieve_binary ** _sbin)152 void sieve_binary_unref(struct sieve_binary **_sbin)
153 {
154 	struct sieve_binary *sbin = *_sbin;
155 
156 	*_sbin = NULL;
157 	if (sbin == NULL)
158 		return;
159 
160 	i_assert(sbin->refcount > 0);
161 	if (--sbin->refcount != 0)
162 		return;
163 
164 	sieve_binary_file_close(&sbin->file);
165 	sieve_binary_update_resource_usage(sbin);
166 	sieve_binary_extensions_free(sbin);
167 
168 	if (sbin->script != NULL)
169 		sieve_script_unref(&sbin->script);
170 
171 	event_unref(&sbin->event);
172 	pool_unref(&sbin->pool);
173 }
174 
sieve_binary_close(struct sieve_binary ** _sbin)175 void sieve_binary_close(struct sieve_binary **_sbin)
176 {
177 	struct sieve_binary *sbin = *_sbin;
178 
179 	*_sbin = NULL;
180 	if (sbin == NULL)
181 		return;
182 
183 	sieve_binary_file_close(&sbin->file);
184 	sieve_binary_update_resource_usage(sbin);
185 	sieve_binary_unref(&sbin);
186 }
187 
188 /*
189  * Resource usage
190  */
191 
sieve_binary_get_resource_usage(struct sieve_binary * sbin,struct sieve_resource_usage * rusage_r)192 void sieve_binary_get_resource_usage(struct sieve_binary *sbin,
193 				     struct sieve_resource_usage *rusage_r)
194 {
195 	struct sieve_binary_header *header = &sbin->header;
196 	time_t update_time = header->resource_usage.update_time;
197 	unsigned int timeout = sbin->svinst->resource_usage_timeout_secs;
198 
199 	if (update_time != 0 && (ioloop_time - update_time) > timeout)
200 		i_zero(&header->resource_usage);
201 
202 	sieve_resource_usage_init(rusage_r);
203 	rusage_r->cpu_time_msecs = header->resource_usage.cpu_time_msecs;
204 	sieve_resource_usage_add(rusage_r, &sbin->rusage);
205 }
206 
sieve_binary_check_resource_usage(struct sieve_binary * sbin)207 bool sieve_binary_check_resource_usage(struct sieve_binary *sbin)
208 {
209 	struct sieve_binary_header *header = &sbin->header;
210 	struct sieve_resource_usage rusage;
211 
212 	sieve_binary_get_resource_usage(sbin, &rusage);
213 
214 	if (sieve_resource_usage_is_excessive(sbin->svinst, &rusage)) {
215 		header->flags |= SIEVE_BINARY_FLAG_RESOURCE_LIMIT;
216 		return FALSE;
217 	}
218 	return TRUE;
219 }
220 
sieve_binary_record_resource_usage(struct sieve_binary * sbin,const struct sieve_resource_usage * rusage)221 bool sieve_binary_record_resource_usage(
222 	struct sieve_binary *sbin, const struct sieve_resource_usage *rusage)
223 {
224 	struct sieve_resource_usage rusage_total;
225 
226 	if (sbin == NULL)
227 		return TRUE;
228 	if (!sieve_resource_usage_is_high(sbin->svinst, rusage))
229 		return TRUE;
230 
231 	sieve_resource_usage_add(&sbin->rusage, rusage);
232 	sbin->rusage_updated = TRUE;
233 
234 	sieve_binary_get_resource_usage(sbin, &rusage_total);
235 
236 	e_debug(sbin->event, "Updated cumulative resource usage: %s",
237 		sieve_resource_usage_get_summary(&rusage_total));
238 
239 	return sieve_binary_check_resource_usage(sbin);
240 }
241 
sieve_binary_set_resource_usage(struct sieve_binary * sbin,const struct sieve_resource_usage * rusage)242 void sieve_binary_set_resource_usage(struct sieve_binary *sbin,
243 				     const struct sieve_resource_usage *rusage)
244 {
245 	struct sieve_binary_header *header = &sbin->header;
246 
247 	i_zero(&header->resource_usage);
248 	sbin->rusage = *rusage;
249 	sbin->rusage_updated = TRUE;
250 
251 	(void)sieve_binary_check_resource_usage(sbin);
252 }
253 
254 /*
255  * Accessors
256  */
257 
sieve_binary_pool(struct sieve_binary * sbin)258 pool_t sieve_binary_pool(struct sieve_binary *sbin)
259 {
260 	return sbin->pool;
261 }
262 
sieve_binary_script(struct sieve_binary * sbin)263 struct sieve_script *sieve_binary_script(struct sieve_binary *sbin)
264 {
265 	return sbin->script;
266 }
267 
sieve_binary_path(struct sieve_binary * sbin)268 const char *sieve_binary_path(struct sieve_binary *sbin)
269 {
270 	return sbin->path;
271 }
272 
sieve_binary_saved(struct sieve_binary * sbin)273 bool sieve_binary_saved(struct sieve_binary *sbin)
274 {
275 	return (sbin->path != NULL);
276 }
277 
sieve_binary_loaded(struct sieve_binary * sbin)278 bool sieve_binary_loaded(struct sieve_binary *sbin)
279 {
280 	return (sbin->file != NULL);
281 }
282 
sieve_binary_source(struct sieve_binary * sbin)283 const char *sieve_binary_source(struct sieve_binary *sbin)
284 {
285 	if (sbin->script != NULL && (sbin->path == NULL || sbin->file == NULL))
286 		return sieve_script_location(sbin->script);
287 
288 	return sbin->path;
289 }
290 
sieve_binary_svinst(struct sieve_binary * sbin)291 struct sieve_instance *sieve_binary_svinst(struct sieve_binary *sbin)
292 {
293 	return sbin->svinst;
294 }
295 
sieve_binary_mtime(struct sieve_binary * sbin)296 time_t sieve_binary_mtime(struct sieve_binary *sbin)
297 {
298 	i_assert(sbin->file != NULL);
299 	return sbin->file->st.st_mtime;
300 }
301 
sieve_binary_stat(struct sieve_binary * sbin)302 const struct stat *sieve_binary_stat(struct sieve_binary *sbin)
303 {
304 	i_assert(sbin->file != NULL);
305 	return &sbin->file->st;
306 }
307 
sieve_binary_script_name(struct sieve_binary * sbin)308 const char *sieve_binary_script_name(struct sieve_binary *sbin)
309 {
310 	return (sbin->script == NULL ?
311 		NULL : sieve_script_name(sbin->script));
312 }
313 
sieve_binary_script_location(struct sieve_binary * sbin)314 const char *sieve_binary_script_location(struct sieve_binary *sbin)
315 {
316 	return (sbin->script == NULL ?
317 		NULL : sieve_script_location(sbin->script));
318 }
319 
320 /*
321  * Utility
322  */
323 
sieve_binfile_from_name(const char * name)324 const char *sieve_binfile_from_name(const char *name)
325 {
326 	return t_strconcat(name, "."SIEVE_BINARY_FILEEXT, NULL);
327 }
328 
329 /*
330  * Block management
331  */
332 
sieve_binary_block_count(struct sieve_binary * sbin)333 unsigned int sieve_binary_block_count(struct sieve_binary *sbin)
334 {
335 	return array_count(&sbin->blocks);
336 }
337 
sieve_binary_block_create(struct sieve_binary * sbin)338 struct sieve_binary_block *sieve_binary_block_create(struct sieve_binary *sbin)
339 {
340 	unsigned int id = sieve_binary_block_count(sbin);
341 	struct sieve_binary_block *sblock;
342 
343 	sblock = p_new(sbin->pool, struct sieve_binary_block, 1);
344 	sblock->data = buffer_create_dynamic(sbin->pool, 64);
345 	sblock->sbin = sbin;
346 	sblock->id = id;
347 
348 	array_append(&sbin->blocks, &sblock, 1);
349 
350 	return sblock;
351 }
352 
353 struct sieve_binary_block *
sieve_binary_block_create_id(struct sieve_binary * sbin,unsigned int id)354 sieve_binary_block_create_id(struct sieve_binary *sbin, unsigned int id)
355 {
356 	struct sieve_binary_block *sblock;
357 
358 	sblock = p_new(sbin->pool, struct sieve_binary_block, 1);
359 
360 	array_idx_set(&sbin->blocks, id, &sblock);
361 	sblock->data = NULL;
362 	sblock->sbin = sbin;
363 	sblock->id = id;
364 
365 	return sblock;
366 }
367 
sieve_binary_block_fetch(struct sieve_binary_block * sblock)368 static bool sieve_binary_block_fetch(struct sieve_binary_block *sblock)
369 {
370 	struct sieve_binary *sbin = sblock->sbin;
371 
372 	if (sbin->file != NULL) {
373 		/* Try to acces the block in the binary on disk (apparently we
374 		   were lazy)
375 		 */
376 		if (!sieve_binary_load_block(sblock) || sblock->data == NULL)
377 			return FALSE;
378 	} else {
379 		sblock->data = buffer_create_dynamic(sbin->pool, 64);
380 		return TRUE;
381 	}
382 
383 	return TRUE;
384 }
385 
386 struct sieve_binary_block *
sieve_binary_block_get(struct sieve_binary * sbin,unsigned int id)387 sieve_binary_block_get(struct sieve_binary *sbin, unsigned int id)
388 {
389 	struct sieve_binary_block *sblock = sieve_binary_block_index(sbin, id);
390 
391 	if (sblock == NULL)
392 		return NULL;
393 
394 	if (sblock->data == NULL && !sieve_binary_block_fetch(sblock))
395 		return NULL;
396 
397 	return sblock;
398 }
399 
sieve_binary_block_clear(struct sieve_binary_block * sblock)400 void sieve_binary_block_clear(struct sieve_binary_block *sblock)
401 {
402 	buffer_set_used_size(sblock->data, 0);
403 }
404 
sieve_binary_block_get_buffer(struct sieve_binary_block * sblock)405 buffer_t *sieve_binary_block_get_buffer(struct sieve_binary_block *sblock)
406 {
407 	if (sblock->data == NULL && !sieve_binary_block_fetch(sblock))
408 		return NULL;
409 
410 	return sblock->data;
411 }
412 
413 struct sieve_binary *
sieve_binary_block_get_binary(const struct sieve_binary_block * sblock)414 sieve_binary_block_get_binary(const struct sieve_binary_block *sblock)
415 {
416 	return sblock->sbin;
417 }
418 
sieve_binary_block_get_id(const struct sieve_binary_block * sblock)419 unsigned int sieve_binary_block_get_id(const struct sieve_binary_block *sblock)
420 {
421 	return sblock->id;
422 }
423 
sieve_binary_block_get_size(const struct sieve_binary_block * sblock)424 size_t sieve_binary_block_get_size(const struct sieve_binary_block *sblock)
425 {
426 	return _sieve_binary_block_get_size(sblock);
427 }
428 
429 /*
430  * Up-to-date checking
431  */
432 
sieve_binary_up_to_date(struct sieve_binary * sbin,enum sieve_compile_flags cpflags)433 bool sieve_binary_up_to_date(struct sieve_binary *sbin,
434 			     enum sieve_compile_flags cpflags)
435 {
436 	struct sieve_binary_extension_reg *const *regs;
437 	struct sieve_binary_block *sblock;
438 	sieve_size_t offset = 0;
439 	unsigned int ext_count, i;
440 	int ret;
441 
442 	i_assert(sbin->file != NULL);
443 
444 	sblock = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_SCRIPT_DATA);
445 	if (sblock == NULL || sbin->script == NULL)
446 		return FALSE;
447 
448 	if ((ret = sieve_script_binary_read_metadata(sbin->script, sblock,
449 						     &offset)) <= 0) {
450 		if (ret < 0) {
451 			e_debug(sbin->event, "up-to-date: "
452 				"failed to read script metadata from binary");
453 		} else {
454 			e_debug(sbin->event, "up-to-date: "
455 				"script metadata indicates that binary is not up-to-date");
456 		}
457 		return FALSE;
458 	}
459 
460 	regs = array_get(&sbin->extensions, &ext_count);
461 	for (i = 0; i < ext_count; i++) {
462 		const struct sieve_binary_extension *binext = regs[i]->binext;
463 
464 		if (binext != NULL && binext->binary_up_to_date != NULL &&
465 		    !binext->binary_up_to_date(regs[i]->extension, sbin,
466 					       regs[i]->context, cpflags)) {
467 			e_debug(sbin->event, "up-to-date: "
468 				"the %s extension indicates binary is not up-to-date",
469 				sieve_extension_name(regs[i]->extension));
470 			return FALSE;
471 		}
472 	}
473 	return TRUE;
474 }
475 
476 /*
477  * Activate the binary (after code generation)
478  */
479 
sieve_binary_activate(struct sieve_binary * sbin)480 void sieve_binary_activate(struct sieve_binary *sbin)
481 {
482 	struct sieve_binary_extension_reg *const *regs;
483 	unsigned int i, ext_count;
484 
485 	/* Load other extensions into binary */
486 	regs = array_get(&sbin->linked_extensions, &ext_count);
487 	for (i = 0; i < ext_count; i++) {
488 		const struct sieve_extension *ext = regs[i]->extension;
489 
490 		if (ext != NULL && ext->def != NULL &&
491 		    ext->def->binary_load != NULL)
492 			ext->def->binary_load(ext, sbin);
493 	}
494 }
495 
496 /*
497  * Extension handling
498  */
499 
sieve_binary_extension_set_context(struct sieve_binary * sbin,const struct sieve_extension * ext,void * context)500 void sieve_binary_extension_set_context(struct sieve_binary *sbin,
501 					const struct sieve_extension *ext,
502 					void *context)
503 {
504 	struct sieve_binary_extension_reg *ereg =
505 		sieve_binary_extension_get_reg(sbin, ext, TRUE);
506 
507 	if (ereg != NULL)
508 		ereg->context = context;
509 }
510 
511 const void *
sieve_binary_extension_get_context(struct sieve_binary * sbin,const struct sieve_extension * ext)512 sieve_binary_extension_get_context(struct sieve_binary *sbin,
513 				   const struct sieve_extension *ext)
514 {
515 	struct sieve_binary_extension_reg *ereg =
516 		sieve_binary_extension_get_reg(sbin, ext, TRUE);
517 
518 	if (ereg != NULL)
519 		return ereg->context;
520 
521 	return NULL;
522 }
523 
sieve_binary_extension_set(struct sieve_binary * sbin,const struct sieve_extension * ext,const struct sieve_binary_extension * bext,void * context)524 void sieve_binary_extension_set(struct sieve_binary *sbin,
525 				const struct sieve_extension *ext,
526 				const struct sieve_binary_extension *bext,
527 				void *context)
528 {
529 	struct sieve_binary_extension_reg *ereg =
530 		sieve_binary_extension_get_reg(sbin, ext, TRUE);
531 
532 	if (ereg != NULL) {
533 		ereg->binext = bext;
534 
535 		if (context != NULL)
536 			ereg->context = context;
537 	}
538 }
539 
540 struct sieve_binary_block *
sieve_binary_extension_create_block(struct sieve_binary * sbin,const struct sieve_extension * ext)541 sieve_binary_extension_create_block(struct sieve_binary *sbin,
542 				    const struct sieve_extension *ext)
543 {
544 	struct sieve_binary_block *sblock;
545 	struct sieve_binary_extension_reg *ereg =
546 		sieve_binary_extension_get_reg(sbin, ext, TRUE);
547 
548 	i_assert(ereg != NULL);
549 
550 	sblock = sieve_binary_block_create(sbin);
551 
552 	if (ereg->block_id < SBIN_SYSBLOCK_LAST)
553 		ereg->block_id = sblock->id;
554 	sblock->ext_index = ereg->index;
555 
556 	return sblock;
557 }
558 
559 struct sieve_binary_block *
sieve_binary_extension_get_block(struct sieve_binary * sbin,const struct sieve_extension * ext)560 sieve_binary_extension_get_block(struct sieve_binary *sbin,
561 				 const struct sieve_extension *ext)
562 {
563 	struct sieve_binary_extension_reg *ereg =
564 		sieve_binary_extension_get_reg(sbin, ext, TRUE);
565 
566 	i_assert(ereg != NULL);
567 
568 	if (ereg->block_id < SBIN_SYSBLOCK_LAST)
569 		return NULL;
570 
571 	return sieve_binary_block_get(sbin, ereg->block_id);
572 }
573 
sieve_binary_extension_link(struct sieve_binary * sbin,const struct sieve_extension * ext)574 int sieve_binary_extension_link(struct sieve_binary *sbin,
575 				const struct sieve_extension *ext)
576 {
577 	return sieve_binary_extension_register(sbin, ext, NULL);
578 }
579 
580 const struct sieve_extension *
sieve_binary_extension_get_by_index(struct sieve_binary * sbin,int index)581 sieve_binary_extension_get_by_index(struct sieve_binary *sbin, int index)
582 {
583 	struct sieve_binary_extension_reg * const *ereg;
584 
585 	if (index < (int)array_count(&sbin->extensions)) {
586 		ereg = array_idx(&sbin->extensions, (unsigned int)index);
587 
588 		return (*ereg)->extension;
589 	}
590 
591 	return NULL;
592 }
593 
sieve_binary_extension_get_index(struct sieve_binary * sbin,const struct sieve_extension * ext)594 int sieve_binary_extension_get_index(struct sieve_binary *sbin,
595 				     const struct sieve_extension *ext)
596 {
597 	struct sieve_binary_extension_reg *ereg =
598 		sieve_binary_extension_get_reg(sbin, ext, FALSE);
599 
600 	return (ereg == NULL ? -1 : ereg->index);
601 }
602 
sieve_binary_extensions_count(struct sieve_binary * sbin)603 int sieve_binary_extensions_count(struct sieve_binary *sbin)
604 {
605 	return (int)array_count(&sbin->extensions);
606 }
607