Coverage Report

Created: 2026-08-13 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/haproxy/src/acl.c
Line
Count
Source
1
/*
2
 * ACL management functions.
3
 *
4
 * Copyright 2000-2013 Willy Tarreau <w@1wt.eu>
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version
9
 * 2 of the License, or (at your option) any later version.
10
 *
11
 */
12
13
#include <ctype.h>
14
#include <stdio.h>
15
#include <string.h>
16
17
#include <import/ebsttree.h>
18
19
#include <haproxy/acl.h>
20
#include <haproxy/api.h>
21
#include <haproxy/arg.h>
22
#include <haproxy/auth.h>
23
#include <haproxy/errors.h>
24
#include <haproxy/global.h>
25
#include <haproxy/list.h>
26
#include <haproxy/pattern.h>
27
#include <haproxy/proxy-t.h>
28
#include <haproxy/sample.h>
29
#include <haproxy/stick_table.h>
30
#include <haproxy/tools.h>
31
#include <haproxy/cfgparse.h>
32
33
/* List head of all known ACL keywords */
34
static struct acl_kw_list acl_keywords = {
35
  .list = LIST_HEAD_INIT(acl_keywords.list)
36
};
37
38
/* input values are 0 or 3, output is the same */
39
static inline enum acl_test_res pat2acl(struct pattern *pat)
40
0
{
41
0
  if (pat)
42
0
    return ACL_TEST_PASS;
43
0
  else
44
0
    return ACL_TEST_FAIL;
45
0
}
46
47
/*
48
 * Registers the ACL keyword list <kwl> as a list of valid keywords for next
49
 * parsing sessions.
50
 */
51
void acl_register_keywords(struct acl_kw_list *kwl)
52
0
{
53
0
  LIST_APPEND(&acl_keywords.list, &kwl->list);
54
0
}
55
56
/*
57
 * Unregisters the ACL keyword list <kwl> from the list of valid keywords.
58
 */
59
void acl_unregister_keywords(struct acl_kw_list *kwl)
60
0
{
61
0
  LIST_DELETE(&kwl->list);
62
0
  LIST_INIT(&kwl->list);
63
0
}
64
65
/* Return a pointer to the ACL <name> within the list starting at <head>, or
66
 * NULL if not found.
67
 */
68
struct acl *find_acl_by_name(const char *name, struct list *head)
69
0
{
70
0
  struct acl *acl;
71
0
  list_for_each_entry(acl, head, list) {
72
0
    if (strcmp(acl->name, name) == 0)
73
0
      return acl;
74
0
  }
75
0
  return NULL;
76
0
}
77
78
/* Return a pointer to the ACL keyword <kw>, or NULL if not found. Note that if
79
 * <kw> contains an opening parenthesis or a comma, only the left part of it is
80
 * checked.
81
 */
82
struct acl_keyword *find_acl_kw(const char *kw)
83
0
{
84
0
  int index;
85
0
  const char *kwend;
86
0
  struct acl_kw_list *kwl;
87
88
0
  kwend = kw;
89
0
  while (is_idchar(*kwend))
90
0
    kwend++;
91
92
0
  list_for_each_entry(kwl, &acl_keywords.list, list) {
93
0
    for (index = 0; kwl->kw[index].kw != NULL; index++) {
94
0
      if ((strncmp(kwl->kw[index].kw, kw, kwend - kw) == 0) &&
95
0
          kwl->kw[index].kw[kwend-kw] == 0)
96
0
        return &kwl->kw[index];
97
0
    }
98
0
  }
99
0
  return NULL;
100
0
}
101
102
static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
103
0
{
104
0
  struct arg *arg;
105
106
0
  pattern_prune(&expr->pat);
107
108
0
  for (arg = expr->smp->arg_p; arg; arg++) {
109
0
    if (arg->type == ARGT_STOP)
110
0
      break;
111
0
    if (arg->type == ARGT_STR || arg->unresolved) {
112
0
      chunk_destroy(&arg->data.str);
113
0
      arg->unresolved = 0;
114
0
    }
115
0
  }
116
117
0
  release_sample_expr(expr->smp);
118
119
0
  return expr;
120
0
}
121
122
/* Parse an ACL expression starting at <args>[0], and return it. If <err> is
123
 * not NULL, it will be filled with a pointer to an error message in case of
124
 * error. This pointer must be freeable or NULL. <al> is an arg_list serving
125
 * as a list head to report missing dependencies. It may be NULL if such
126
 * dependencies are not allowed.
127
 *
128
 * Right now, the only accepted syntax is :
129
 * <subject> [<value>...]
130
 */
131
struct acl_expr *parse_acl_expr(const char **args, char **err, struct arg_list *al,
132
                                const char *file, int line)
