blob: efa785319091790dfad8071ca67ef66d4120ebfe [file] [log] [blame]
Willy Tarreaua84d3742007-05-07 00:36:48 +02001/*
2 * ACL management functions.
3 *
4 * Copyright 2000-2007 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
Willy Tarreauae8b7962007-06-09 23:10:04 +020013#include <ctype.h>
Willy Tarreaua84d3742007-05-07 00:36:48 +020014#include <stdio.h>
15#include <string.h>
16
17#include <common/config.h>
18#include <common/mini-clist.h>
19#include <common/standard.h>
20
21#include <proto/acl.h>
22
23#include <types/acl.h>
24#include <types/proxy.h>
25#include <types/session.h>
26
27/* List head of all known ACL keywords */
28static struct acl_kw_list acl_keywords = {
29 .list = LIST_HEAD_INIT(acl_keywords.list)
30};
31
32
33/* This one always returns 1 because its only purpose is to check that the
34 * value is present, which is already checked by getval().
35 */
36int acl_match_pst(struct acl_test *test, struct acl_pattern *pattern)
37{
38 return 1;
39}
40
41/* NB: For two strings to be identical, it is required that their lengths match */
42int acl_match_str(struct acl_test *test, struct acl_pattern *pattern)
43{
44 if (pattern->len != test->len)
45 return 0;
46 if (strncmp(pattern->ptr.str, test->ptr, test->len) == 0)
47 return 1;
48 return 0;
49}
50
Willy Tarreauf3d25982007-05-08 22:45:09 +020051/* Executes a regex. It needs to change the data. If it is marked READ_ONLY
52 * then it will be allocated and duplicated in place so that others may use
53 * it later on. Note that this is embarrassing because we always try to avoid
54 * allocating memory at run time.
55 */
56int acl_match_reg(struct acl_test *test, struct acl_pattern *pattern)
57{
58 char old_char;
59 int ret;
60
61 if (unlikely(test->flags & ACL_TEST_F_READ_ONLY)) {
62 char *new_str;
63
64 new_str = calloc(1, test->len + 1);
65 if (!new_str)
66 return 0;
67
68 memcpy(new_str, test->ptr, test->len);
69 new_str[test->len] = 0;
70 if (test->flags & ACL_TEST_F_MUST_FREE)
71 free(test->ptr);
72 test->ptr = new_str;
73 test->flags |= ACL_TEST_F_MUST_FREE;
74 test->flags &= ~ACL_TEST_F_READ_ONLY;
75 }
76
77 old_char = test->ptr[test->len];
78 test->ptr[test->len] = 0;
79
80 if (regexec(pattern->ptr.reg, test->ptr, 0, NULL, 0) == 0)
81 ret = 1;
82 else
83 ret = 0;
84
85 test->ptr[test->len] = old_char;
86 return ret;
87}
88
Willy Tarreaua84d3742007-05-07 00:36:48 +020089/* Checks that the pattern matches the beginning of the tested string. */
90int acl_match_beg(struct acl_test *test, struct acl_pattern *pattern)
91{
92 if (pattern->len > test->len)
93 return 0;
94 if (strncmp(pattern->ptr.str, test->ptr, pattern->len) != 0)
95 return 0;
96 return 1;
97}
98
99/* Checks that the pattern matches the end of the tested string. */
100int acl_match_end(struct acl_test *test, struct acl_pattern *pattern)
101{
102 if (pattern->len > test->len)
103 return 0;
104 if (strncmp(pattern->ptr.str, test->ptr + test->len - pattern->len, pattern->len) != 0)
105 return 0;
106 return 1;
107}
108
109/* Checks that the pattern is included inside the tested string.
110 * NB: Suboptimal, should be rewritten using a Boyer-Moore method.
111 */
112int acl_match_sub(struct acl_test *test, struct acl_pattern *pattern)
113{
114 char *end;
115 char *c;
116
117 if (pattern->len > test->len)
118 return 0;
119
120 end = test->ptr + test->len - pattern->len;
121 for (c = test->ptr; c <= end; c++) {
122 if (*c != *pattern->ptr.str)
123 continue;
124 if (strncmp(pattern->ptr.str, c, pattern->len) == 0)
125 return 1;
126 }
127 return 0;
128}
129
130/* This one is used by other real functions. It checks that the pattern is
131 * included inside the tested string, but enclosed between the specified
132 * delimitor, or a '/' or a '?' or at the beginning or end of the string.
133 * The delimitor is stripped at the beginning or end of the pattern are
134 * ignored.
135 */
136static int match_word(struct acl_test *test, struct acl_pattern *pattern, char delim)
137{
138 int may_match;
139 char *c, *end;
140 char *ps;
141 int pl;
142
143 pl = pattern->len;
144 ps = pattern->ptr.str;
145 while (pl > 0 && *ps == delim) {
146 pl--;
147 ps++;
148 }
149
150 while (pl > 0 && *(ps + pl - 1) == delim)
151 pl--;
152
153 if (pl > test->len)
154 return 0;
155
156 may_match = 1;
157 end = test->ptr + test->len - pl;
158 for (c = test->ptr; c <= end; c++) {
159 if (*c == '/' || *c == delim || *c == '?') {
160 may_match = 1;
161 continue;
162 }
163 if (may_match && (*c == *ps) &&
164 (strncmp(ps, c, pl) == 0) &&
165 (c == end || c[pl] == '/' || c[pl] == delim || c[pl] == '?'))
166 return 1;
167
168 may_match = 0;
169 }
170 return 0;
171}
172
173/* Checks that the pattern is included inside the tested string, but enclosed
174 * between slashes or at the beginning or end of the string. Slashes at the
175 * beginning or end of the pattern are ignored.
176 */
177int acl_match_dir(struct acl_test *test, struct acl_pattern *pattern)
178{
179 return match_word(test, pattern, '/');
180}
181
182/* Checks that the pattern is included inside the tested string, but enclosed
183 * between dots or at the beginning or end of the string. Dots at the beginning
184 * or end of the pattern are ignored.
185 */
186int acl_match_dom(struct acl_test *test, struct acl_pattern *pattern)
187{
188 return match_word(test, pattern, '.');
189}
190
191/* Checks that the integer in <test> is included between min and max */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200192int acl_match_int(struct acl_test *test, struct acl_pattern *pattern)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200193{
Willy Tarreauae8b7962007-06-09 23:10:04 +0200194 if ((!pattern->val.range.min_set || pattern->val.range.min <= test->i) &&
195 (!pattern->val.range.max_set || test->i <= pattern->val.range.max))
Willy Tarreaua84d3742007-05-07 00:36:48 +0200196 return 1;
197 return 0;
198}
199
Willy Tarreaua67fad92007-05-08 19:50:09 +0200200int acl_match_ip(struct acl_test *test, struct acl_pattern *pattern)
201{
202 struct in_addr *s;
203
204 if (test->i != AF_INET)
205 return 0;
206
207 s = (void *)test->ptr;
208 if (((s->s_addr ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0)
209 return 1;
210 return 0;
211}
212
Willy Tarreaua84d3742007-05-07 00:36:48 +0200213/* Parse a string. It is allocated and duplicated. */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200214int acl_parse_str(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200215{
216 int len;
217
Willy Tarreauae8b7962007-06-09 23:10:04 +0200218 len = strlen(*text);
219 pattern->ptr.str = strdup(*text);
Willy Tarreaua84d3742007-05-07 00:36:48 +0200220 if (!pattern->ptr.str)
221 return 0;
222 pattern->len = len;
223 return 1;
224}
225
Willy Tarreauf3d25982007-05-08 22:45:09 +0200226/* Parse a regex. It is allocated. */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200227int acl_parse_reg(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreauf3d25982007-05-08 22:45:09 +0200228{
229 regex_t *preg;
230
231 preg = calloc(1, sizeof(regex_t));
232
233 if (!preg)
234 return 0;
235
Willy Tarreauae8b7962007-06-09 23:10:04 +0200236 if (regcomp(preg, *text, REG_EXTENDED | REG_NOSUB) != 0) {
Willy Tarreauf3d25982007-05-08 22:45:09 +0200237 free(preg);
238 return 0;
239 }
240
241 pattern->ptr.reg = preg;
242 return 1;
243}
244
Willy Tarreauae8b7962007-06-09 23:10:04 +0200245/* Parse a range of positive integers delimited by either ':' or '-'. If only
246 * one integer is read, it is set as both min and max. An operator may be
247 * specified as the prefix, among this list of 5 :
248 *
249 * 0:eq, 1:gt, 2:ge, 3:lt, 4:le
250 *
251 * The default operator is "eq". It supports range matching. Ranges are
252 * rejected for other operators. The operator may be changed at any time.
253 * The operator is stored in the 'opaque' argument.
254 *
Willy Tarreaua84d3742007-05-07 00:36:48 +0200255 */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200256int acl_parse_int(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200257{
Willy Tarreauae8b7962007-06-09 23:10:04 +0200258 signed long long i;
259 unsigned int j, last, skip = 0;
260 const char *ptr = *text;
261
262
263 while (!isdigit(*ptr)) {
264 if (strcmp(ptr, "eq") == 0) *opaque = 0;
265 else if (strcmp(ptr, "gt") == 0) *opaque = 1;
266 else if (strcmp(ptr, "ge") == 0) *opaque = 2;
267 else if (strcmp(ptr, "lt") == 0) *opaque = 3;
268 else if (strcmp(ptr, "le") == 0) *opaque = 4;
269 else
270 return 0;
271
272 skip++;
273 ptr = text[skip];
274 }
Willy Tarreaua84d3742007-05-07 00:36:48 +0200275
276 last = i = 0;
277 while (1) {
Willy Tarreauae8b7962007-06-09 23:10:04 +0200278 j = *ptr++;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200279 if ((j == '-' || j == ':') && !last) {
280 last++;
281 pattern->val.range.min = i;
282 i = 0;
283 continue;
284 }
285 j -= '0';
286 if (j > 9)
287 // also catches the terminating zero
288 break;
289 i *= 10;
290 i += j;
291 }
Willy Tarreauae8b7962007-06-09 23:10:04 +0200292
293 if (last && *opaque >= 1 && *opaque <= 4)
294 /* having a range with a min or a max is absurd */
295 return 0;
296
Willy Tarreaua84d3742007-05-07 00:36:48 +0200297 if (!last)
298 pattern->val.range.min = i;
299 pattern->val.range.max = i;
Willy Tarreauae8b7962007-06-09 23:10:04 +0200300
301 switch (*opaque) {
302 case 0: /* eq */
303 pattern->val.range.min_set = 1;
304 pattern->val.range.max_set = 1;
305 break;
306 case 1: /* gt */
307 pattern->val.range.min++; /* gt = ge + 1 */
308 case 2: /* ge */
309 pattern->val.range.min_set = 1;
310 pattern->val.range.max_set = 0;
311 break;
312 case 3: /* lt */
313 pattern->val.range.max--; /* lt = le - 1 */
314 case 4: /* le */
315 pattern->val.range.min_set = 0;
316 pattern->val.range.max_set = 1;
317 break;
318 }
319 return skip + 1;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200320}
321
Willy Tarreaua67fad92007-05-08 19:50:09 +0200322/* Parse an IP address and an optional mask in the form addr[/mask].
323 * The addr may either be an IPv4 address or a hostname. The mask
324 * may either be a dotted mask or a number of bits. Returns 1 if OK,
325 * otherwise 0.
326 */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200327int acl_parse_ip(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua67fad92007-05-08 19:50:09 +0200328{
Willy Tarreauae8b7962007-06-09 23:10:04 +0200329 if (str2net(*text, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask))
330 return 1;
331 else
332 return 0;
Willy Tarreaua67fad92007-05-08 19:50:09 +0200333}
334
Willy Tarreaua84d3742007-05-07 00:36:48 +0200335/*
336 * Registers the ACL keyword list <kwl> as a list of valid keywords for next
337 * parsing sessions.
338 */
339void acl_register_keywords(struct acl_kw_list *kwl)
340{
341 LIST_ADDQ(&acl_keywords.list, &kwl->list);
342}
343
344/*
345 * Unregisters the ACL keyword list <kwl> from the list of valid keywords.
346 */
347void acl_unregister_keywords(struct acl_kw_list *kwl)
348{
349 LIST_DEL(&kwl->list);
350 LIST_INIT(&kwl->list);
351}
352
353/* Return a pointer to the ACL <name> within the list starting at <head>, or
354 * NULL if not found.
355 */
356struct acl *find_acl_by_name(const char *name, struct list *head)
357{
358 struct acl *acl;
359 list_for_each_entry(acl, head, list) {
360 if (strcmp(acl->name, name) == 0)
361 return acl;
362 }
363 return NULL;
364}
365
366/* Return a pointer to the ACL keyword <kw>, or NULL if not found. Note that if
367 * <kw> contains an opening parenthesis, only the left part of it is checked.
368 */
369struct acl_keyword *find_acl_kw(const char *kw)
370{
371 int index;
372 const char *kwend;
373 struct acl_kw_list *kwl;
374
375 kwend = strchr(kw, '(');
376 if (!kwend)
377 kwend = kw + strlen(kw);
378
379 list_for_each_entry(kwl, &acl_keywords.list, list) {
380 for (index = 0; kwl->kw[index].kw != NULL; index++) {
381 if ((strncmp(kwl->kw[index].kw, kw, kwend - kw) == 0) &&
382 kwl->kw[index].kw[kwend-kw] == 0)
383 return &kwl->kw[index];
384 }
385 }
386 return NULL;
387}
388
389static void free_pattern(struct acl_pattern *pat)
390{
391 if (pat->ptr.ptr)
392 free(pat->ptr.ptr);
393 free(pat);
394}
395
396static void free_pattern_list(struct list *head)
397{
398 struct acl_pattern *pat, *tmp;
399 list_for_each_entry_safe(pat, tmp, head, list)
400 free_pattern(pat);
401}
402
403static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
404{
405 free_pattern_list(&expr->patterns);
406 LIST_INIT(&expr->patterns);
407 if (expr->arg.str)
408 free(expr->arg.str);
409 expr->kw->use_cnt--;
410 return expr;
411}
412
413/* Parse an ACL expression starting at <args>[0], and return it.
414 * Right now, the only accepted syntax is :
415 * <subject> [<value>...]
416 */
417struct acl_expr *parse_acl_expr(const char **args)
418{
419 __label__ out_return, out_free_expr, out_free_pattern;
420 struct acl_expr *expr;
421 struct acl_keyword *aclkw;
422 struct acl_pattern *pattern;
Willy Tarreauae8b7962007-06-09 23:10:04 +0200423 int opaque;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200424 const char *arg;
425
426 aclkw = find_acl_kw(args[0]);
427 if (!aclkw || !aclkw->parse)
428 goto out_return;
429
430 expr = (struct acl_expr *)calloc(1, sizeof(*expr));
431 if (!expr)
432 goto out_return;
433
434 expr->kw = aclkw;
435 aclkw->use_cnt++;
436 LIST_INIT(&expr->patterns);
437 expr->arg.str = NULL;
438
439 arg = strchr(args[0], '(');
440 if (arg != NULL) {
441 char *end, *arg2;
442 /* there is an argument in the form "subject(arg)" */
443 arg++;
444 end = strchr(arg, ')');
445 if (!end)
446 goto out_free_expr;
447 arg2 = (char *)calloc(1, end - arg + 1);
448 if (!arg2)
449 goto out_free_expr;
450 memcpy(arg2, arg, end - arg);
451 arg2[end-arg] = '\0';
452 expr->arg.str = arg2;
453 }
454
455 /* now parse all patterns */
456 args++;
Willy Tarreauae8b7962007-06-09 23:10:04 +0200457 opaque = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200458 while (**args) {
Willy Tarreauae8b7962007-06-09 23:10:04 +0200459 int ret;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200460 pattern = (struct acl_pattern *)calloc(1, sizeof(*pattern));
461 if (!pattern)
462 goto out_free_expr;
Willy Tarreauae8b7962007-06-09 23:10:04 +0200463 ret = aclkw->parse(args, pattern, &opaque);
464 if (!ret)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200465 goto out_free_pattern;
466 LIST_ADDQ(&expr->patterns, &pattern->list);
Willy Tarreauae8b7962007-06-09 23:10:04 +0200467 args += ret;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200468 }
469
470 return expr;
471
472 out_free_pattern:
473 free_pattern(pattern);
474 out_free_expr:
475 prune_acl_expr(expr);
476 free(expr);
477 out_return:
478 return NULL;
479}
480
481/* Parse an ACL with the name starting at <args>[0], and with a list of already
482 * known ACLs in <acl>. If the ACL was not in the list, it will be added.
483 * A pointer to that ACL is returned.
484 *
485 * args syntax: <aclname> <acl_expr>
486 */
487struct acl *parse_acl(const char **args, struct list *known_acl)
488{
489 __label__ out_return, out_free_acl_expr, out_free_name;
490 struct acl *cur_acl;
491 struct acl_expr *acl_expr;
492 char *name;
493
494 acl_expr = parse_acl_expr(args + 1);
495 if (!acl_expr)
496 goto out_return;
497
498 cur_acl = find_acl_by_name(args[0], known_acl);
499 if (!cur_acl) {
500 name = strdup(args[0]);
501 if (!name)
502 goto out_free_acl_expr;
503 cur_acl = (struct acl *)calloc(1, sizeof(*cur_acl));
504 if (cur_acl == NULL)
505 goto out_free_name;
506
507 LIST_INIT(&cur_acl->expr);
508 LIST_ADDQ(known_acl, &cur_acl->list);
509 cur_acl->name = name;
510 }
511
512 LIST_ADDQ(&cur_acl->expr, &acl_expr->list);
513 return cur_acl;
514
515 out_free_name:
516 free(name);
517 out_free_acl_expr:
518 prune_acl_expr(acl_expr);
519 free(acl_expr);
520 out_return:
521 return NULL;
522}
523
524
525/* Purge everything in the acl_cond <cond>, then return <cond>. */
526struct acl_cond *prune_acl_cond(struct acl_cond *cond)
527{
528 struct acl_term_suite *suite, *tmp_suite;
529 struct acl_term *term, *tmp_term;
530
531 /* iterate through all term suites and free all terms and all suites */
532 list_for_each_entry_safe(suite, tmp_suite, &cond->suites, list) {
533 list_for_each_entry_safe(term, tmp_term, &suite->terms, list)
534 free(term);
535 free(suite);
536 }
537 return cond;
538}
539
540/* Parse an ACL condition starting at <args>[0], relying on a list of already
541 * known ACLs passed in <known_acl>. The new condition is returned (or NULL in
542 * case of low memory). Supports multiple conditions separated by "or".
543 */
544struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int pol)
545{
546 __label__ out_return, out_free_suite, out_free_term;
547 int arg;
548 int neg = 0;
549 const char *word;
550 struct acl *cur_acl;
551 struct acl_term *cur_term;
552 struct acl_term_suite *cur_suite;
553 struct acl_cond *cond;
554
555 cond = (struct acl_cond *)calloc(1, sizeof(*cond));
556 if (cond == NULL)
557 goto out_return;
558
559 LIST_INIT(&cond->list);
560 LIST_INIT(&cond->suites);
561 cond->pol = pol;
562
563 cur_suite = NULL;
564 for (arg = 0; *args[arg]; arg++) {
565 word = args[arg];
566
567 /* remove as many exclamation marks as we can */
568 while (*word == '!') {
569 neg = !neg;
570 word++;
571 }
572
573 /* an empty word is allowed because we cannot force the user to
574 * always think about not leaving exclamation marks alone.
575 */
576 if (!*word)
577 continue;
578
579 if (strcasecmp(word, "or") == 0) {
580 /* new term suite */
581 cur_suite = NULL;
582 neg = 0;
583 continue;
584 }
585
586 /* search for <word> in the known ACL names */
587 cur_acl = find_acl_by_name(word, known_acl);
588 if (cur_acl == NULL)
589 goto out_free_suite;
590
591 cur_term = (struct acl_term *)calloc(1, sizeof(*cur_term));
592 if (cur_term == NULL)
593 goto out_free_suite;
594
595 cur_term->acl = cur_acl;
596 cur_term->neg = neg;
597
598 if (!cur_suite) {
599 cur_suite = (struct acl_term_suite *)calloc(1, sizeof(*cur_suite));
600 if (cur_term == NULL)
601 goto out_free_term;
602 LIST_INIT(&cur_suite->terms);
603 LIST_ADDQ(&cond->suites, &cur_suite->list);
604 }
605 LIST_ADDQ(&cur_suite->terms, &cur_term->list);
606 }
607
608 return cond;
609
610 out_free_term:
611 free(cur_term);
612 out_free_suite:
613 prune_acl_cond(cond);
614 free(cond);
615 out_return:
616 return NULL;
617}
618
619/* Execute condition <cond> and return 0 if test fails or 1 if test succeeds.
620 * This function only computes the condition, it does not apply the polarity
621 * required by IF/UNLESS, it's up to the caller to do this.
622 */
623int acl_exec_cond(struct acl_cond *cond, struct proxy *px, struct session *l4, void *l7)
624{
625 __label__ fetch_next;
626 struct acl_term_suite *suite;
627 struct acl_term *term;
628 struct acl_expr *expr;
629 struct acl *acl;
630 struct acl_pattern *pattern;
631 struct acl_test test;
632 int acl_res, pat_res, suite_res, cond_res;
633
634 /* we're doing a logical OR between conditions so we initialize to FAIL */
635 cond_res = ACL_PAT_FAIL;
636 list_for_each_entry(suite, &cond->suites, list) {
637 /* evaluate condition suite <suite>. We stop at the first term
638 * which does not return ACL_PAT_PASS.
639 */
640
641 /* we're doing a logical AND between terms, so we must set the
642 * initial value to PASS.
643 */
644 suite_res = ACL_PAT_PASS;
645 list_for_each_entry(term, &suite->terms, list) {
646 acl = term->acl;
647
648 /* FIXME: use cache !
649 * check acl->cache_idx for this.
650 */
651
652 /* ACL result not cached. Let's scan all the expressions
653 * and use the first one to match.
654 */
655 acl_res = ACL_PAT_FAIL;
656 list_for_each_entry(expr, &acl->expr, list) {
657 test.flags = test.len = 0;
658 fetch_next:
659 if (!expr->kw->fetch(px, l4, l7, expr->arg.str, &test))
660 continue;
661
662 /* apply all tests to this value */
663 list_for_each_entry(pattern, &expr->patterns, list) {
664 pat_res = expr->kw->match(&test, pattern);
665
666 if (pat_res & ACL_PAT_MISS) {
667 /* there is at least one test which might be worth retrying later. */
668 acl_res |= ACL_PAT_MISS;
669 continue;
670 } else if (pat_res & ACL_PAT_PASS) {
671 /* we found one ! */
672 acl_res |= ACL_PAT_PASS;
673 break;
674 }
675 }
676 /*
677 * OK now we have the result of this expression in expr_res.
678 * - we have the PASS bit set if at least one pattern matched ;
679 * - we have the MISS bit set if at least one pattern may match
680 * later so that we should not cache a failure ;
681 *
682 * Then if (PASS || !MISS) we can cache the result, and put
683 * (test.flags & ACL_TEST_F_VOLATILE) in the cache flags.
684 *
685 * FIXME: implement cache.
686 *
687 */
688
689 /* now we may have some cleanup to do */
690 if (test.flags & ACL_TEST_F_MUST_FREE) {
691 free(test.ptr);
692 test.len = 0;
693 }
694
695 if (acl_res & ACL_PAT_PASS)
696 break;
697
698 /* prepare to test another expression */
699 acl_res = ACL_PAT_FAIL;
700
701 if (test.flags & ACL_TEST_F_FETCH_MORE)
702 goto fetch_next;
703 }
704 /*
705 * Here we have the result of an ACL (cached or not).
706 * ACLs are combined, negated or not, to form conditions.
707 */
708
709 acl_res &= ACL_PAT_PASS;
710 if (term->neg)
711 acl_res ^= ACL_PAT_PASS;
712
713 suite_res &= acl_res;
714 if (!(suite_res & ACL_PAT_PASS))
715 break;
716 }
717 cond_res |= suite_res;
718 if (cond_res & ACL_PAT_PASS)
719 break;
720 }
721
722 return (cond_res & ACL_PAT_PASS) ? 1 : 0;
723}
724
725
726/************************************************************************/
727/* All supported keywords must be declared here. */
728/************************************************************************/
729
730/* Note: must not be declared <const> as its list will be overwritten */
731static struct acl_kw_list acl_kws = {{ },{
732#if 0
733 { "time", acl_parse_time, acl_fetch_time, acl_match_time },
734#endif
735 { NULL, NULL, NULL, NULL }
736}};
737
738
739__attribute__((constructor))
740static void __acl_init(void)
741{
742 acl_register_keywords(&acl_kws);
743}
744
745
746/*
747 * Local variables:
748 * c-indent-level: 8
749 * c-basic-offset: 8
750 * End:
751 */