Coverage Report

Created: 2024-02-11 06:22

/src/dovecot/src/lib/event-filter.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "array.h"
5
#include "llist.h"
6
#include "str.h"
7
#include "strescape.h"
8
#include "wildcard-match.h"
9
#include "lib-event-private.h"
10
#include "event-filter.h"
11
#include "event-filter-private.h"
12
13
enum event_filter_code {
14
  EVENT_FILTER_CODE_NAME    = 'n',
15
  EVENT_FILTER_CODE_SOURCE  = 's',
16
  EVENT_FILTER_CODE_CATEGORY  = 'c',
17
  EVENT_FILTER_CODE_FIELD   = 'f',
18
};
19
20
enum cmp_flags {
21
  CMP_FLAG_WILDCARD = BIT(1),
22
  CMP_FLAG_CASE_SENSITIVE = BIT(2),
23
};
24
25
/* map <log type> to <event filter log type & name> */
26
static const struct log_type_map {
27
  enum event_filter_log_type log_type;
28
  const char *name;
29
} event_filter_log_type_map[] = {
30
  [LOG_TYPE_DEBUG]   = { EVENT_FILTER_LOG_TYPE_DEBUG, "debug" },
31
  [LOG_TYPE_INFO]    = { EVENT_FILTER_LOG_TYPE_INFO, "info" },
32
  [LOG_TYPE_WARNING] = { EVENT_FILTER_LOG_TYPE_WARNING, "warning" },
33
  [LOG_TYPE_ERROR]   = { EVENT_FILTER_LOG_TYPE_ERROR, "error" },
34
  [LOG_TYPE_FATAL]   = { EVENT_FILTER_LOG_TYPE_FATAL, "fatal" },
35
  [LOG_TYPE_PANIC]   = { EVENT_FILTER_LOG_TYPE_PANIC, "panic" },
36
};
37
static_assert_array_size(event_filter_log_type_map, LOG_TYPE_COUNT);
38
39
struct event_filter_query_internal {
40
  struct event_filter_node *expr;
41
  void *context;
42
};
43
44
static struct event_filter *event_filters = NULL;
45
46
static struct event_filter *event_filter_create_real(pool_t pool, bool fragment)
47
0
{
48
0
  struct event_filter *filter;
49
50
0
  filter = p_new(pool, struct event_filter, 1);
51
0
  filter->pool = pool;
52
0
  filter->refcount = 1;
53
0
  filter->named_queries_only = TRUE;
54
0
  filter->fragment = fragment;
55
0
  p_array_init(&filter->queries, pool, 4);
56
0
  if (!fragment)
57
0
    DLLIST_PREPEND(&event_filters, filter);
58
0
  return filter;
59
0
}
60
61
struct event_filter *event_filter_create(void)
62
0
{
63
0
  return event_filter_create_real(pool_alloconly_create("event filter", 2048), FALSE);
64
0
}
65
66
struct event_filter *event_filter_create_with_pool(pool_t pool)
67
0
{
68
0
  return event_filter_create_real(pool, FALSE);
69
0
}
70
71
struct event_filter *event_filter_create_fragment(pool_t pool)
72
0
{
73
0
  return event_filter_create_real(pool, TRUE);
74
0
}
75
76
void event_filter_ref(struct event_filter *filter)
77
0
{
78
0
  i_assert(filter->refcount > 0);
79
0
  filter->refcount++;
80
0
}
81
82
void event_filter_unref(struct event_filter **_filter)
83
0
{
84
0
  struct event_filter *filter = *_filter;
85
86
0
  if (filter == NULL)
87
0
    return;
88
0
  i_assert(filter->refcount > 0);
89
90
0
  *_filter = NULL;
91
0
  if (--filter->refcount > 0)
92
0
    return;
93
94
0
  if (!filter->fragment) {
95
0
    DLLIST_REMOVE(&event_filters, filter);
96
97
    /* fragments' pools are freed by the consumer */
98
0
    pool_unref(&filter->pool);
99
0
  }
100
0
}
101
102
static const char *
103
wanted_field_value_str(const struct event_field *wanted_field)
104
0
{
105
0
  switch (wanted_field->value_type) {
106
0
  case EVENT_FIELD_VALUE_TYPE_STR:
107
0
    return wanted_field->value.str;
108
0
  case EVENT_FIELD_VALUE_TYPE_INTMAX:
109
0
    return dec2str(wanted_field->value.intmax);
110
0
  case EVENT_FIELD_VALUE_TYPE_IP: {
111
0
    const char *str = net_ip2addr(&wanted_field->value.ip);
112
0
    if (wanted_field->value.ip_bits ==
113
0
        IPADDR_BITS(&wanted_field->value.ip))
114
0
      return str;
115
0
    return t_strdup_printf("%s/%u", str, wanted_field->value.ip_bits);
116
0
  }
117
0
  case EVENT_FIELD_VALUE_TYPE_TIMEVAL:
118
0
  case EVENT_FIELD_VALUE_TYPE_STRLIST:
119
0
    break;
120
0
  }
121
0
  i_unreached();
122
0
}
123
124
/*
125
 * Look for an existing query with the same context pointer and return it.
126
 *
127
 * If not found, allocate a new internal query and return it.
128
 */
129
static struct event_filter_query_internal *
130
event_filter_get_or_alloc_internal_query(struct event_filter *filter,
131
           void *context)
132
0
{
133
0
  struct event_filter_query_internal *query;
134
135
0
  array_foreach_modifiable(&filter->queries, query) {
136
0
    if (query->context == context)
137
0
      return query;
138
0
  }
139
140
  /* no matching context, allocate a new query */
141
0
  query = array_append_space(&filter->queries);
142
0
  query->context = context;
143
0
  query->expr = NULL;
144
145
0
  return query;
146
0
}
147
148
static void add_node(pool_t pool, struct event_filter_node **root,
149
         struct event_filter_node *new,
150
         enum event_filter_node_op op)