133
0
{
134
0
  __label__ out_return, out_free_expr;
135
0
  struct acl_expr *expr;
136
0
  struct acl_keyword *aclkw;
137
0
  int refflags, patflags;
138
0
  const char *arg;
139
0
  struct sample_expr *smp = NULL;
140
0
  int idx = 0;
141
0
  const char *endt;
142
0
  int cur_type;
143
0
  int nbargs;
144
0
  int operator = STD_OP_EQ;
145
0
  int op;
146
0
  int contain_colon, have_dot;
147
0
  const char *dot;
148
0
  signed long long value, minor;
149
  /* The following buffer contain two numbers, a ':' separator and the final \0. */
150
0
  char buffer[NB_LLMAX_STR + 1 + NB_LLMAX_STR + 1];
151
0
  int is_loaded, match_forced;
152
0
  int unique_id;
153
0
  char *error;
154
0
  struct pat_ref *ref;
155
0
  struct pattern_expr *pattern_expr;
156
0
  int load_as_map = 0;
157
0
  int acl_conv_found = 0;
158
159
  /* First, we look for an ACL keyword. And if we don't find one, then
160
   * we look for a sample fetch expression starting with a sample fetch
161
   * keyword.
162
   */
163
164
0
  if (al) {
165
0
    al->ctx  = ARGC_ACL;   // to report errors while resolving args late
166
0
    al->conv = NULL;
167
0
  }
168
169
0
  aclkw = find_acl_kw(args[0]);
170
0
  if (aclkw) {
171
    /* OK we have a real ACL keyword */
172
173
0
    if (al)
174
0
      al->kw = aclkw->kw;
175
176
    /* build new sample expression for this ACL */
177
0
    smp = calloc(1, sizeof(*smp));
178
0
    if (!smp) {
179
0
      memprintf(err, "out of memory when parsing ACL expression");
180
0
      goto out_return;
181
0
    }
182
0
    LIST_INIT(&(smp->conv_exprs));
183
0
    smp->fetch = aclkw->smp;
184
0
    smp->arg_p = empty_arg_list;
185
186
    /* look for the beginning of the subject arguments */
187
0
    for (arg = args[0]; is_idchar(*arg); arg++)
188
0
      ;
189
190
    /* At this point, we have :
191
     *   - args[0] : beginning of the keyword
192
     *   - arg     : end of the keyword, first character not part of keyword
193
     */
194
0
    nbargs = make_arg_list(arg, -1, smp->fetch->arg_mask, &smp->arg_p,
195
0
                           err, &endt, NULL, al);
196
0
    if (nbargs < 0) {
197
      /* note that make_arg_list will have set <err> here */
198
0
      memprintf(err, "ACL keyword '%s' : %s", aclkw->kw, *err);
199
0
      goto out_free_smp;
200
0
    }
201
202
0
    if (!smp->arg_p) {
203
0
      smp->arg_p = empty_arg_list;
204
0
    }
205
0
    else if (smp->fetch->val_args && !smp->fetch->val_args(smp->arg_p, err)) {
206
      /* invalid keyword argument, error must have been
207
       * set by val_args().
208
       */
209
0
      memprintf(err, "in argument to '%s', %s", aclkw->kw, *err);
210
0
      goto out_free_smp;
211
0
    }
212
213
    /* look for the beginning of the converters list. Those directly attached
214
     * to the ACL keyword are found just after the comma.
215
     * If we find any converter, then we don't use the ACL keyword's match
216
     * anymore but the one related to the converter's output type.
217
     */
218
0
    if (!sample_parse_expr_cnv((char **)args, NULL, NULL, err, al, file, line, smp, endt)) {
219
0
      if (err)
220
0
        memprintf(err, "ACL keyword '%s' : %s", aclkw->kw, *err);
221
0
      goto out_free_smp;
222
0
    }
223
0
    acl_conv_found = !LIST_ISEMPTY(&smp->conv_exprs);
224
0
  }
225
0
  else {
226
    /* This is not an ACL keyword, so we hope this is a sample fetch
227
     * keyword that we're going to transparently use as an ACL. If
228
     * so, we retrieve a completely parsed expression with args and
229
     * convs already done.
230
     */
231
0
    smp = sample_parse_expr((char **)args, &idx, file, line, err, al, NULL);
232
0
    if (!smp) {
233
0
      memprintf(err, "%s in ACL expression '%s'", *err, *args);
234
0
      goto out_return;
235
0
    }
236
0
  }
237
238
  /* get last effective output type for smp */
239
0
  cur_type = smp_expr_output_type(smp);
240
241
0
  expr = calloc(1, sizeof(*expr));
242
0
  if (!expr) {
243
0
    memprintf(err, "out of memory when parsing ACL expression");
244
0
    goto out_free_smp;
245
0
  }
246
247
0
  pattern_init_head(&expr->pat);
248
249
0
  expr->pat.expect_type = cur_type;
250
0
  expr->smp             = smp;
251
0
  expr->kw              = smp->fetch->kw;
252
0
  smp = NULL; /* don't free it anymore */
253
254
0
  if (aclkw && !acl_conv_found) {
255
0
    expr->kw = aclkw->kw;
256
0
    expr->pat.parse  = aclkw->parse  ? aclkw->parse  : pat_parse_fcts[aclkw->match_type];
257
0
    expr->pat.index  = aclkw->index  ? aclkw->index  : pat_index_fcts[aclkw->match_type];
258
0
    expr->pat.match  = aclkw->match  ? aclkw->match  : pat_match_fcts[aclkw->match_type];
259
0
    expr->pat.prune  = aclkw->prune  ? aclkw->prune  : pat_prune_fcts[aclkw->match_type];
260
0
  }
261
262
0
  if (!expr->pat.parse) {
263
    /* Parse/index/match functions depend on the expression type,
264
     * so we have to map them now. Some types can be automatically
265
     * converted.
266
     */
267
0
    switch (cur_type) {
268
0
    case SMP_T_BOOL:
269
0
      expr->pat.parse = pat_parse_fcts[PAT_MATCH_BOOL];
270
0
      expr->pat.index = pat_index_fcts[PAT_MATCH_BOOL];
271
0
      expr->pat.match = pat_match_fcts[PAT_MATCH_BOOL];
272
0
      expr->pat.prune = pat_prune_fcts[PAT_MATCH_BOOL];
273
0
      expr->pat.expect_type = pat_match_types[PAT_MATCH_BOOL];
274
0
      break;
275
0
    case SMP_T_SINT:
276
0
      expr->pat.parse = pat_parse_fcts[PAT_MATCH_INT];
277
0
      expr->pat.index = pat_index_fcts[PAT_MATCH_INT];
278
0
      expr->pat.match = pat_match_fcts[PAT_MATCH_INT];
279
0
      expr->pat.prune = pat_prune_fcts[PAT_MATCH_INT];
280
0
      expr->pat.expect_type = pat_match_types[PAT_MATCH_INT];
281
0
      break;
282
0
    case SMP_T_ADDR:
283
0
    case SMP_T_IPV4:
284
0
    case SMP_T_IPV6:
285
0
      expr->pat.parse = pat_parse_fcts[PAT_MATCH_IP];
286
0
      expr->pat.index = pat_index_fcts[PAT_MATCH_IP];
287
0
      expr->pat.match = pat_match_fcts[PAT_MATCH_IP];
288
0
      expr->pat.prune = pat_prune_fcts[PAT_MATCH_IP];
289
0
      expr->pat.expect_type = pat_match_types[PAT_MATCH_IP];
290
0
      break;
291
0
    case SMP_T_STR:
292
0
      expr->pat.parse = pat_parse_fcts[PAT_MATCH_STR];
293
0
      expr->pat.index = pat_index_fcts[PAT_MATCH_STR];
294
0
      expr->pat.match = pat_match_fcts[PAT_MATCH_STR];
295
0
      expr->pat.prune = pat_prune_fcts[PAT_MATCH_STR];
296
0
      expr->pat.expect_type = pat_match_types[PAT_MATCH_STR];
297
0
      break;
298
0
    }
299
0
  }
300
301
  /* Additional check to protect against common mistakes */
302
0
  if (expr->pat.parse && cur_type != SMP_T_BOOL && !*args[1]) {
303
0
    ha_warning("parsing acl keyword '%s' :\n"
304
0
         "  no pattern to match against were provided, so this ACL will never match.\n"
305
0
         "  If this is what you intended, please add '--' to get rid of this warning.\n"
306
0
         "  If you intended to match only for existence, please use '-m found'.\n"
307
0
         "  If you wanted to force an int to match as a bool, please use '-m bool'.\n"
308
0
         "\n",
309
0
         args[0]);
310
0
  }
311
312
0
  args++;
313
314
  /* check for options before patterns. Supported options are :
315
   *   -i : ignore case for all patterns by default
316
   *   -f : read patterns from those files
317
   *   -m : force matching method (must be used before -f)
318
   *   -M : load the file as map file
319
   *   -u : force the unique id of the acl
320
   *   -- : everything after this is not an option
321
   */
322
0
  refflags = PAT_REF_ACL;
323
0
  patflags = 0;
324
0
  is_loaded = 0;
325
0
  match_forced = 0;
326
0
  unique_id = -1;
327
0
  while (**args == '-') {
328
0
    if (strcmp(*args, "-i") == 0)
329
0
      patflags |= PAT_MF_IGNORE_CASE;
330
0
    else if (strcmp(*args, "-n") == 0)
331
0
      patflags |= PAT_MF_NO_DNS;
332
0
    else if (strcmp(*args, "-u") == 0) {
333
0
      unique_id = strtol(args[1], &error, 10);
334
0
      if (*error != '\0') {
335
0
        memprintf(err, "the argument of -u must be an integer");
336
0
        goto out_free_expr;
337
0
      }
338
339
      /* Check if this id is really unique. */
340
0
      if (pat_ref_lookupid(unique_id)) {
341
0
        memprintf(err, "the id is already used");
342
0
        goto out_free_expr;
343
0
      }
344
345
0
      args++;
346
0
    }
347
0
    else if (strcmp(*args, "-f") == 0) {
348
0
      if (!expr->pat.parse) {
349
0
        memprintf(err, "matching method must be specified first (using '-m') when using a sample fetch of this type ('%s')", expr->kw);
350
0
        goto out_free_expr;
351
0
      }
352
353
0
      if (!pattern_read_from_file(&expr->pat, refflags, args[1], patflags, load_as_map, err, file, line))
354
0
        goto out_free_expr;
355
0
      is_loaded = 1;
356
0
      args++;
357
0
    }
358
0
    else if (strcmp(*args, "-m") == 0) {
359
0
      int idx;
360
361
0
      if (is_loaded) {
362
0
        memprintf(err, "'-m' must only be specified before patterns and files in parsing ACL expression");
363
0
        goto out_free_expr;
364
0
      }
365
0
      if (match_forced) {
366
0
        memprintf(err, "only one explicit matching method can be defined with '-m' parameter."
367
0
            " if migrating from an old version, just keep the last one");
368
0
        goto out_free_expr;
369
0
      }
370
371
0
      idx = pat_find_match_name(args[1]);
372
0
      if (idx < 0) {
373
0
        memprintf(err, "unknown matching method '%s' when parsing ACL expression", args[1]);
374
0
        goto out_free_expr;
375
0
      }
376
377
      /* Note: -m found is always valid, bool/int are compatible, str/bin/reg/len are compatible */
378
0
      if (idx != PAT_MATCH_FOUND && !sample_casts[cur_type][pat_match_types[idx]]) {
379
0
        memprintf(err, "matching method '%s' cannot be used with fetch keyword '%s'", args[1], expr->kw);
380
0
        goto out_free_expr;
381
0
      }
382
0
      expr->pat.parse = pat_parse_fcts[idx];
383
0
      expr->pat.index = pat_index_fcts[idx];
384
0
      expr->pat.match = pat_match_fcts[idx];
385
0
      expr->pat.prune = pat_prune_fcts[idx];
386
0
      expr->pat.expect_type = pat_match_types[idx];
387
0
      match_forced = 1;
388
0
      args++;
389
0
    }
390
0
    else if (strcmp(*args, "-M") == 0) {
391
0
      refflags |= PAT_REF_MAP;
392
0
      load_as_map = 1;
393
0
    }
394
0
    else if (strcmp(*args, "--") == 0) {
395
0
      args++;
396
0
      break;
397
0
    }
398
0
    else {
399
0
      memprintf(err, "'%s' is not a valid ACL option. Please use '--' before any pattern beginning with a '-'", args[0]);
400
0
      goto out_free_expr;
401
0
      break;
402
0
    }
403
0
    args++;
404
0
  }
405
406
0
  if (!expr->pat.parse) {
407
0
    memprintf(err, "matching method must be specified first (using '-m') when using a sample fetch of this type ('%s')", expr->kw);
408
0
    goto out_free_expr;
409
0
  }
410
411
0
  if (aclkw) {
412
0
    if (((aclkw->match_type == PAT_MATCH_BEG || aclkw->match_type == PAT_MATCH_DIR ||
413
0
          aclkw->match_type == PAT_MATCH_DOM || aclkw->match_type == PAT_MATCH_END || aclkw->match_type == PAT_MATCH_LEN ||
414
0
          aclkw->match_type == PAT_MATCH_REG || aclkw->match_type == PAT_MATCH_SUB) &&
415
0
         expr->pat.match != pat_match_fcts[aclkw->match_type]) ||
416
0
        (aclkw->match && expr->pat.match != aclkw->match))
417
0
      ha_warning("parsing [%s:%d] : original matching method '%s' was overwritten and will not be applied as expected.\n",
418
0
           file, line, aclkw->kw);
419
0
  }
420
421
  /* Create displayed reference */
422
0
  snprintf(trash.area, trash.size, "acl '%s' file '%s' line %d",
423
0
     expr->kw, file, line);
424
0
  trash.area[trash.size - 1] = '\0';
425
426
  /* Create new pattern reference. */
427
0
  ref = pat_ref_newid(unique_id, trash.area, PAT_REF_ACL);
428
0
  if (!ref) {
429
0
    memprintf(err, "memory error");
430
0
    goto out_free_expr;
431
0
  }
432
433
  /* Create new pattern expression associated to this reference. */
434
0
  pattern_expr = pattern_new_expr(&expr->pat, ref, patflags, err, NULL);
435
0
  if (!pattern_expr)
436
0
    goto out_free_expr;
437
438
  /* now parse all patterns */
439
0
  while (**args) {
440
0
    arg = *args;
441
442
    /* Compatibility layer. Each pattern can parse only one string per pattern,
443
     * but the pat_parse_int() and pat_parse_dotted_ver() parsers need
444
     * optionally two operators. The first operator is the match method: eq,
445
     * le, lt, ge and gt. pat_parse_int() and pat_parse_dotted_ver() functions
446
     * can have a compatibility syntax based on ranges:
447
     *
448
     * pat_parse_int():
449
     *
450
     *   "eq x" -> "x" or "x:x"
451
     *   "le x" -> ":x"
452
     *   "lt x" -> ":y" (with y = x - 1)
453
     *   "ge x" -> "x:"
454
     *   "gt x" -> "y:" (with y = x + 1)
455
     *
456
     * pat_parse_dotted_ver():
457
     *
458
     *   "eq x.y" -> "x.y" or "x.y:x.y"
459
     *   "le x.y" -> ":x.y"
460
     *   "lt x.y" -> ":w.z" (with w.z = x.y - 1)
461
     *   "ge x.y" -> "x.y:"
462
     *   "gt x.y" -> "w.z:" (with w.z = x.y + 1)
463
     *
464
     * If y is not present, assume that is "0".
465
     *
466
     * The syntax eq, le, lt, ge and gt are proper to the acl syntax. The
467
     * following block of code detect the operator, and rewrite each value
468
     * in parsable string.
469
     */
470
0
    if (expr->pat.parse == pat_parse_int ||
471
0
        expr->pat.parse == pat_parse_dotted_ver) {
472
      /* Check for operator. If the argument is operator, memorise it and
473
       * continue to the next argument.
474
       */
475
0
      op = get_std_op(arg);
476
0
      if (op != -1) {
477
0
        operator = op;
478
0
        args++;
479
0
        continue;
480
0
      }
481
482
      /* Check if the pattern contain ':' or '-' character. */
483
0
      contain_colon = (strchr(arg, ':') || strchr(arg, '-'));
484
485
      /* If the pattern contain ':' or '-' character, give it to the parser as is.
486
       * If no contain ':' and operator is STD_OP_EQ, give it to the parser as is.
487
       * In other case, try to convert the value according with the operator.
488
       */
489
0
      if (!contain_colon && operator != STD_OP_EQ) {
490
        /* Search '.' separator. */
491
0
        dot = strchr(arg, '.');
492
0
        if (!dot) {
493
0
          have_dot = 0;
494
0
          minor = 0;
495
0
          dot = arg + strlen(arg);
496
0
        }
497
0
        else
498
0
          have_dot = 1;
499
500
        /* convert the integer minor part for the pat_parse_dotted_ver() function. */
501
0
        if (expr->pat.parse == pat_parse_dotted_ver && have_dot) {
502
0
          if (strl2llrc(dot+1, strlen(dot+1), &minor) != 0) {
503
0
            memprintf(err, "'%s' is neither a number nor a supported operator", arg);
504
0
            goto out_free_expr;
505
0
          }
506
0
          if (minor >= 65536) {
507
0
            memprintf(err, "'%s' contains too large a minor value", arg);
508
0
            goto out_free_expr;
509
0
          }
510
0
        }
511
512
        /* convert the integer value for the pat_parse_int() function, and the
513
         * integer major part for the pat_parse_dotted_ver() function.
514
         */
515
0
        if (strl2llrc(arg, dot - arg, &value) != 0) {
516
0
          memprintf(err, "'%s' is neither a number nor a supported operator", arg);
517
0
          goto out_free_expr;
518
0
        }
519
0
        if (expr->pat.parse == pat_parse_dotted_ver)  {
520
0
          if (value >= 65536) {
521
0
            memprintf(err, "'%s' contains too large a major value", arg);
522
0
            goto out_free_expr;
523
0
          }
524
0
          value = (value << 16) | (minor & 0xffff);
525
0
        }
526
527
0
        switch (operator) {
528
529
0
        case STD_OP_EQ: /* this case is not possible. */
530
0
          memprintf(err, "internal error");
531
0
          goto out_free_expr;
532
533
0
        case STD_OP_GT:
534
0
          value++; /* gt = ge + 1 */
535
0
          __fallthrough;
536
537
0
        case STD_OP_GE:
538
0
          if (expr->pat.parse == pat_parse_int)
539
0
            snprintf(buffer, NB_LLMAX_STR+NB_LLMAX_STR+2, "%lld:", value);
540
0
          else
541
0
            snprintf(buffer, NB_LLMAX_STR+NB_LLMAX_STR+2, "%lld.%lld:",
542
0
                     value >> 16, value & 0xffff);
543
0
          arg = buffer;
544
0
          break;
545
546
0
        case STD_OP_LT:
547
0
          value--; /* lt = le - 1 */
548
0
          __fallthrough;
549
550
0
        case STD_OP_LE:
551
0
          if (expr->pat.parse == pat_parse_int)
552
0
            snprintf(buffer, NB_LLMAX_STR+NB_LLMAX_STR+2, ":%lld", value);
553
0
          else
554
0
            snprintf(buffer, NB_LLMAX_STR+NB_LLMAX_STR+2, ":%lld.%lld",
555
0
                     value >> 16, value & 0xffff);
556
0
          arg = buffer;
557
0
          break;
558
0
        }
559
0
      }
560
0
    }
561
562
    /* Add sample to the reference, and try to compile it for each pattern
563
     * using this value.
564
     */
565
0
    if (!pat_ref_add(ref, arg, NULL, err))
566
0
      goto out_free_expr;
567
568
0
    if (global.mode & MODE_DIAG) {
569
0
      if (strcmp(arg, "&&") == 0 || strcmp(arg, "and") == 0 ||
570
0
          strcmp(arg, "||") == 0 ||  strcmp(arg, "or") == 0)
571
0
        ha_diag_warning("parsing [%s:%d] : pattern '%s' looks like a failed attempt at using an operator inside a pattern list\n", file, line, arg);
572
0
      else if (strcmp(arg, "#") == 0 || strcmp(arg, "//") == 0)
573
0
        ha_diag_warning("parsing [%s:%d] : pattern '%s' looks like a failed attempt at commenting an end of line\n", file, line, arg);
574
0
      else if (find_acl_kw(arg))
575
0
        ha_diag_warning("parsing [%s:%d] : pattern '%s' suspiciously looks like a known acl keyword\n", file, line, arg);
576
0
      else {
577
0
        const char *begw = arg, *endw;
578
579
0
        for (endw = begw; is_idchar(*endw); endw++)
580
0
          ;
581
582
0
        if (endw != begw && find_sample_fetch(begw, endw - begw))
583
0
          ha_diag_warning("parsing [%s:%d] : pattern '%s' suspiciously looks like a known sample fetch keyword\n", file, line, arg);
584
0
      }
585
0
    }
586
0
    args++;
587
0
  }
588
589
0
  return expr;
590
591
0
 out_free_expr:
592
0
  prune_acl_expr(expr);
593
0
  free(expr);
594
0
 out_free_smp:
595
0
  free(smp);
596
0
 out_return:
597
0
  return NULL;
598
0
}
599
600
/* Purge everything in the acl <acl>, then return <acl>. */
601
0
struct acl *prune_acl(struct acl *acl) {
602
603
0
  struct acl_expr *expr, *exprb;
604
605
0
  free(acl->name);
606
607
0
  list_for_each_entry_safe(expr, exprb, &acl->expr, list) {
608
0
    LIST_DELETE(&expr->list);
609
0
    prune_acl_expr(expr);
610
0
    free(expr);
611
0
  }
612
613
0
  return acl;
614
0
}
615
616
/* Walk the ACL tree, following nested acl() sample fetches, for no more than
617
 * max_recurse evaluations. Returns -1 if a recursive loop is detected, 0 if
618
 * the max_recurse was reached, otherwise the number of max_recurse left.
619
 */
