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