151
0
{
152
0
  struct event_filter_node *parent;
153
154
0
  i_assert((op == EVENT_FILTER_OP_AND) || (op == EVENT_FILTER_OP_OR));
155
156
0
  if (*root == NULL) {
157
0
    *root = new;
158
0
    return;
159
0
  }
160
161
0
  parent = p_new(pool, struct event_filter_node, 1);
162
0
  parent->type = EVENT_FILTER_NODE_TYPE_LOGIC;
163
0
  parent->op = op;
164
0
  parent->children[0] = *root;
165
0
  parent->children[1] = new;
166
167
0
  *root = parent;
168
0
}
169
170
static bool filter_node_requires_event_name(struct event_filter_node *node)
171
0
{
172
0
  switch (node->op) {
173
0
  case EVENT_FILTER_OP_NOT:
174
0
    return filter_node_requires_event_name(node->children[0]);
175
0
  case EVENT_FILTER_OP_AND:
176
0
    return filter_node_requires_event_name(node->children[0]) ||
177
0
      filter_node_requires_event_name(node->children[1]);
178
0
  case EVENT_FILTER_OP_OR:
179
0
    return filter_node_requires_event_name(node->children[0]) &&
180
0
      filter_node_requires_event_name(node->children[1]);
181
0
  default:
182
0
    return node->type == EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD ||
183
0
      node->type == EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT;
184
0
  }
185
0
}
186
187
static int
188
event_filter_parse_real(const char *str, struct event_filter *filter,
189
      bool case_sensitive, const char **error_r)
190
0
{
191
0
  struct event_filter_query_internal *int_query;
192
0
  struct event_filter_parser_state state;
193
0
  int ret;
194
195
0
  i_zero(&state);
196
0
  state.input = str;
197
0
  state.len = strlen(str);
198
0
  state.pos = 0;
199
0
  state.pool = filter->pool;
200
0
  state.case_sensitive = case_sensitive;
201
202
0
  event_filter_parser_lex_init(&state.scanner);
203
0
  event_filter_parser_set_extra(&state, state.scanner);
204
205
0
  ret = event_filter_parser_parse(&state);
206
207
0
  event_filter_parser_lex_destroy(state.scanner);
208
209
0
  if ((ret == 0) && (state.output != NULL)) {
210
    /* success - non-NULL expression */
211
0
    i_assert(state.error == NULL);
212
213
0
    int_query = event_filter_get_or_alloc_internal_query(filter, NULL);
214
215
0
    add_node(filter->pool, &int_query->expr, state.output,
216
0
       EVENT_FILTER_OP_OR);
217
218
0
    filter->named_queries_only = filter->named_queries_only &&
219
0
      filter_node_requires_event_name(state.output);
220
0
  } else if (ret != 0) {
221
    /* error */
222
0
    i_assert(state.error != NULL);
223
224
0
    *error_r = state.error;
225
0
  }
226
227
  /*
228
   * Note that success with a NULL expression output is possible, but
229
   * turns into a no-op.
230
   */
231
232
0
  return (ret != 0) ? -1 : 0;
233
0
}
234
235
int event_filter_parse(const char *str, struct event_filter *filter,
236
           const char **error_r)
237
0
{
238
0
  int ret;
239
0
  T_BEGIN {
240
0
    ret = event_filter_parse_real(str, filter, FALSE, error_r);
241
0
  } T_END_PASS_STR_IF(ret < 0, error_r);
242
0
  return ret;
243
0
}
244
245
int event_filter_parse_case_sensitive(const char *str,
246
              struct event_filter *filter,
247
              const char **error_r)
248
0
{
249
0
  int ret;
250
0
  T_BEGIN {
251
0
    ret = event_filter_parse_real(str, filter, TRUE, error_r);
252
0
  } T_END_PASS_STR_IF(ret < 0, error_r);
253
0
  return ret;
254
0
}
255
256
static const char *
257
event_filter_node_find_field_exact(struct event_filter_node *node,
258
           const char *key, bool op_not, bool *op_not_r)
259
0
{
260
0
  const char *value;
261
262
0
  switch (node->op) {
263
0
  case EVENT_FILTER_OP_CMP_EQ:
264
0
    if (node->type == EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT &&
265
0
        strcmp(node->field.key, key) == 0 &&
266
0
        node->field.value_type == EVENT_FIELD_VALUE_TYPE_STR) {
267
0
      *op_not_r = op_not;
268
0
      return node->field.value.str;
269
0
    }
270
0
    break;
271
0
  case EVENT_FILTER_OP_AND:
272
0
  case EVENT_FILTER_OP_OR:
273
0
    value = event_filter_node_find_field_exact(node->children[0],
274
0
                 key, op_not, op_not_r);
275
0
    if (value != NULL)
276
0
      return value;
277
0
    value = event_filter_node_find_field_exact(node->children[1],
278
0
                 key, op_not, op_not_r);
279
0
    if (value != NULL)
280
0
      return value;
281
0
    break;
282
0
  case EVENT_FILTER_OP_NOT:
283
0
    return event_filter_node_find_field_exact(node->children[0],
284
0
                key, !op_not, op_not_r);
285
0
  default:
286
0
    break;
287
0
  }
288
0
  return NULL;
289
0
}
290
291
const char *event_filter_find_field_exact(struct event_filter *filter,
292
            const char *key, bool *op_not_r)
293
0
{
294
0
  const struct event_filter_query_internal *query;
295
0
  const char *value;
296
297
0
  array_foreach(&filter->queries, query) {
298
0
    value = event_filter_node_find_field_exact(query->expr, key, FALSE, op_not_r);
299
0
    if (value != NULL)
300
0
      return value;
301
0
  }
302
0
  return NULL;
303
0
}
304
305
bool event_filter_category_to_log_type(const char *name,
306
               enum event_filter_log_type *log_type_r)