620
static int parse_acl_recurse(struct acl *acl, struct acl_expr *expr, int max_recurse)
621
0
{
622
0
  struct acl_term *term;
623
0
  struct acl_sample *sample;
624
625
0
  if (strcmp(expr->smp->fetch->kw, "acl") != 0)
626
0
    return max_recurse;
627
628
0
  if (--max_recurse <= 0)
629
0
    return 0;
630
631
0
  sample = (struct acl_sample *)expr->smp->arg_p->data.ptr;
632
0
  list_for_each_entry(term, &sample->suite.terms, list) {
633
0
    if (term->acl == acl)
634
0
      return -1;
635
0
    list_for_each_entry(expr, &term->acl->expr, list) {
636
0
      max_recurse = parse_acl_recurse(acl, expr, max_recurse);
637
0
      if (max_recurse <= 0)
638
0
        return max_recurse;
639
0
    }
640
0
  }
641
642
0
  return max_recurse;
643
0
}
644
645
/* Parse an ACL with the name starting at <args>[0], and with a list of already
646
 * known ACLs in <acl>. If the ACL was not in the list, it will be added.
647
 * A pointer to that ACL is returned. If the ACL has an empty name, then it's
648
 * an anonymous one and it won't be merged with any other one. If <err> is not
649
 * NULL, it will be filled with an appropriate error. This pointer must be
650
 * freeable or NULL. <al> is the arg_list serving as a head for unresolved
651
 * dependencies. It may be NULL if such dependencies are not allowed.
652
 *
653
 * args syntax: <aclname> <acl_expr>
654
 */
