Move common code into macros
[freeradius.git] / src / main / parser.c
1 /*
2  * parser.c     Parse various things
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2013  Alan DeKok <aland@freeradius.org>
21  */
22
23 RCSID("$Id$")
24
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/parser.h>
27 #include <freeradius-devel/rad_assert.h>
28
29 #include <ctype.h>
30
31 /*
32  *      This file shouldn't use any functions from the server core.
33  */
34
35 size_t fr_cond_sprint(char *buffer, size_t bufsize, fr_cond_t const *c)
36 {
37         size_t len;
38         char *p = buffer;
39         char *end = buffer + bufsize - 1;
40
41 next:
42         if (c->negate) {
43                 *(p++) = '!';   /* FIXME: only allow for child? */
44         }
45
46         switch (c->type) {
47         case COND_TYPE_EXISTS:
48                 rad_assert(c->data.vpt != NULL);
49                 len = radius_tmpl2str(p, end - p, c->data.vpt);
50                 p += len;
51                 break;
52
53         case COND_TYPE_MAP:
54                 rad_assert(c->data.map != NULL);
55 #if 0
56                 *(p++) = '[';   /* for extra-clear debugging */
57 #endif
58                 len = radius_map2str(p, end - p, c->data.map);
59                 p += len;
60 #if 0
61                 *(p++) = ']';
62 #endif
63                 break;
64
65         case COND_TYPE_CHILD:
66                 rad_assert(c->data.child != NULL);
67                 *(p++) = '(';
68                 len = fr_cond_sprint(p, end - p, c->data.child);
69                 p += len;
70                 *(p++) = ')';
71                 break;
72
73         default:
74                 *buffer = '\0';
75                 return 0;
76         }
77
78         if (c->next_op == COND_NONE) {
79                 rad_assert(c->next == NULL);
80                 *p = '\0';
81                 return p - buffer;
82         }
83
84         if (c->next_op == COND_AND) {
85                 strlcpy(p, " && ", end - p);
86                 p += strlen(p);
87
88         } else if (c->next_op == COND_OR) {
89                 strlcpy(p, " || ", end - p);
90                 p += strlen(p);
91
92         } else {
93                 rad_assert(0 == 1);
94         }
95
96         c = c->next;
97         goto next;
98 }
99
100
101 static ssize_t condition_tokenize_string(TALLOC_CTX *ctx, char const *start, char **out,
102                                          FR_TOKEN *op, char const **error)
103 {
104         const char *p = start;
105         char *q;
106
107         switch (*p++) {
108         default:
109                 return -1;
110
111         case '"':
112                 *op = T_DOUBLE_QUOTED_STRING;
113                 break;
114
115         case '\'':
116                 *op = T_SINGLE_QUOTED_STRING;
117                 break;
118
119         case '`':
120                 *op = T_BACK_QUOTED_STRING;
121                 break;
122
123         case '/':
124                 *op = T_OP_REG_EQ; /* a bit of a hack. */
125                 break;
126
127         }
128
129         *out = talloc_array(ctx, char, strlen(start) - 1); /* + 2 - 1 */
130         if (!*out) return -1;
131
132         q = *out;
133
134         while (*p) {
135                 if (*p == *start) {
136                         *q = '\0';
137                         p++;
138                         return (p - start);
139                 }
140
141                 if (*p == '\\') {
142                         p++;
143                         if (!*p) {
144                                 *error = "End of string after escape";
145                                 return -(p - start);
146                         }
147
148                         switch (*p) {
149                         case 'r':
150                                 *q++ = '\r';
151                                 break;
152                         case 'n':
153                                 *q++ = '\n';
154                                 break;
155                         case 't':
156                                 *q++ = '\t';
157                                 break;
158                         default:
159                                 *q++ = *p;
160                                 break;
161                         }
162                         p++;
163                         continue;
164                 }
165         
166                 *(q++) = *(p++);
167         }
168
169         *error = "Unterminated string";
170         return -1;
171 }
172
173 static ssize_t condition_tokenize_word(TALLOC_CTX *ctx, char const *start, char **out,
174                                        FR_TOKEN *op, char const **error)
175 {
176         size_t len;
177         char const *p = start;
178
179         if ((*p == '"') || (*p == '\'') || (*p == '`') || (*p == '/')) {
180                 return condition_tokenize_string(ctx, start, out, op, error);
181         }
182
183         *op = T_BARE_WORD;
184         if (*p == '&') p++;     /* special-case &User-Name */
185
186         while (*p) {
187                 /*
188                  *      The LHS should really be limited to only a few
189                  *      things.  For now, we allow pretty much anything.
190                  */
191                 if (*p == '\\') {
192                         *error = "Unexpected escape";
193                         return -(p - start);
194                 }
195
196                 /*
197                  *      ("foo") is valid.
198                  */
199                 if (*p == ')') {
200                         break;
201                 }
202
203                 /*
204                  *      Spaces or special characters delineate the word
205                  */
206                 if (isspace((int) *p) || (*p == '&') || (*p == '|') ||
207                     (*p == '!') || (*p == '=') || (*p == '<') || (*p == '>')) {
208                         break;
209                 }
210
211                 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
212                         *error = "Unexpected start of string";
213                         return -(p - start);
214                 }
215
216                 p++;
217         }
218
219         len = p - start;
220         if (!len) {
221                 *error = "Empty string is invalid";
222                 return 0;
223         }
224
225         *out = talloc_array(ctx, char, len + 1);
226         memcpy(*out, start, len);
227         (*out)[len] = '\0';
228         return len;
229 }
230
231 /*
232  *      Less code means less bugs
233  */
234 #define return_P(_x) talloc_free(c);*error = _x;return -(p - start)
235 #define return_SLEN talloc_free(c);return slen -(p - start)
236
237
238 /** Tokenize a conditional check
239  *
240  *  @param[in] ctx for talloc
241  *  @param[in] start the start of the string to process.  Should be "(..."
242  *  @param[in] brace look for a closing brace
243  *  @param[out] pcond pointer to the returned condition structure
244  *  @param[out] error the parse error (if any)
245  *  @return length of the string skipped, or when negative, the offset to the offending error
246  */
247 static ssize_t condition_tokenize(TALLOC_CTX *ctx, char const *start, int brace, fr_cond_t **pcond, char const **error)
248 {
249         ssize_t slen;
250         const char *p = start;
251         fr_cond_t *c;
252         char *lhs, *rhs;
253         FR_TOKEN op, lhs_type, rhs_type;
254
255         c = talloc_zero(ctx, fr_cond_t);
256
257         rad_assert(c != NULL);
258
259         while (isspace((int) *p)) p++; /* skip spaces before condition */
260
261         if (!*p) {
262                 return_P("Empty condition is invalid");
263         }
264
265         /*
266          *      !COND
267          */
268         if (*p == '!') {
269                 p++;
270                 c->negate = true;
271                 while (isspace((int) *p)) p++; /* skip spaces after negation */
272
273                 /*
274                  *  Just for stupidity
275                  */
276                 if (*p == '!') {
277                         return_P("Double negation is invalid");
278                 }
279         }
280
281         /*
282          *      (COND)
283          */
284         if (*p == '(') {
285                 p++;
286
287                 /*
288                  *      We've already eaten one layer of
289                  *      brackets.  Go recurse to get more.
290                  */
291                 c->type = COND_TYPE_CHILD;
292                 slen = condition_tokenize(c, p, true, &c->data.child, error);
293                 if (slen <= 0) {
294                         return_SLEN;
295                 }
296
297                 if (!c->data.child) {
298                         return_P("Empty condition is invalid");
299                 }
300
301                 p += slen;
302                 while (isspace((int) *p)) p++; /* skip spaces after (COND)*/
303
304         } else { /* it's a bare FOO==BAR */
305                 /*
306                  *      We didn't see anything special.  The condition must be one of
307                  *
308                  *      FOO
309                  *      FOO OP BAR
310                  */
311
312                 /*
313                  *      Grab the LHS
314                  */
315                 if (*p == '/') {
316                         return_P("Conditional check cannot begin with a regular expression");
317                 }
318
319                 slen = condition_tokenize_word(c, p, &lhs, &lhs_type, error);
320                 if (slen <= 0) {
321                         return_SLEN;
322                 }
323                 p += slen;
324
325                 while (isspace((int)*p)) p++; /* skip spaces after LHS */
326
327                 /*
328                  *      We may (or not) have an operator
329                  */
330
331
332                 /*
333                  *      (FOO)
334                  */
335                 if (*p == ')') {
336                         /*
337                          *      don't skip the brace.  We'll look for it later.
338                          */
339                         goto exists;
340
341                         /*
342                          *      FOO
343                          */
344                 } else if (!*p) {
345                         if (brace) {
346                                 return_P("No closing brace at end of string");
347                         }
348
349                         goto exists;
350
351                         /*
352                          *      FOO && ...
353                          */
354                 } else if (((p[0] == '&') && (p[1] == '&')) ||
355                            ((p[0] == '|') && (p[1] == '|'))) {
356
357                 exists:
358                         c->type = COND_TYPE_EXISTS;
359                         c->data.vpt = radius_str2tmpl(c, lhs, lhs_type);
360                         if (!c->data.vpt) {
361                                 return_P("Failed creating exists");
362                         }
363
364                 } else { /* it's an operator */
365                         int regex;
366
367                         /*
368                          *      The next thing should now be a comparison operator.
369                          */
370                         regex = false;
371                         c->type = COND_TYPE_MAP;
372                         switch (*p) {
373                         default:
374                                 return_P("Invalid text. Expected comparison operator");
375
376                         case '!':
377                                 if (p[1] == '=') {
378                                         op = T_OP_NE;
379                                         p += 2;
380
381                                 } else if (p[1] == '~') {
382                                 regex = true;
383
384                                 op = T_OP_REG_NE;
385                                 p += 2;
386
387                                 } else if (p[1] == '*') {
388                                         /*
389                                          *      FOO !* BAR
390                                          *
391                                          *      is really !(FOO)
392                                          *
393                                          *      FIXME: we should
394                                          *      really re-write it...
395                                          */
396                                         op = T_OP_CMP_FALSE;
397                                         p += 2;
398
399                                 } else {
400                                         goto invalid_operator;
401                                 }
402                                 break;
403
404                         case '=':
405                                 if (p[1] == '=') {
406                                         op = T_OP_CMP_EQ;
407                                         p += 2;
408
409                                 } else if (p[1] == '~') {
410                                         regex = true;
411
412                                         op = T_OP_REG_EQ;
413                                         p += 2;
414
415                                 } else if (p[1] == '*') {
416                                         op = T_OP_CMP_TRUE;
417                                         p += 2;
418
419                                 } else {
420                                 invalid_operator:
421                                         return_P("Invalid operator");
422                                 }
423
424                                 break;
425
426                         case '<':
427                                 if (p[1] == '=') {
428                                         op = T_OP_LE;
429                                         p += 2;
430
431                                 } else {
432                                         op = T_OP_LT;
433                                         p++;
434                                 }
435                                 break;
436
437                         case '>':
438                                 if (p[1] == '=') {
439                                         op = T_OP_GE;
440                                         p += 2;
441
442                                 } else {
443                                         op = T_OP_GT;
444                                         p++;
445                                 }
446                                 break;
447                         }
448
449                         while (isspace((int) *p)) p++; /* skip spaces after operator */
450
451                         if (!*p) {
452                                 return_P("Expected text after operator");
453                         }
454
455                         /*
456                          *      Grab the RHS
457                          */
458                         slen = condition_tokenize_word(c, p, &rhs, &rhs_type, error);
459                         if (slen <= 0) {
460                                 return_SLEN;
461                         }
462
463                         /*
464                          *      Sanity checks for regexes.
465                          */
466                         if (regex) {
467                                 if (*p != '/') {
468                                         return_P("Expected regular expression");
469                                 }
470
471                                 /*
472                                  *      Allow /foo/i
473                                  */
474                                 if (p[slen] == 'i') {
475                                         c->regex_i = true;
476                                         slen++;
477                                 }
478
479                         } else if (!regex && (*p == '/')) {
480                                 return_P("Unexpected regular expression");
481                         }
482
483                         c->data.map = radius_str2map(c, lhs, lhs_type, op, rhs, rhs_type,
484                                                      REQUEST_CURRENT, PAIR_LIST_REQUEST,
485                                                      REQUEST_CURRENT, PAIR_LIST_REQUEST);
486                         if (!c->data.map) {
487                                 return_P("Failed creating check");
488                         }
489
490                         /*
491                          *      @todo: check LHS and RHS separately, to
492                          *      get better errors
493                          */
494                         if ((c->data.map->src->type == VPT_TYPE_LIST) ||
495                             (c->data.map->dst->type == VPT_TYPE_LIST)) {
496                                 talloc_free(c);
497                                 *error = "Cannot use list references in condition";
498                                 return 0;
499                         }
500
501                         if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
502                             (c->data.map->dst->type != VPT_TYPE_ATTR)) {
503                                 talloc_free(c);
504                                 *error = "Cannot use attribute reference on right side of condition";
505                                 return 0;
506                         }
507
508                         if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
509                             (c->data.map->dst->type == VPT_TYPE_ATTR) &&
510                             (c->data.map->dst->da->type != c->data.map->src->da->type)) {
511                                 talloc_free(c);
512                                 *error = "Attribute comparisons must be of the same attribute type";
513                                 return 0;
514                         }
515
516                         p += slen;
517
518                         while (isspace((int) *p)) p++; /* skip spaces after RHS */
519                 } /* parse OP RHS */
520         } /* parse a condition (COND) or FOO OP BAR*/
521
522         /*
523          *      ...COND)
524          */
525         if (*p == ')') {
526                 if (!brace) {
527                         return_P("Unexpected closing brace");
528                 }
529
530                 p++;
531                 while (isspace((int) *p)) p++; /* skip spaces after closing brace */
532                 brace = false;
533                 goto done;
534         }
535
536         /*
537          *      End of string is now allowed.
538          */
539         if (!*p) {
540                 if (brace) {
541                         return_P("No closing brace at end of string");
542                 }
543
544                 goto done;
545         }
546
547         if (!(((p[0] == '&') && (p[1] == '&')) ||
548               ((p[0] == '|') && (p[1] == '|')))) {
549                 return_P("Unexpected text after condition");
550         }
551
552         /*
553          *      Recurse to parse the next condition.
554          */
555         c->next_op = p[0];
556         p += 2;
557
558         /*
559          *      May still be looking for a closing brace.
560          */
561         slen = condition_tokenize(c, p, brace, &c->next, error);
562         if (slen <= 0) {
563                 return_SLEN;
564         }
565         p += slen;
566
567 done:
568         /*
569          *      Normalize it before returning it.
570          */
571
572         /*
573          *      (FOO)     --> FOO
574          *      (FOO) ... --> FOO ...
575          */
576         if ((c->type == COND_TYPE_CHILD) && !c->data.child->next) {
577                 fr_cond_t *child;
578
579                 child = c->data.child;
580                 child->next = c->next;
581                 child->next_op = c->next_op;
582                 c->next = NULL;
583                 c->data.child = NULL;
584
585                 /*
586                  *      Set the negation properly
587                  */
588                 if ((c->negate && !child->negate) ||
589                     (!c->negate && child->negate)) {
590                         child->negate = true;
591                 } else {
592                         child->negate = false;
593                 }
594                 
595                 (void) talloc_steal(ctx, child);
596                 talloc_free(c);
597                 c = child;
598         }
599
600         /*
601          *      (FOO ...) --> FOO ...
602          *
603          *      But don't do !(FOO || BAR) --> !FOO || BAR
604          *      Because that's different.
605          */
606         if ((c->type == COND_TYPE_CHILD) &&
607             !c->next && !c->negate) {
608                 fr_cond_t *child;
609
610                 child = c->data.child;
611                 c->data.child = NULL;
612                 (void) talloc_steal(ctx, child);
613                 talloc_free(c);
614                 c = child;
615         }
616
617         /*
618          *      Normalize negation.  This doesn't really make any
619          *      difference, but it simplifies the run-time code in
620          *      evaluate.c
621          */
622         if (c->type == COND_TYPE_MAP) {
623                 /*
624                  *      !FOO !~ BAR --> FOO =~ BAR
625                  */
626                 if (c->negate && (c->data.map->op == T_OP_REG_NE)) {
627                         c->negate = false;
628                         c->data.map->op = T_OP_REG_EQ;
629                 }
630
631                 /*
632                  *      FOO !~ BAR --> !FOO =~ BAR
633                  */
634                 if (!c->negate && (c->data.map->op == T_OP_REG_NE)) {
635                         c->negate = true;
636                         c->data.map->op = T_OP_REG_EQ;
637                 }
638
639                 /*
640                  *      !FOO != BAR --> FOO == BAR
641                  */
642                 if (c->negate && (c->data.map->op == T_OP_NE)) {
643                         c->negate = false;
644                         c->data.map->op = T_OP_CMP_EQ;
645                 }
646
647                 /*
648                  *      This next one catches "LDAP-Group != foo",
649                  *      which doesn't really work, but this hack fixes it.
650                  *
651                  *      FOO != BAR --> !FOO == BAR
652                  */
653                 if (!c->negate && (c->data.map->op == T_OP_NE)) {
654                         c->negate = true;
655                         c->data.map->op = T_OP_CMP_EQ;
656                 }
657         }
658
659         *pcond = c;
660         return p - start;
661 }
662
663 /** Tokenize a conditional check
664  *
665  *  @param[in] ctx for talloc
666  *  @param[in] start the start of the string to process.  Should be "(..."
667  *  @param[out] head the parsed condition structure
668  *  @param[out] error the parse error (if any)
669  *  @return length of the string skipped, or when negative, the offset to the offending error
670  */
671 ssize_t fr_condition_tokenize(TALLOC_CTX *ctx, char const *start, fr_cond_t **head, char const **error)
672 {
673         return condition_tokenize(ctx, start, false, head, error);
674 }