307
0
{
308
0
  unsigned int i;
309
310
0
  for (i = 0; i < N_ELEMENTS(event_filter_log_type_map); i++) {
311
0
    if (strcmp(name, event_filter_log_type_map[i].name) == 0) {
312
0
      *log_type_r = event_filter_log_type_map[i].log_type;
313
0
      return TRUE;
314
0
    }
315
0
  }
316
0
  return FALSE;
317
0
}
318
319
const char *
320
event_filter_category_from_log_type(enum event_filter_log_type log_type)
321
0
{
322
0
  unsigned int i;
323
324
0
  for (i = 0; i < N_ELEMENTS(event_filter_log_type_map); i++) {
325
0
    if (event_filter_log_type_map[i].log_type == log_type)
326
0
      return event_filter_log_type_map[i].name;
327
0
  }
328
0
  i_unreached();
329
0
}
330
331
static struct event_filter_node *
332
clone_expr(pool_t pool, struct event_filter_node *old)
333
0
{
334
0
  struct event_filter_node *new;
335
336
0
  if (old == NULL)
337
0
    return NULL;
338
339
0
  new = p_new(pool, struct event_filter_node, 1);
340
0
  new->type = old->type;
341
0
  new->op = old->op;
342
0
  switch (old->type) {
343
0
  case EVENT_FILTER_NODE_TYPE_LOGIC:
344
0
    new->children[0] = clone_expr(pool, old->children[0]);
345
0
    new->children[1] = clone_expr(pool, old->children[1]);
346
0
    break;
347
0
  case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY:
348
0
    new->category.log_type = old->category.log_type;
349
0
    new->category.name = p_strdup(pool, old->category.name);
350
0
    new->category.ptr = old->category.ptr;
351
0
    break;
352
0
  default:
353
0
    new->field.key = p_strdup(pool, old->field.key);
354
0
    new->field.value_type = old->field.value_type;
355
356
0
    switch (old->field.value_type) {
357
0
    case EVENT_FIELD_VALUE_TYPE_STR:
358
0
      new->field.value.str = p_strdup(pool, old->field.value.str);
359
0
      break;
360
0
    case EVENT_FIELD_VALUE_TYPE_INTMAX:
361
0
      new->field.value.intmax = old->field.value.intmax;
362
0
      break;
363
0
    case EVENT_FIELD_VALUE_TYPE_TIMEVAL:
364
0
      new->field.value.timeval = old->field.value.timeval;
365
0
      break;
366
0
    case EVENT_FIELD_VALUE_TYPE_IP:
367
0
      new->field.value.ip = old->field.value.ip;
368
0
      new->field.value.ip_bits = old->field.value.ip_bits;
369
0
      break;
370
0
    case EVENT_FIELD_VALUE_TYPE_STRLIST:
371
0
      if (array_is_created(&old->field.value.strlist)) {
372
0
        const char *str;
373
0
        p_array_init(&new->field.value.strlist, pool,
374
0
               array_count(&old->field.value.strlist));
375
0
        array_foreach_elem(&old->field.value.strlist, str) {
376
0
          str = p_strdup(pool, str);
377
0
          array_push_back(&new->field.value.strlist, &str);
378
0
        }
379
0
      }
380
0
      break;
381
0
    }
382
0
  }
383
0
  new->ambiguous_unit = old->ambiguous_unit;
384
0
  new->warned_ambiguous_unit = old->warned_ambiguous_unit;
385
0
  new->warned_type_mismatch = old->warned_type_mismatch;
386
0
  new->warned_string_inequality = old->warned_string_inequality;
387
0
  new->warned_timeval_not_implemented = old->warned_timeval_not_implemented;
388
0
  new->case_sensitive = old->case_sensitive;
389
390
0
  return new;
391
0
}
392
393
static void
394
event_filter_merge_with_context_internal(struct event_filter *dest,
395
           const struct event_filter *src,
396
           void *new_context, bool with_context)
397
0
{
398
0
  const struct event_filter_query_internal *int_query;
399
400
0
  array_foreach(&src->queries, int_query) T_BEGIN {
401
0
    void *context = with_context ? new_context : int_query->context;
402
0
    struct event_filter_query_internal *new;
403
404
0
    new = event_filter_get_or_alloc_internal_query(dest, context);
405
406
0
    add_node(dest->pool, &new->expr,
407
0
       clone_expr(dest->pool, int_query->expr),
408
0
       EVENT_FILTER_OP_OR);
409
0
    dest->named_queries_only = dest->named_queries_only &&
410
0
      filter_node_requires_event_name(int_query->expr);
411
0
  } T_END;
412
0
}
413
414
bool event_filter_remove_queries_with_context(struct event_filter *filter,
415
                void *context)
416
0
{
417
0
  const struct event_filter_query_internal *int_query;
418
0
  unsigned int idx;
419
420
0
  array_foreach(&filter->queries, int_query) {
421
0
    if (int_query->context == context) {
422
0
      idx = array_foreach_idx(&filter->queries, int_query);
423
0
      array_delete(&filter->queries, idx, 1);
424
0
      return TRUE;
425
0
    }
426
0
  }
427
0
  return FALSE;
428
0
}
429
430
void event_filter_merge(struct event_filter *dest,
431
      const struct event_filter *src)
432
0
{
433
0
  event_filter_merge_with_context_internal(dest, src, NULL, FALSE);
434
0
}
435
436
void event_filter_merge_with_context(struct event_filter *dest,
437
             const struct event_filter *src,
438
             void *new_context)
439
0
{
440
0
  event_filter_merge_with_context_internal(dest, src, new_context, TRUE);
441
0
}
442
443
static void
444
event_filter_append_escaped(string_t *dest, const char *src, bool wildcard)
445
0
{
446
0
  if (!wildcard)
447
0
    str_append_escaped(dest, src, strlen(src));
448
0
  else {
449
    /* src is already escaped */
450
0
    str_append(dest, src);
451
0
  }
452
0
}
453
454
static const char *
455
event_filter_export_query_expr_op(enum event_filter_node_op op)
456
0
{
457
0
  switch (op) {
458
0
  case EVENT_FILTER_OP_AND:
459
0
  case EVENT_FILTER_OP_OR:
460
0
  case EVENT_FILTER_OP_NOT:
461
0
    i_unreached();
462
0
  case EVENT_FILTER_OP_CMP_EQ:
463
0
    return "=";
464
0
  case EVENT_FILTER_OP_CMP_GT:
465
0
    return ">";
466
0
  case EVENT_FILTER_OP_CMP_LT:
467
0
    return "<";
468
0
  case EVENT_FILTER_OP_CMP_GE:
469
0
    return ">=";
470
0
  case EVENT_FILTER_OP_CMP_LE:
471
0
    return "<=";
472
0
  }
473
474
0
  i_unreached();
475
0
}
476
477
static void
478
event_filter_export_query_expr(const struct event_filter_query_internal *query,
479
             struct event_filter_node *node,
480
             string_t *dest)