655
struct acl *parse_acl(const char **args, struct list *known_acl, char **err, struct arg_list *al,
656
                      const char *file, int line)
657
0
{
658
0
  __label__ out_return, out_free_acl_expr, out_free_name;
659
0
  struct acl *cur_acl;
660
0
  struct acl_expr *acl_expr;
661
0
  char *name;
662
0
  const char *pos;
663
664
0
  if (**args && (pos = invalid_char(*args))) {
665
0
    memprintf(err, "invalid character in ACL name : '%c'", *pos);
666
0
    goto out_return;
667
0
  }
668
669
0
  acl_expr = parse_acl_expr(args + 1, err, al, file, line);
670
0
  if (!acl_expr) {
671
    /* parse_acl_expr will have filled <err> here */
672
0
    goto out_return;
673
0
  }
674
675
  /* Check for args beginning with an opening parenthesis just after the
676
   * subject, as this is almost certainly a typo. Right now we can only
677
   * emit a warning, so let's do so.
678
   */
679
0
  if (!strchr(args[1], '(') && *args[2] == '(')
680
0
    ha_warning("parsing acl '%s' :\n"
681
0
         "  matching '%s' for pattern '%s' is likely a mistake and probably\n"
682
0
         "  not what you want. Maybe you need to remove the extraneous space before '('.\n"
683
0
         "  If you are really sure this is not an error, please insert '--' between the\n"
684
0
         "  match and the pattern to make this warning message disappear.\n",
685
0
         args[0], args[1], args[2]);
686
687
0
  if (*args[0])
688
0
    cur_acl = find_acl_by_name(args[0], known_acl);
689
0
  else
690
0
    cur_acl = NULL;
691
692
0
  if (cur_acl) {
693
0
    int ret = parse_acl_recurse(cur_acl, acl_expr, ACL_MAX_RECURSE);
694
0
    if (ret <= 0) {
695
0
      if (ret < 0)
696
0
        memprintf(err, "have a recursive loop");
697
0
      else
698
0
        memprintf(err, "too deep acl() tree");
699
0
      goto out_free_acl_expr;
700
0
    }
701
0
  } else {
702
0
    name = strdup(args[0]);
703
0
    if (!name) {
704
0
      memprintf(err, "out of memory when parsing ACL");
705
0
      goto out_free_acl_expr;
706
0
    }
707
0
    cur_acl = calloc(1, sizeof(*cur_acl));
708
0
    if (cur_acl == NULL) {
709
0
      memprintf(err, "out of memory when parsing ACL");
710
0
      goto out_free_name;
711
0
    }
712
713
0
    LIST_INIT(&cur_acl->expr);
714
0
    LIST_APPEND(known_acl, &cur_acl->list);
715
0
    cur_acl->name = name;
716
0
  }
717
718
  /* We want to know what features the ACL needs (typically HTTP parsing),
719
   * and where it may be used. If an ACL relies on multiple matches, it is
720
   * OK if at least one of them may match in the context where it is used.
721
   */
722
0
  cur_acl->use |= acl_expr->smp->fetch->use;
723
0
  cur_acl->val |= acl_expr->smp->fetch->val;
724
0
  LIST_APPEND(&cur_acl->expr, &acl_expr->list);
725
0
  return cur_acl;
726
727
0
 out_free_name:
728
0
  free(name);
729
0
 out_free_acl_expr:
730
0
  prune_acl_expr(acl_expr);
731
0
  free(acl_expr);
732
0
 out_return:
733
0
  return NULL;
734
0
}
735
736
/* Some useful ACLs provided by default. Only those used are allocated. */
737
738
const struct {
739
  const char *name;
740
  const char *expr[4]; /* put enough for longest expression */
741
} default_acl_list[] = {
742
  { .name = "TRUE",           .expr = {"always_true",""}},
743
  { .name = "FALSE",          .expr = {"always_false",""}},
744
  { .name = "LOCALHOST",      .expr = {"src","127.0.0.1/8","::1",""}},
745
  { .name = "HTTP",           .expr = {"req.proto_http",""}},
746
  { .name = "HTTP_1.0",       .expr = {"req.ver","1.0",""}},
747
  { .name = "HTTP_1.1",       .expr = {"req.ver","1.1",""}},
748
  { .name = "HTTP_2.0",       .expr = {"req.ver","2.0",""}},
749
  { .name = "HTTP_3.0",       .expr = {"req.ver","3.0",""}},
750
  { .name = "METH_CONNECT",   .expr = {"method","CONNECT",""}},
751
  { .name = "METH_DELETE",    .expr = {"method","DELETE",""}},
752
  { .name = "METH_GET",       .expr = {"method","GET","HEAD",""}},
753
  { .name = "METH_HEAD",      .expr = {"method","HEAD",""}},
754
  { .name = "METH_OPTIONS",   .expr = {"method","OPTIONS",""}},
755
  { .name = "METH_POST",      .expr = {"method","POST",""}},
756
  { .name = "METH_PUT",       .expr = {"method","PUT",""}},
757
  { .name = "METH_TRACE",     .expr = {"method","TRACE",""}},
758
  { .name = "HTTP_URL_ABS",   .expr = {"url_reg","^[^/:]*://",""}},
759
  { .name = "HTTP_URL_SLASH", .expr = {"url_beg","/",""}},
760
  { .name = "HTTP_URL_STAR",  .expr = {"url","*",""}},
761
  { .name = "HTTP_CONTENT",   .expr = {"req.hdr_val(content-length)","gt","0",""}},
762
  { .name = "RDP_COOKIE",     .expr = {"req.rdp_cookie_cnt","gt","0",""}},
763
  { .name = "REQ_CONTENT",    .expr = {"req.len","gt","0",""}},
764
  { .name = "WAIT_END",       .expr = {"wait_end",""}},
765
  { .name = NULL, .expr = {""}}
766
};
767
768
/* Find a default ACL from the default_acl list, compile it and return it.
769
 * If the ACL is not found, NULL is returned. In theory, it cannot fail,
770
 * except when default ACLs are broken, in which case it will return NULL.
771
 * If <known_acl> is not NULL, the ACL will be queued at its tail. If <err> is
772
 * not NULL, it will be filled with an error message if an error occurs. This
773
 * pointer must be freeable or NULL. <al> is an arg_list serving as a list head
774
 * to report missing dependencies. It may be NULL if such dependencies are not
775
 * allowed.
776
 */
777
struct acl *find_acl_default(const char *acl_name, struct list *known_acl,
778
                             char **err, struct arg_list *al,
779
                             const char *file, int line)
780
0
{
781
0
  __label__ out_return, out_free_acl_expr, out_free_name;
782
0
  struct acl *cur_acl;
783
0
  struct acl_expr *acl_expr;
784
0
  char *name;
785
0
  int index;
786
787
0
  for (index = 0; default_acl_list[index].name != NULL; index++) {
788
0
    if (strcmp(acl_name, default_acl_list[index].name) == 0)
789
0
      break;
790
0
  }
791
792
0
  if (default_acl_list[index].name == NULL) {
793
0
    memprintf(err, "no such ACL : '%s'", acl_name);
794
0
    return NULL;
795
0
  }
796
797
0
  acl_expr = parse_acl_expr((const char **)default_acl_list[index].expr, err, al, file, line);
798
0
  if (!acl_expr) {
799
    /* parse_acl_expr must have filled err here */
800
0
    goto out_return;
801
0
  }
802
803
0
  name = strdup(acl_name);
804
0
  if (!name) {
805
0
    memprintf(err, "out of memory when building default ACL '%s'", acl_name);
806
0
    goto out_free_acl_expr;
807
0
  }
808
809
0
  cur_acl = calloc(1, sizeof(*cur_acl));
810
0
  if (cur_acl == NULL) {
811
0
    memprintf(err, "out of memory when building default ACL '%s'", acl_name);
812
0
    goto out_free_name;
813
0
  }
814
815
0
  cur_acl->name = name;
816
0
  cur_acl->use |= acl_expr->smp->fetch->use;
817
0
  cur_acl->val |= acl_expr->smp->fetch->val;
818
0
  LIST_INIT(&cur_acl->expr);
819
0
  LIST_APPEND(&cur_acl->expr, &acl_expr->list);
820
0
  if (known_acl)
821
0
    LIST_APPEND(known_acl, &cur_acl->list);
822
823
0
  return cur_acl;
824
825
0
 out_free_name:
826
0
  free(name);
827
0
 out_free_acl_expr:
828
0
  prune_acl_expr(acl_expr);
829
0
  free(acl_expr);
830
0
 out_return:
831
0
  return NULL;
832
0
}
833
834
/* Parse an ACL condition starting at <args>[0], relying on a list of already
835
 * known ACLs passed in <known_acl>. The new condition is returned (or NULL in
836
 * case of low memory). Supports multiple conditions separated by "or". If
837
 * <err> is not NULL, it will be filled with a pointer to an error message in
838
 * case of error, that the caller is responsible for freeing. The initial
839
 * location must either be freeable or NULL. The list <al> serves as a list head
840
 * for unresolved dependencies. It may be NULL if such dependencies are not
841
 * allowed.
842
 */
843
struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl,
844
                                enum acl_cond_pol pol, char **err, struct arg_list *al,
845
                                const char *file, int line)