481
0
{
482
0
  switch (node->type) {
483
0
  case EVENT_FILTER_NODE_TYPE_LOGIC:
484
0
    str_append_c(dest, '(');
485
0
    switch (node->op) {
486
0
    case EVENT_FILTER_OP_AND:
487
0
      event_filter_export_query_expr(query, node->children[0], dest);
488
0
      str_append(dest, " AND ");
489
0
      event_filter_export_query_expr(query, node->children[1], dest);
490
0
      break;
491
0
    case EVENT_FILTER_OP_OR:
492
0
      event_filter_export_query_expr(query, node->children[0], dest);
493
0
      str_append(dest, " OR ");
494
0
      event_filter_export_query_expr(query, node->children[1], dest);
495
0
      break;
496
0
    case EVENT_FILTER_OP_NOT:
497
0
      str_append(dest, "NOT ");
498
0
      event_filter_export_query_expr(query, node->children[0], dest);
499
0
      break;
500
0
    case EVENT_FILTER_OP_CMP_EQ:
501
0
    case EVENT_FILTER_OP_CMP_GT:
502
0
    case EVENT_FILTER_OP_CMP_LT:
503
0
    case EVENT_FILTER_OP_CMP_GE:
504
0
    case EVENT_FILTER_OP_CMP_LE:
505
0
      i_unreached();
506
0
    }
507
0
    str_append_c(dest, ')');
508
0
    break;
509
0
  case EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT:
510
0
  case EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD:
511
0
    str_append(dest, "event");
512
0
    str_append(dest, event_filter_export_query_expr_op(node->op));
513
0
    str_append_c(dest, '"');
514
0
    event_filter_append_escaped(dest, node->field.value.str,
515
0
      node->type == EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD);
516
0
    str_append_c(dest, '"');
517
0
    break;
518
0
  case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION:
519
0
    str_append(dest, "source_location");
520
0
    str_append(dest, event_filter_export_query_expr_op(node->op));
521
0
    str_append_c(dest, '"');
522
0
    event_filter_append_escaped(dest, node->field.value.str, FALSE);
523
0
    str_append_c(dest, '"');
524
0
    break;
525
0
  case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY:
526
0
    str_append(dest, "category");
527
0
    str_append(dest, event_filter_export_query_expr_op(node->op));
528
0
    if (node->category.name != NULL) {
529
0
      str_append_c(dest, '"');
530
0
      event_filter_append_escaped(dest, node->category.name, FALSE);
531
0
      str_append_c(dest, '"');
532
0
    } else
533
0
      str_append(dest, event_filter_category_from_log_type(node->category.log_type));
534
0
    break;
535
0
  case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT:
536
0
  case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD:
537
0
  case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_NUMERIC_WILDCARD:
538
0
    str_append_c(dest, '"');
539
0
    event_filter_append_escaped(dest, node->field.key, FALSE);
540
0
    str_append_c(dest, '"');
541
0
    str_append(dest, event_filter_export_query_expr_op(node->op));
542
0
    str_append_c(dest, '"');
543
0
    event_filter_append_escaped(dest,
544
0
      wanted_field_value_str(&node->field),
545
0
      node->type != EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT);
546
0
    str_append_c(dest, '"');
547
0
    break;
548
0
  }
549
0
}
550
551
static void
552
event_filter_export_query(const struct event_filter_query_internal *query,
553
        string_t *dest)
554
0
{
555
0
  str_append_c(dest, '(');
556
0
  event_filter_export_query_expr(query, query->expr, dest);
557
0
  str_append_c(dest, ')');
558
0
}
559
560
void event_filter_export(struct event_filter *filter, string_t *dest)
561
0
{
562
0
  const struct event_filter_query_internal *query;
563
0
  bool first = TRUE;
564
565
0
  array_foreach(&filter->queries, query) {
566
0
    if (!first)
567
0
      str_append(dest, " OR ");
568
0
    first = FALSE;
569
0
    event_filter_export_query(query, dest);
570
0
  }
571
0
}
572
573
struct event_filter_node *
574
event_filter_get_expr_for_testing(struct event_filter *filter,
575
          unsigned int *count_r)
576
0
{
577
0
  const struct event_filter_query_internal *queries;
578
579
0
  queries = array_get(&filter->queries, count_r);
580
581
0
  return (*count_r == 0) ? NULL : queries[0].expr;
582
0
}
583
584
static bool
585
event_category_match(const struct event_category *category,
586
         const struct event_category *wanted_category)
587
0
{
588
0
  for (; category != NULL; category = category->parent) {
589
0
    if (category->internal == wanted_category->internal)
590
0
      return TRUE;
591
0
  }
592
0
  return FALSE;
593
0
}
594
595
static bool
596
event_has_category_nonrecursive(struct event *event,
597
        struct event_category *wanted_category)
598
0
{
599
0
  struct event_category *cat;
600
601
0
  if (array_is_created(&event->categories)) {
602
0
    array_foreach_elem(&event->categories, cat) {
603
0
      if (event_category_match(cat, wanted_category))
604
0
        return TRUE;
605
0
    }
606
0
  }
607
0
  return FALSE;
608
0
}
609
610
static bool
611
event_has_category(struct event *event, struct event_filter_node *node,
612
       enum event_filter_log_type log_type)