846
0
{
847
0
  __label__ out_return, out_free_suite, out_free_term;
848
0
  int arg, neg;
849
0
  const char *word;
850
0
  struct acl *cur_acl;
851
0
  struct acl_term *cur_term;
852
0
  struct acl_term_suite *cur_suite;
853
0
  struct acl_cond *cond;
854
0
  unsigned int suite_val;
855
856
0
  cond = calloc(1, sizeof(*cond));
857
0
  if (cond == NULL) {
858
0
    memprintf(err, "out of memory when parsing condition");
859
0
    goto out_return;
860
0
  }
861
862
0
  LIST_INIT(&cond->list);
863
0
  LIST_INIT(&cond->suites);
864
0
  cond->pol = pol;
865
0
  cond->val = 0;
866
867
0
  cur_suite = NULL;
868
0
  suite_val = ~0U;
869
0
  neg = 0;
870
0
  for (arg = 0; *args[arg]; arg++) {
871
0
    word = args[arg];
872
873
    /* remove as many exclamation marks as we can */
874
0
    while (*word == '!') {
875
0
      neg = !neg;
876
0
      word++;
877
0
    }
878
879
    /* an empty word is allowed because we cannot force the user to
880
     * always think about not leaving exclamation marks alone.
881
     */
882
0
    if (!*word)
883
0
      continue;
884
885
0
    if (strcasecmp(word, "or") == 0 || strcmp(word, "||") == 0) {
886
      /* new term suite */
887
0
      cond->val |= suite_val;
888
0
      suite_val = ~0U;
889
0
      cur_suite = NULL;
890
0
      neg = 0;
891
0
      continue;
892
0
    }
893
894
0
    if (strcmp(word, "{") == 0) {
895
      /* we may have a complete ACL expression between two braces,
896
       * find the last one.
897
       */
898
0
      int arg_end = arg + 1;
899
0
      const char **args_new;
900
901
0
      while (*args[arg_end] && strcmp(args[arg_end], "}") != 0)
902
0
        arg_end++;
903
904
0
      if (!*args[arg_end]) {
905
0
        memprintf(err, "missing closing '}' in condition");
906
0
        goto out_free_suite;
907
0
      }
908
909
0
      args_new = calloc(1, (arg_end - arg + 1) * sizeof(*args_new));
910
0
      if (!args_new) {
911
0
        memprintf(err, "out of memory when parsing condition");
912
0
        goto out_free_suite;
913
0
      }
914
915
0
      args_new[0] = "";
916
0
      memcpy(args_new + 1, args + arg + 1, (arg_end - arg) * sizeof(*args_new));
917
0
      args_new[arg_end - arg] = "";
918
0
      cur_acl = parse_acl(args_new, known_acl, err, al, file, line);
919
0
      free(args_new);
920
921
0
      if (!cur_acl) {
922
        /* note that parse_acl() must have filled <err> here */
923
0
        goto out_free_suite;
924
0
      }
925
0
      arg = arg_end;
926
0
    }
927
0
    else {
928
      /* search for <word> in the known ACL names. If we do not find
929
       * it, let's look for it in the default ACLs, and if found, add
930
       * it to the list of ACLs of this proxy. This makes it possible
931
       * to override them.
932
       */
933
0
      cur_acl = find_acl_by_name(word, known_acl);
934
0
      if (cur_acl == NULL) {
935
0
        cur_acl = find_acl_default(word, known_acl, err, al, file, line);
936
0
        if (cur_acl == NULL) {
937
          /* note that find_acl_default() must have filled <err> here */
938
0
          goto out_free_suite;
939
0
        }
940
0
      }
941
0
    }
942
943
0
    cur_term = calloc(1, sizeof(*cur_term));
944
0
    if (cur_term == NULL) {
945
0
      memprintf(err, "out of memory when parsing condition");
946
0
      goto out_free_suite;
947
0
    }
948
949
0
    cur_term->acl = cur_acl;
950
0
    cur_term->neg = neg;
951
952
    /* Here it is a bit complex. The acl_term_suite is a conjunction
953
     * of many terms. It may only be used if all of its terms are
954
     * usable at the same time. So the suite's validity domain is an
955
     * AND between all ACL keywords' ones. But, the global condition
956
     * is valid if at least one term suite is OK. So it's an OR between
957
     * all of their validity domains. We could emit a warning as soon
958
     * as suite_val is null because it means that the last ACL is not
959
     * compatible with the previous ones. Let's remain simple for now.
960
     */
961
0
    cond->use |= cur_acl->use;
962
0
    suite_val &= cur_acl->val;
963
964
0
    if (!cur_suite) {
965
0
      cur_suite = calloc(1, sizeof(*cur_suite));
966
0
      if (cur_suite == NULL) {
967
0
        memprintf(err, "out of memory when parsing condition");
968
0
        goto out_free_term;
969
0
      }
970
0
      LIST_INIT(&cur_suite->terms);
971
0
      LIST_APPEND(&cond->suites, &cur_suite->list);
972
0
    }
973
0
    LIST_APPEND(&cur_suite->terms, &cur_term->list);
974
0
    neg = 0;
975
0
  }
976
977
0
  cond->val |= suite_val;
978
0
  return cond;
979
980
0
 out_free_term:
981
0
  free(cur_term);
982
0
 out_free_suite:
983
0
  free_acl_cond(cond);
984
0
 out_return:
985
0
  return NULL;
986
0
}
987
988
/* Builds an ACL condition starting at the if/unless keyword. The complete
989
 * condition is returned. NULL is returned in case of error or if the first
990
 * word is neither "if" nor "unless". It automatically sets the file name and
991
 * the line number in the condition for better error reporting, and sets the
992
 * HTTP initialization requirements in the proxy. If <err> is not NULL, it will
993
 * be filled with a pointer to an error message in case of error, that the
994
 * caller is responsible for freeing. The initial location must either be
995
 * freeable or NULL.
996
 */
997
struct acl_cond *build_acl_cond(const char *file, int line, struct list *known_acl,
998
        struct proxy *px, const char **args, char **err)
999
0
{
1000
0
  enum acl_cond_pol pol = ACL_COND_NONE;
1001
0
  struct acl_cond *cond = NULL;
1002
1003
0
  if (err)
1004
0
    *err = NULL;
1005
1006
0
  if (strcmp(*args, "if") == 0) {
1007
0
    pol = ACL_COND_IF;
1008
0
    args++;
1009
0
  }
1010
0
  else if (strcmp(*args, "unless") == 0) {
1011
0
    pol = ACL_COND_UNLESS;
1012
0
    args++;
1013
0
  }
1014
0
  else {
1015
0
    memprintf(err, "conditions must start with either 'if' or 'unless'");
1016
0
    return NULL;
1017
0
  }
1018
1019
0
  cond = parse_acl_cond(args, known_acl, pol, err, &px->conf.args, file, line);
1020
0
  if (!cond) {
1021
    /* note that parse_acl_cond must have filled <err> here */
1022
0
    return NULL;
1023
0
  }
1024
1025
0
  cond->file = file;
1026
0
  cond->line = line;
1027
0
  px->http_needed |= !!(cond->use & SMP_USE_HTTP_ANY);
1028
0
  return cond;
1029
0
}
1030
1031
/* Execute condition <cond> and return either ACL_TEST_FAIL, ACL_TEST_MISS or
1032
 * ACL_TEST_PASS depending on the test results. ACL_TEST_MISS may only be
1033
 * returned if <opt> does not contain SMP_OPT_FINAL, indicating that incomplete
1034
 * data is being examined. The function automatically sets SMP_OPT_ITERATE. This
1035
 * function only computes the condition, it does not apply the polarity required
1036
 * by IF/UNLESS, it's up to the caller to do this using something like this :
1037
 *
1038
 *     res = acl_pass(res);
1039
 *     if (res == ACL_TEST_MISS)
1040
 *         return 0;
1041
 *     if (cond->pol == ACL_COND_UNLESS)
1042
 *         res = !res;
1043
 */