613
0
{
614
0
  struct event_category *wanted_category = node->category.ptr;
615
616
  /* category is a log type */
617
0
  if (node->category.name == NULL)
618
0
    return (node->category.log_type & log_type) != 0;
619
620
  /* category not registered, therefore the event cannot have it */
621
0
  if (wanted_category == NULL)
622
0
    return FALSE;
623
624
0
  while (event != NULL) {
625
0
    if (event_has_category_nonrecursive(event, wanted_category))
626
0
      return TRUE;
627
    /* try also the parent events */
628
0
    event = event_get_parent(event);
629
0
  }
630
  /* check also the global event and its parents */
631
0
  event = event_get_global();
632
0
  while (event != NULL) {
633
0
    if (event_has_category_nonrecursive(event, wanted_category))
634
0
      return TRUE;
635
0
    event = event_get_parent(event);
636
0
  }
637
0
  return FALSE;
638
0
}
639
640
static bool
641
cmp_str(const char *value, const char *wanted_value, enum cmp_flags flags)
642
0
{
643
0
  if ((flags & CMP_FLAG_WILDCARD) != 0) {
644
0
    if ((flags & CMP_FLAG_CASE_SENSITIVE) != 0)
645
0
      return wildcard_match_escaped(value, wanted_value);
646
0
    else
647
0
      return wildcard_match_escaped_icase(value, wanted_value);
648
0
  } else {
649
0
    if ((flags & CMP_FLAG_CASE_SENSITIVE) != 0)
650
0
      return strcmp(value, wanted_value) == 0;
651
0
    else
652
0
      return strcasecmp(value, wanted_value) == 0;
653
0
  }
654
0
}
655
656
static bool
657
cmp_value(const char *value, const struct event_field *wanted_field,
658
    enum cmp_flags flags)
659
0
{
660
0
  return cmp_str(value, wanted_field_value_str(wanted_field), flags);
661
0
}
662
663
static bool
664
event_match_strlist_recursive(struct event *event,
665
            const char *wanted_key, const char *wanted_value,
666
            enum cmp_flags cmp_flags, bool *seen)
667
0
{
668
0
  const struct event_field *field;
669
0
  const char *value;
670
671
0
  if (event == NULL)
672
0
    return FALSE;
673
674
0
  field = event_find_field_nonrecursive(event, wanted_key);
675
0
  if (field != NULL) {
676
0
    i_assert(field->value_type == EVENT_FIELD_VALUE_TYPE_STRLIST);
677
0
    array_foreach_elem(&field->value.strlist, value) {
678
0
      *seen = TRUE;
679
0
      if (cmp_str(value, wanted_value, cmp_flags))
680
0
        return TRUE;
681
0
    }
682
0
  }
683
0
  return event_match_strlist_recursive(event->parent, wanted_key,
684
0
               wanted_value, cmp_flags, seen);
685
0
}
686
687
static bool
688
event_match_strlist(struct event *event, const struct event_field *wanted_field,
689
        enum cmp_flags cmp_flags)
690
0
{
691
0
  const char *wanted_value = wanted_field_value_str(wanted_field);
692
0
  bool seen = FALSE;
693
694
0
  if (event_match_strlist_recursive(event, wanted_field->key, wanted_value,
695
0
            cmp_flags, &seen))
696
0
    return TRUE;
697
0
  if (event_match_strlist_recursive(event_get_global(),
698
0
            wanted_field->key, wanted_value,
699
0
            cmp_flags, &seen))
700
0
    return TRUE;
701
0
  if (wanted_value[0] == '\0' && !seen) {
702
    /* strlist="" matches nonexistent strlist */
703
0
    return TRUE;
704
0
  }
705
0
  return FALSE;
706
707
0
}
708
709
static bool
710
event_filter_handle_numeric_operation(enum event_filter_node_op op,
711
              intmax_t a, intmax_t b)
712
0
{
713
0
  switch (op) {
714
0
  case EVENT_FILTER_OP_CMP_EQ:
715
0
    return a == b;
716
0
  case EVENT_FILTER_OP_CMP_GT:
717
0
    return a > b;
718
0
  case EVENT_FILTER_OP_CMP_LT:
719
0
    return a < b;
720
0
  case EVENT_FILTER_OP_CMP_GE:
721
0
    return a >= b;
722
0
  case EVENT_FILTER_OP_CMP_LE:
723
0
    return a <= b;
724
0
  case EVENT_FILTER_OP_AND:
725
0
  case EVENT_FILTER_OP_OR:
726
0
  case EVENT_FILTER_OP_NOT:
727
0
    i_unreached();
728
0
  }
729
0
  i_unreached();
730
0
}
731
732
static bool
733
event_match_field(struct event *event, struct event_filter_node *node,
734
      bool use_strcmp, const char *source_filename,
735
      unsigned int source_linenum)