1044
enum acl_test_res acl_exec_cond(struct acl_cond *cond, struct proxy *px, struct session *sess, struct stream *strm, unsigned int opt)
1045
0
{
1046
0
  __label__ fetch_next;
1047
0
  struct acl_term_suite *suite;
1048
0
  struct acl_term *term;
1049
0
  struct acl_expr *expr;
1050
0
  struct acl *acl;
1051
0
  struct sample smp;
1052
0
  enum acl_test_res acl_res, suite_res, cond_res;
1053
1054
  /* ACLs are iterated over all values, so let's always set the flag to
1055
   * indicate this to the fetch functions.
1056
   */
1057
0
  opt |= SMP_OPT_ITERATE;
1058
1059
  /* We're doing a logical OR between conditions so we initialize to FAIL.
1060
   * The MISS status is propagated down from the suites.
1061
   */
1062
0
  cond_res = ACL_TEST_FAIL;
1063
0
  list_for_each_entry(suite, &cond->suites, list) {
1064
    /* Evaluate condition suite <suite>. We stop at the first term
1065
     * which returns ACL_TEST_FAIL. The MISS status is still propagated
1066
     * in case of uncertainty in the result.
1067
     */
1068
1069
    /* we're doing a logical AND between terms, so we must set the
1070
     * initial value to PASS.
1071
     */
1072
0
    suite_res = ACL_TEST_PASS;
1073
0
    list_for_each_entry(term, &suite->terms, list) {
1074
0
      acl = term->acl;
1075
1076
      /* FIXME: use cache !
1077
       * check acl->cache_idx for this.
1078
       */
1079
1080
      /* ACL result not cached. Let's scan all the expressions
1081
       * and use the first one to match.
1082
       */
1083
0
      acl_res = ACL_TEST_FAIL;
1084
0
      list_for_each_entry(expr, &acl->expr, list) {
1085
        /* we need to reset context and flags */
1086
0
        memset(&smp, 0, sizeof(smp));
1087
0
      fetch_next:
1088
0
        if (!sample_process(px, sess, strm, opt, expr->smp, &smp)) {
1089
          /* maybe we could not fetch because of missing data */
1090
0
          if (smp.flags & SMP_F_MAY_CHANGE && !(opt & SMP_OPT_FINAL))
1091
0
            acl_res |= ACL_TEST_MISS;
1092
0
          continue;
1093
0
        }
1094
1095
0
        acl_res |= pat2acl(pattern_exec_match(&expr->pat, &smp, 0));
1096
        /*
1097
         * OK now acl_res holds the result of this expression
1098
         * as one of ACL_TEST_FAIL, ACL_TEST_MISS or ACL_TEST_PASS.
1099
         *
1100
         * Then if (!MISS) we can cache the result, and put
1101
         * (smp.flags & SMP_F_VOLATILE) in the cache flags.
1102
         *
1103
         * FIXME: implement cache.
1104
         *
1105
         */
1106
1107
        /* we're ORing these terms, so a single PASS is enough */
1108
0
        if (acl_res == ACL_TEST_PASS)
1109
0
          break;
1110
1111
0
        if (smp.flags & SMP_F_NOT_LAST)
1112
0
          goto fetch_next;
1113
1114
        /* sometimes we know the fetched data is subject to change
1115
         * later and give another chance for a new match (eg: request
1116
         * size, time, ...)
1117
         */
1118
0
        if (smp.flags & SMP_F_MAY_CHANGE && !(opt & SMP_OPT_FINAL))
1119
0
          acl_res |= ACL_TEST_MISS;
1120
0
      }
1121
      /*
1122
       * Here we have the result of an ACL (cached or not).
1123
       * ACLs are combined, negated or not, to form conditions.
1124
       */
1125
1126
0
      if (term->neg)
1127
0
        acl_res = acl_neg(acl_res);
1128
1129
0
      suite_res &= acl_res;
1130
1131
      /* we're ANDing these terms, so a single FAIL or MISS is enough */
1132
0
      if (suite_res != ACL_TEST_PASS)
1133
0
        break;
1134
0
    }
1135
0
    cond_res |= suite_res;
1136
1137
    /* we're ORing these terms, so a single PASS is enough */
1138
0
    if (cond_res == ACL_TEST_PASS)
1139
0
      break;
1140
0
  }
1141
0
  return cond_res;
1142
0
}
1143
1144
/* Returns a pointer to the first ACL conflicting with usage at place <where>
1145
 * which is one of the SMP_VAL_* bits indicating a check place, or NULL if
1146
 * no conflict is found. Only full conflicts are detected (ACL is not usable).
1147
 * Use the next function to check for useless keywords.
1148
 */
1149
const struct acl *acl_cond_conflicts(const struct acl_cond *cond, unsigned int where)
1150
0
{
1151
0
  struct acl_term_suite *suite;
1152
0
  struct acl_term *term;
1153
0
  struct acl *acl;
1154
1155
0
  list_for_each_entry(suite, &cond->suites, list) {
1156
0
    list_for_each_entry(term, &suite->terms, list) {
1157
0
      acl = term->acl;
1158
0
      if (!(acl->val & where))
1159
0
        return acl;
1160
0
    }
1161
0
  }
1162
0
  return NULL;
1163
0
}
1164
1165
/* Returns a pointer to the first ACL and its first keyword to conflict with
1166
 * usage at place <where> which is one of the SMP_VAL_* bits indicating a check
1167
 * place. Returns true if a conflict is found, with <acl> and <kw> set (if non
1168
 * null), or false if not conflict is found. The first useless keyword is
1169
 * returned.
1170
 */
1171
int acl_cond_kw_conflicts(const struct acl_cond *cond, unsigned int where, struct acl const **acl, char const **kw)
1172
0
{
1173
0
  struct acl_term_suite *suite;
1174
0
  struct acl_term *term;
1175
0
  struct acl_expr *expr;
1176
1177
0
  list_for_each_entry(suite, &cond->suites, list) {
1178
0
    list_for_each_entry(term, &suite->terms, list) {
1179
0
      list_for_each_entry(expr, &term->acl->expr, list) {
1180
0
        if (!(expr->smp->fetch->val & where)) {
1181
0
          if (acl)
1182
0
            *acl = term->acl;
1183
0
          if (kw)
1184
0
            *kw = expr->kw;
1185
0
          return 1;
1186
0
        }
1187
0
      }
1188
0
    }
1189
0
  }
1190
0
  return 0;
1191
0
}
1192
1193
/*
1194
 * Find targets for userlist and groups in acl. Function returns the number
1195
 * of errors or OK if everything is fine. It must be called only once sample
1196
 * fetch arguments have been resolved (after smp_resolve_args()).
1197
 */
1198
int acl_find_targets(struct proxy *p)
1199
0
{
1200
1201
0
  struct acl *acl;
1202
0
  struct acl_expr *expr;
1203
0
  struct pattern_list *pattern;
1204
0
  int cfgerr = 0;
1205
0
  struct pattern_expr_list *pexp;
1206
1207
0
  list_for_each_entry(acl, &p->acl, list) {
1208
0
    list_for_each_entry(expr, &acl->expr, list) {
1209
0
      if (strcmp(expr->kw, "http_auth_group") == 0) {
1210
        /* Note: the ARGT_USR argument may only have been resolved earlier
1211
         * by smp_resolve_args().
1212
         */
1213
0
        if (expr->smp->arg_p->unresolved) {
1214
0
          ha_alert("Internal bug in proxy %s: %sacl %s %s() makes use of unresolved userlist '%s'. Please report this.\n",
1215
0
             p->id, *acl->name ? "" : "anonymous ", acl->name, expr->kw,
1216
0
             expr->smp->arg_p->data.str.area);
1217
0
          cfgerr++;
1218
0
          continue;
1219
0
        }
1220
1221
0
        if (LIST_ISEMPTY(&expr->pat.head)) {
1222
0
          ha_alert("proxy %s: acl %s %s(): no groups specified.\n",
1223
0
             p->id, acl->name, expr->kw);
1224
0
          cfgerr++;
1225
0
          continue;
1226
0
        }
1227
1228
        /* For each pattern, check if the group exists. */
1229
0
        list_for_each_entry(pexp, &expr->pat.head, list) {
1230
0
          if (LIST_ISEMPTY(&pexp->expr->patterns)) {
1231
0
            ha_alert("proxy %s: acl %s %s(): no groups specified.\n",
1232
0
               p->id, acl->name, expr->kw);
1233
0
            cfgerr++;
1234
0
            continue;
1235
0
          }
1236
1237
0
          list_for_each_entry(pattern, &pexp->expr->patterns, list) {
1238
            /* this keyword only has one argument */
1239
0
            if (!check_group(expr->smp->arg_p->data.usr, pattern->pat.ptr.str)) {
1240
0
              ha_alert("proxy %s: acl %s %s(): invalid group '%s'.\n",
1241
0
                 p->id, acl->name, expr->kw, pattern->pat.ptr.str);
1242
0
              cfgerr++;
1243
0
            }
1244
0
          }
1245
0
        }
1246
0
      }
1247
0
    }
1248
0
  }
1249
1250
0
  return cfgerr;
1251
0
}
1252
1253
/* initializes ACLs by resolving the sample fetch names they rely upon.
1254
 * Returns 0 on success, otherwise an error.
1255
 */