736
0
{
737
0
  const struct event_field *field;
738
0
  struct event_field duration;
739
0
  bool ret;
740
741
0
  const struct event_field *wanted_field = &node->field;
742
0
  if (strcmp(wanted_field->key, "duration") == 0) {
743
0
    uintmax_t duration_value;
744
0
    i_zero(&duration);
745
0
    duration.key = "duration";
746
0
    duration.value_type = EVENT_FIELD_VALUE_TYPE_INTMAX;
747
0
    event_get_last_duration(event, &duration_value);
748
0
    duration.value.intmax = (intmax_t)duration_value;
749
0
    field = &duration;
750
0
  } else {
751
    /* wanted_field has the value in all available formats */
752
0
    field = event_find_field_recursive(event, wanted_field->key);
753
0
  }
754
0
  if (field == NULL) {
755
    /* field="" matches nonexistent field */
756
0
    return wanted_field->value_type == EVENT_FIELD_VALUE_TYPE_STR &&
757
0
      wanted_field->value.str[0] == '\0';
758
0
  }
759
0
  enum cmp_flags cmp_flags = 0;
760
0
  if (!use_strcmp)
761
0
    cmp_flags |= CMP_FLAG_WILDCARD;
762
0
  if (node->case_sensitive)
763
0
    cmp_flags |= CMP_FLAG_CASE_SENSITIVE;
764
765
0
  switch (field->value_type) {
766
0
  case EVENT_FIELD_VALUE_TYPE_STR:
767
    /* We only support string equality comparisons. */
768
0
    if (node->op != EVENT_FILTER_OP_CMP_EQ) {
769
      /* Warn about non-equality comparisons. */
770
0
      if (!node->warned_string_inequality) {
771
0
        const char *name = event->sending_name;
772
        /* Use i_warning to prevent event filter recursions. */
773
0
        i_warning("Event filter for string field '%s' "
774
0
            "only supports equality operation "
775
0
            "'=' not '%s'. (event=%s, source=%s:%u)",
776
0
            wanted_field->key,
777
0
            event_filter_export_query_expr_op(node->op),
778
0
            name != NULL ? name : "",
779
0
            source_filename, source_linenum);
780
0
        node->warned_string_inequality = TRUE;
781
0
      }
782
0
      return FALSE;
783
0
    }
784
0
    if (field->value.str[0] == '\0') {
785
      /* field was removed, but it matches field="" filter */
786
0
      return wanted_field->value_type == EVENT_FIELD_VALUE_TYPE_STR &&
787
0
        wanted_field->value.str[0] == '\0';
788
0
    }
789
0
    T_BEGIN {
790
0
      ret = cmp_value(field->value.str, wanted_field, cmp_flags);
791
0
    } T_END;
792
0
    return ret;
793
0
  case EVENT_FIELD_VALUE_TYPE_INTMAX:
794
0
    if (node->ambiguous_unit) {
795
0
      if (!node->warned_ambiguous_unit) {
796
0
        const char *name = event->sending_name;
797
        /* Use i_warning to prevent event filter recursions. */
798
0
        i_warning("Event filter matches integer field "
799
0
            "'%s' with value that has an "
800
0
            "ambiguous unit '%s'. Please use "
801
0
            "either 'mins' or 'MB' to specify "
802
0
            "interval or size respectively. "
803
0
            "(event=%s, source=%s:%u)",
804
0
            wanted_field->key,
805
0
            wanted_field_value_str(wanted_field),
806
0
            name != NULL ? name : "",
807
0
            source_filename, source_linenum);
808
0
        node->warned_ambiguous_unit = TRUE;
809
0
      }
810
0
      return FALSE;
811
0
    } else if (wanted_field->value_type == EVENT_FIELD_VALUE_TYPE_INTMAX) {
812
      /* compare against an integer */
813
0
      return event_filter_handle_numeric_operation(
814
0
        node->op, field->value.intmax, wanted_field->value.intmax);
815
0
    } else if ((cmp_flags & CMP_FLAG_WILDCARD) == 0 ||
816
0
         (node->type != EVENT_FILTER_NODE_TYPE_EVENT_FIELD_NUMERIC_WILDCARD)) {
817
0
      if (!node->warned_type_mismatch) {
818
0
        const char *name = event->sending_name;
819
        /* Use i_warning to prevent event filter recursions. */
820
0
        i_warning("Event filter matches integer field "
821
0
            "'%s' against non-integer value '%s'. "
822
0
            "(event=%s, source=%s:%u)",
823
0
            wanted_field->key,
824
0
            wanted_field_value_str(wanted_field),
825
0
            name != NULL ? name : "",
826
0
            source_filename, source_linenum);
827
0
        node->warned_type_mismatch = TRUE;
828
0
      }
829
0
      return FALSE;
830
0
    } else if (node->op != EVENT_FILTER_OP_CMP_EQ) {
831
      /* In this branch a numeric value is matched against a
832
         wildcard, which requires an equality operation. */
833
0
      if (!node->warned_type_mismatch) {
834
0
        const char *name = event->sending_name;
835
        /* Use i_warning to prevent event filter recursions. */
836
0
        i_warning("Event filter matches integer field "
837
0
            "'%s' against wildcard value '%s' "
838
0
            "with an incompatible operation '%s', "
839
0
            "please use '='. (event=%s, "
840
0
            "source=%s:%u)",
841
0
            wanted_field->key,
842
0
            wanted_field_value_str(wanted_field),
843
0
            event_filter_export_query_expr_op(node->op),
844
0
            name != NULL ? name : "",
845
0
            source_filename, source_linenum);
846
0
        node->warned_type_mismatch = TRUE;
847
0
      }
848
0
      return FALSE;
849
0
    } else {
850
0
      char tmp[MAX_INT_STRLEN];
851
0
      i_snprintf(tmp, sizeof(tmp), "%jd", field->value.intmax);
852
0
      T_BEGIN {
853
0
        ret = wildcard_match_escaped_icase(tmp,
854
0
          wanted_field_value_str(wanted_field));
855
0
      } T_END;
856
0
      return ret;
857
0
    }
858
0
  case EVENT_FIELD_VALUE_TYPE_TIMEVAL: {
859
    /* Filtering for timeval fields is not implemented. */
860
0
    if (!node->warned_timeval_not_implemented) {
861
0
         const char *name = event->sending_name;
862
0
         i_warning("Event filter for timeval field '%s' is "
863
0
             "currently not implemented. (event=%s, "
864
0
             "source=%s:%u)",
865
0
             wanted_field->key, name != NULL ? name : "",
866
0
             source_filename, source_linenum);
867
0
      node->warned_timeval_not_implemented = TRUE;
868
0
    }
869
0
    return FALSE;
870
0
  }
871
0
  case EVENT_FIELD_VALUE_TYPE_IP:
872
0
    if (node->op != EVENT_FILTER_OP_CMP_EQ) {
873
      /* we only support IP equality comparisons */
874
0
      if (!node->warned_ip_inequality) {
875
0
        const char *name = event->sending_name;
876
        /* Use i_warning to prevent event filter recursions. */
877
0
        i_warning("Event filter for IP field '%s' "
878
0
            "only supports equality operation "
879
0
            "'=' not '%s'. (event=%s, source=%s:%u)",
880
0
            wanted_field->key,
881
0
            event_filter_export_query_expr_op(node->op),
882
0
            name != NULL ? name : "",
883
0
            source_filename, source_linenum);
884
0
        node->warned_ip_inequality = TRUE;
885
0
      }
886
0
      return FALSE;
887
0
    }
888
0
    if (wanted_field->value_type == EVENT_FIELD_VALUE_TYPE_IP) {
889
0
      return net_is_in_network(&field->value.ip,
890
0
             &wanted_field->value.ip,
891
0
             wanted_field->value.ip_bits);
892
0
    }
893
0
    if ((cmp_flags & CMP_FLAG_WILDCARD) == 0) {
894
      /* If the matched value was a number, it was already
895
         matched in the previous branch. So here we have a
896
         non-wildcard IP, which can never be a match to an
897
         IP. */
898
0
      if (!node->warned_type_mismatch) {
899
0
        const char *name = event->sending_name;
900
        /* Use i_warning to prevent event filter recursions. */
901
0
        i_warning("Event filter matches IP field "
902
0
            "'%s' against non-IP value '%s'. "
903
0
            "(event=%s, source=%s:%u)",
904
0
            wanted_field->key,
905
0
            wanted_field_value_str(wanted_field),
906
0
            name != NULL ? name : "",
907
0
            source_filename, source_linenum);
908
0
        node->warned_type_mismatch = TRUE;
909
0
      }
910
0
      return FALSE;
911
0
    }
912
0
    T_BEGIN {
913
0
      ret = wildcard_match_escaped_icase(net_ip2addr(&field->value.ip),
914
0
                 wanted_field_value_str(wanted_field));
915
0
    } T_END;
916
0
    return ret;
917
0
  case EVENT_FIELD_VALUE_TYPE_STRLIST:
918
    /* check if the value is (or is not) on the list,
919
       only string matching makes sense here. */
920
0
    if (node->op != EVENT_FILTER_OP_CMP_EQ) {
921
0
      if (!node->warned_string_inequality) {
922
0
        const char *name = event->sending_name;
923
0
        i_warning("Event filter for string list field "
924
0
            "'%s' only supports equality "
925
0
            "operation '=' not '%s'. (event=%s, "
926
0
            "source=%s:%u)",
927
0
            wanted_field->key,
928
0
            event_filter_export_query_expr_op(node->op),
929
0
            name != NULL ? name : "",
930
0
            source_filename, source_linenum);
931
0
        node->warned_string_inequality = TRUE;
932
0
      }
933
0
      return FALSE;
934
0
    }
935
0
    T_BEGIN {
936
0
      ret = event_match_strlist(event, wanted_field, cmp_flags);
937
0
    } T_END;
938
0
    return ret;
939
0
  }
940
0
  i_unreached();
941
0
}
942
943
static bool
944
event_filter_query_match_cmp(struct event_filter_node *node,
945
           struct event *event, const char *source_filename,
946
           unsigned int source_linenum,
947
           enum event_filter_log_type log_type)
948
0
{
949
0
  i_assert((node->op == EVENT_FILTER_OP_CMP_EQ) ||
950
0
     (node->op == EVENT_FILTER_OP_CMP_GT) ||
951
0
     (node->op == EVENT_FILTER_OP_CMP_LT) ||
952
0
     (node->op == EVENT_FILTER_OP_CMP_GE) ||
953
0
     (node->op == EVENT_FILTER_OP_CMP_LE));
954
955
0
  switch (node->type) {
956
0
    case EVENT_FILTER_NODE_TYPE_LOGIC:
957
0
      i_unreached();
958
0
    case EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT:
959
0
      return (event->sending_name != NULL) &&
960
0
             strcmp(event->sending_name, node->field.value.str) == 0;
961
0
    case EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD:
962
0
      return (event->sending_name != NULL) &&
963
0
        wildcard_match_escaped(event->sending_name,
964
0
                   node->field.value.str);
965
0
    case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION: {
966
0
      bool ret;
967
0
      if (strchr(node->field.value.str, ':') == NULL)
968
0
        ret = strcmp(node->field.value.str, source_filename) == 0;
969
0
      else T_BEGIN {
970
0
        const char *wanted_str =
971
0
          t_strdup_printf("%s:%u", source_filename, source_linenum);
972
0
        ret = strcmp(node->field.value.str, wanted_str) == 0;
973
0
      } T_END;
974
0
      return ret;
975
0
    }
976
0
    case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY:
977
0
      return event_has_category(event, node, log_type);
978
0
    case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT:
979
0
      return event_match_field(event, node, TRUE, source_filename, source_linenum);
980
0
    case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD:
981
0
    case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_NUMERIC_WILDCARD:
982
0
      return event_match_field(event, node, FALSE, source_filename, source_linenum);
983
0
  }
984
985
0
  i_unreached();
986
0
}
987
988
bool
989
event_filter_query_match_eval(struct event_filter_node *node,
990
            struct event *event, const char *source_filename,
991
            unsigned int source_linenum,
992
            enum event_filter_log_type log_type)
993
0
{
994
0
  switch (node->op) {
995
0
  case EVENT_FILTER_OP_CMP_EQ:
996
0
  case EVENT_FILTER_OP_CMP_GT:
997
0
  case EVENT_FILTER_OP_CMP_LT:
998
0
  case EVENT_FILTER_OP_CMP_GE:
999
0
  case EVENT_FILTER_OP_CMP_LE:
1000
0
    return event_filter_query_match_cmp(node, event, source_filename,
1001
0
                source_linenum, log_type);
1002
0
  case EVENT_FILTER_OP_AND:
1003
0
    return event_filter_query_match_eval(node->children[0], event,
1004
0
                 source_filename, source_linenum,
1005
0
                 log_type) &&
1006
0
           event_filter_query_match_eval(node->children[1], event,
1007
0
                 source_filename, source_linenum,
1008
0
                 log_type);
1009
0
  case EVENT_FILTER_OP_OR:
1010
0
    return event_filter_query_match_eval(node->children[0], event,
1011
0
                 source_filename, source_linenum,
1012
0
                 log_type) ||
1013
0
           event_filter_query_match_eval(node->children[1], event,
1014
0
                 source_filename, source_linenum,
1015
0
                 log_type);
1016
0
  case EVENT_FILTER_OP_NOT:
1017
0
    return !event_filter_query_match_eval(node->children[0], event,
1018
0
                  source_filename, source_linenum,
1019
0
                  log_type);
1020
0
  }
1021
1022
0
  i_unreached();
1023
0
}
1024
1025
static bool
1026
event_filter_query_match(const struct event_filter_query_internal *query,
1027
       struct event *event, const char *source_filename,
1028
       unsigned int source_linenum,
1029
       const struct failure_context *ctx)