1256
int init_acl()
1257
0
{
1258
0
  int err = 0;
1259
0
  int index;
1260
0
  const char *name;
1261
0
  struct acl_kw_list *kwl;
1262
0
  struct sample_fetch *smp;
1263
1264
0
  list_for_each_entry(kwl, &acl_keywords.list, list) {
1265
0
    for (index = 0; kwl->kw[index].kw != NULL; index++) {
1266
0
      name = kwl->kw[index].fetch_kw;
1267
0
      if (!name)
1268
0
        name = kwl->kw[index].kw;
1269
1270
0
      smp = find_sample_fetch(name, strlen(name));
1271
0
      if (!smp) {
1272
0
        ha_alert("Critical internal error: ACL keyword '%s' relies on sample fetch '%s' which was not registered!\n",
1273
0
           kwl->kw[index].kw, name);
1274
0
        err++;
1275
0
        continue;
1276
0
      }
1277
0
      kwl->kw[index].smp = smp;
1278
0
    }
1279
0
  }
1280
0
  return err;
1281
0
}
1282
1283
/* dump known ACL keywords on stdout */
1284
void acl_dump_kwd(void)
1285
0
{
1286
0
  struct acl_kw_list *kwl;
1287
0
  const struct acl_keyword *kwp, *kw;
1288
0
  const char *name;
1289
0
  int index;
1290
1291
0
  for (kw = kwp = NULL;; kwp = kw) {
1292
0
    list_for_each_entry(kwl, &acl_keywords.list, list) {
1293
0
      for (index = 0; kwl->kw[index].kw != NULL; index++) {
1294
0
        if (strordered(kwp ? kwp->kw : NULL,
1295
0
                 kwl->kw[index].kw,
1296
0
                 kw != kwp ? kw->kw : NULL))
1297
0
          kw = &kwl->kw[index];
1298
0
      }
1299
0
    }
1300
1301
0
    if (kw == kwp)
1302
0
      break;
1303
1304
0
    name = kw->fetch_kw;
1305
0
    if (!name)
1306
0
      name = kw->kw;
1307
1308
0
    printf("%s = %s -m %s\n", kw->kw, name, pat_match_names[kw->match_type]);
1309
0
  }
1310
0
}
1311
1312
/* Purge everything in the acl_cond <cond>, then free <cond> */
1313
void free_acl_cond(struct acl_cond *cond)
1314
0
{
1315
0
  struct acl_term_suite *suite, *suiteb;
1316
0
  struct acl_term *term, *termb;
1317
1318
0
  if (!cond)
1319
0
    return;
1320
1321
0
  list_for_each_entry_safe(suite, suiteb, &cond->suites, list) {
1322
0
    list_for_each_entry_safe(term, termb, &suite->terms, list) {
1323
0
      LIST_DELETE(&term->list);
1324
0
      free(term);
1325
0
    }
1326
0
    LIST_DELETE(&suite->list);
1327
0
    free(suite);
1328
0
  }
1329
1330
0
  free(cond);
1331
0
}
1332
1333
1334
static int smp_fetch_acl(const struct arg *args, struct sample *smp, const char *kw, void *private)
1335
0
{
1336
0
  struct acl_sample *acl_sample = (struct acl_sample *)args->data.ptr;
1337
0
  enum acl_test_res ret;
1338
1339
0
  ret = acl_exec_cond(&acl_sample->cond, smp->px, smp->sess, smp->strm, smp->opt);
1340
0
  if (ret == ACL_TEST_MISS)
1341
0
    return 0;
1342
0
  smp->data.u.sint = ret == ACL_TEST_PASS;
1343
0
  smp->data.type = SMP_T_BOOL;
1344
0
  return 1;
1345
0
}
1346
1347
int smp_fetch_acl_parse(struct arg *args, char **err_msg)
1348
0
{
1349
0
  struct acl_sample *acl_sample;
1350
0
  char *name;
1351
0
  int i;
1352
1353
0
  for (i = 0; args[i].type != ARGT_STOP; i++)
1354
0
    ;
1355
0
  acl_sample = calloc(1, sizeof(struct acl_sample) + sizeof(struct acl_term) * i);
1356
0
  if (unlikely(!acl_sample)) {
1357
0
    memprintf(err_msg, "out of memory when parsing ACL expression");
1358
0
    return 0;
1359
0
  }
1360
0
  LIST_INIT(&acl_sample->suite.terms);
1361
0
  LIST_INIT(&acl_sample->cond.suites);
1362
0
  LIST_APPEND(&acl_sample->cond.suites, &acl_sample->suite.list);
1363
0
  acl_sample->cond.val = ~0U; // the keyword is valid everywhere for now.
1364
1365
0
  for (i = 0; args[i].type != ARGT_STOP; i++) {
1366
0
    name = args[i].data.str.area;
1367
0
    if (name[0] == '!') {
1368
0
      acl_sample->terms[i].neg = 1;
1369
0
      name++;
1370
0
    }
1371
1372
1373
0
    if (
1374
0
      !(acl_sample->terms[i].acl = find_acl_by_name(name, &curproxy->acl)) &&
1375
0
      !(acl_sample->terms[i].acl = find_acl_default(name, &curproxy->acl, err_msg, NULL, NULL, 0))
1376
0
      ) {
1377
0
      memprintf(err_msg, "ACL '%s' not found", name);
1378
0
      goto err;
1379
0
    }
1380
1381
0
    acl_sample->cond.use |= acl_sample->terms[i].acl->use;
1382
0
    acl_sample->cond.val &= acl_sample->terms[i].acl->val;
1383
1384
0
    LIST_APPEND(&acl_sample->suite.terms, &acl_sample->terms[i].list);
1385
0
  }
1386
1387
  /* make the argument for smp_fetch_acl() */
1388
0
  args->data.ptr = acl_sample;
1389
0
  args->type     = ARGT_PTR;
1390
0
  return 1;
1391
1392
0
err:
1393
0
  free(acl_sample);
1394
0
  return 0;
1395
0
}
1396
1397
/************************************************************************/
1398
/*      All supported sample and ACL keywords must be declared here.    */
1399
/************************************************************************/
1400
1401
/* Note: must not be declared <const> as its list will be overwritten.
1402
 * Please take care of keeping this list alphabetically sorted.
1403
 */
1404
static struct acl_kw_list acl_kws = {ILH, {
1405
  { /* END */ },
1406
}};
1407
1408
INITCALL1(STG_REGISTER, acl_register_keywords, &acl_kws);
1409
1410
static struct sample_fetch_kw_list smp_kws = {ILH, {
1411
  { "acl", smp_fetch_acl, ARG12(1,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR), smp_fetch_acl_parse, SMP_T_BOOL, SMP_USE_CONST },
1412
  { /* END */ },
1413
}};
1414
1415
INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
1416
1417
/*
1418
 * Local variables:
1419
 *  c-indent-level: 8
1420
 *  c-basic-offset: 8
1421
 * End:
1422
 */