1030
0
{
1031
0
  enum event_filter_log_type log_type;
1032
1033
0
  i_assert(ctx->type < N_ELEMENTS(event_filter_log_type_map));
1034
0
  log_type = event_filter_log_type_map[ctx->type].log_type;
1035
1036
0
  return event_filter_query_match_eval(query->expr, event, source_filename,
1037
0
               source_linenum, log_type);
1038
0
}
1039
1040
static bool
1041
event_filter_match_fastpath(struct event_filter *filter, struct event *event)
1042
0
{
1043
0
  if (filter->named_queries_only && event->sending_name == NULL) {
1044
    /* No debug logging is enabled. Only named events may be wanted
1045
       for stats. This event doesn't have a name, so we don't need
1046
       to check any further. */
1047
0
    return FALSE;
1048
0
  }
1049
0
  return TRUE;
1050
0
}
1051
1052
bool event_filter_match(struct event_filter *filter, struct event *event,
1053
      const struct failure_context *ctx)
1054
0
{
1055
0
  if (filter == NULL)
1056
0
    return FALSE;
1057
0
  return event_filter_match_source(filter, event, event->source_filename,
1058
0
           event->source_linenum, ctx);
1059
0
}
1060
1061
bool event_filter_match_source(struct event_filter *filter, struct event *event,
1062
             const char *source_filename,
1063
             unsigned int source_linenum,
1064
             const struct failure_context *ctx)
1065
0
{
1066
0
  const struct event_filter_query_internal *query;
1067
1068
0
  i_assert(!filter->fragment);
1069
1070
0
  if (!event_filter_match_fastpath(filter, event))
1071
0
    return FALSE;
1072
1073
0
  array_foreach(&filter->queries, query) {
1074
0
    if (event_filter_query_match(query, event, source_filename,
1075
0
               source_linenum, ctx))
1076
0
      return TRUE;
1077
0
  }
1078
0
  return FALSE;
1079
0
}
1080
1081
struct event_filter_match_iter {
1082
  struct event_filter *filter;
1083
  struct event *event;
1084
  const struct failure_context *failure_ctx;
1085
  unsigned int idx;
1086
};
1087
1088
struct event_filter_match_iter *
1089
event_filter_match_iter_init(struct event_filter *filter, struct event *event,
1090
           const struct failure_context *ctx)
1091
0
{
1092
0
  struct event_filter_match_iter *iter;
1093
1094
0
  i_assert(!filter->fragment);
1095
1096
0
  iter = i_new(struct event_filter_match_iter, 1);
1097
0
  iter->filter = filter;
1098
0
  iter->event = event;
1099
0
  iter->failure_ctx = ctx;
1100
0
  if (!event_filter_match_fastpath(filter, event))
1101
0
    iter->idx = UINT_MAX;
1102
0
  return iter;
1103
0
}
1104
1105
void *event_filter_match_iter_next(struct event_filter_match_iter *iter)
1106
0
{
1107
0
  const struct event_filter_query_internal *queries;
1108
0
  unsigned int count;
1109
1110
0
  queries = array_get(&iter->filter->queries, &count);
1111
0
  while (iter->idx < count) {
1112
0
    const struct event_filter_query_internal *query =
1113
0
      &queries[iter->idx];
1114
1115
0
    iter->idx++;
1116
0
    if (query->context != NULL &&
1117
0
        event_filter_query_match(query, iter->event,
1118
0
               iter->event->source_filename,
1119
0
               iter->event->source_linenum,
1120
0
               iter->failure_ctx))
1121
0
      return query->context;
1122
0
  }
1123
0
  return NULL;
1124
0
}
1125
1126
void event_filter_match_iter_deinit(struct event_filter_match_iter **_iter)
1127
0
{
1128
0
  struct event_filter_match_iter *iter = *_iter;
1129
1130
0
  *_iter = NULL;
1131
0
  i_free(iter);
1132
0
}
1133
1134
static void
1135
event_filter_query_update_category(struct event_filter_query_internal *query,
1136
           struct event_filter_node *node,
1137
           struct event_category *category,
1138
           bool add)
1139
0
{
1140
0
  if (node == NULL)
1141
0
    return;
1142
1143
0
  switch (node->type) {
1144
0
  case EVENT_FILTER_NODE_TYPE_LOGIC:
1145
0
    event_filter_query_update_category(query, node->children[0], category, add);
1146
0
    event_filter_query_update_category(query, node->children[1], category, add);
1147
0
    break;
1148
0
  case EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT:
1149
0
  case EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD:
1150
0
  case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION:
1151
0
  case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT:
1152
0
  case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD:
1153
0
  case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_NUMERIC_WILDCARD:
1154
0
    break;
1155
0
  case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY:
1156
0
    if (node->category.name == NULL)
1157
0
      break; /* log type */
1158
1159
0
    if (add) {
1160
0
      if (node->category.ptr != NULL)
1161
0
        break;
1162
1163
0
      if (strcmp(node->category.name, category->name) == 0)
1164
0
        node->category.ptr = category;
1165
0
    } else {
1166
0
      if (node->category.ptr == category)
1167
0
        node->category.ptr = NULL;
1168
0
    }
1169
0
    break;
1170
0
  }
1171
0
}
1172
1173
static void event_filter_category_registered(struct event_category *category)
1174
1
{
1175
1
  const bool add = category->internal != NULL;
1176
1
  struct event_filter_query_internal *query;
1177
1
  struct event_filter *filter;
1178
1179
1
  for (filter = event_filters; filter != NULL; filter = filter->next) {
1180
0
    array_foreach_modifiable(&filter->queries, query) {
1181
0
      event_filter_query_update_category(query, query->expr,
1182
0
                 category, add);
1183
0
    }
1184
0
  }
1185
1
}
1186
1187
void event_filter_init(void)
1188
5
{
1189
5
  event_category_register_callback(event_filter_category_registered);
1190
5
}
1191
1192
void event_filter_deinit(void)
1193
0
{
1194
0
  event_category_unregister_callback(event_filter_category_registered);
1195
0
}