Allow casting for LHS of conditions.
[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                 if (c->cast) {
50                         len = snprintf(p, end - p, "<%s>", fr_int2str(dict_attr_types,
51                                                                       c->cast->type, "??"));
52                         p += len;
53                 }
54
55                 len = radius_tmpl2str(p, end - p, c->data.vpt);
56                 p += len;
57                 break;
58
59         case COND_TYPE_MAP:
60                 rad_assert(c->data.map != NULL);
61 #if 0
62                 *(p++) = '[';   /* for extra-clear debugging */
63 #endif
64                 if (c->cast) {
65                         len = snprintf(p, end - p, "<%s>", fr_int2str(dict_attr_types,
66                                                                       c->cast->type, "??"));
67                         p += len;
68                 }
69
70                 len = radius_map2str(p, end - p, c->data.map);
71                 p += len;
72 #if 0
73                 *(p++) = ']';
74 #endif
75                 break;
76
77         case COND_TYPE_CHILD:
78                 rad_assert(c->data.child != NULL);
79                 *(p++) = '(';
80                 len = fr_cond_sprint(p, end - p, c->data.child);
81                 p += len;
82                 *(p++) = ')';
83                 break;
84
85         default:
86                 *buffer = '\0';
87                 return 0;
88         }
89
90         if (c->next_op == COND_NONE) {
91                 rad_assert(c->next == NULL);
92                 *p = '\0';
93                 return p - buffer;
94         }
95
96         if (c->next_op == COND_AND) {
97                 strlcpy(p, " && ", end - p);
98                 p += strlen(p);
99
100         } else if (c->next_op == COND_OR) {
101                 strlcpy(p, " || ", end - p);
102                 p += strlen(p);
103
104         } else {
105                 rad_assert(0 == 1);
106         }
107
108         c = c->next;
109         goto next;
110 }
111
112
113 static ssize_t condition_tokenize_string(TALLOC_CTX *ctx, char const *start, char **out,
114                                          FR_TOKEN *op, char const **error)
115 {
116         const char *p = start;
117         char *q;
118
119         switch (*p++) {
120         default:
121                 return -1;
122
123         case '"':
124                 *op = T_DOUBLE_QUOTED_STRING;
125                 break;
126
127         case '\'':
128                 *op = T_SINGLE_QUOTED_STRING;
129                 break;
130
131         case '`':
132                 *op = T_BACK_QUOTED_STRING;
133                 break;
134
135         case '/':
136                 *op = T_OP_REG_EQ; /* a bit of a hack. */
137                 break;
138
139         }
140
141         *out = talloc_array(ctx, char, strlen(start) - 1); /* + 2 - 1 */
142         if (!*out) return -1;
143
144         q = *out;
145
146         while (*p) {
147                 if (*p == *start) {
148                         *q = '\0';
149                         p++;
150                         return (p - start);
151                 }
152
153                 if (*p == '\\') {
154                         p++;
155                         if (!*p) {
156                                 *error = "End of string after escape";
157                                 return -(p - start);
158                         }
159
160                         switch (*p) {
161                         case 'r':
162                                 *q++ = '\r';
163                                 break;
164                         case 'n':
165                                 *q++ = '\n';
166                                 break;
167                         case 't':
168                                 *q++ = '\t';
169                                 break;
170                         default:
171                                 *q++ = *p;
172                                 break;
173                         }
174                         p++;
175                         continue;
176                 }
177         
178                 *(q++) = *(p++);
179         }
180
181         *error = "Unterminated string";
182         return -1;
183 }
184
185 static ssize_t condition_tokenize_word(TALLOC_CTX *ctx, char const *start, char **out,
186                                        FR_TOKEN *op, char const **error)
187 {
188         size_t len;
189         char const *p = start;
190
191         if ((*p == '"') || (*p == '\'') || (*p == '`') || (*p == '/')) {
192                 return condition_tokenize_string(ctx, start, out, op, error);
193         }
194
195         *op = T_BARE_WORD;
196         if (*p == '&') p++;     /* special-case &User-Name */
197
198         while (*p) {
199                 /*
200                  *      The LHS should really be limited to only a few
201                  *      things.  For now, we allow pretty much anything.
202                  */
203                 if (*p == '\\') {
204                         *error = "Unexpected escape";
205                         return -(p - start);
206                 }
207
208                 /*
209                  *      ("foo") is valid.
210                  */
211                 if (*p == ')') {
212                         break;
213                 }
214
215                 /*
216                  *      Spaces or special characters delineate the word
217                  */
218                 if (isspace((int) *p) || (*p == '&') || (*p == '|') ||
219                     (*p == '!') || (*p == '=') || (*p == '<') || (*p == '>')) {
220                         break;
221                 }
222
223                 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
224                         *error = "Unexpected start of string";
225                         return -(p - start);
226                 }
227
228                 p++;
229         }
230
231         len = p - start;
232         if (!len) {
233                 *error = "Empty string is invalid";
234                 return 0;
235         }
236
237         *out = talloc_array(ctx, char, len + 1);
238         memcpy(*out, start, len);
239         (*out)[len] = '\0';
240         return len;
241 }
242
243
244 static ssize_t condition_tokenize_cast(char const *start, DICT_ATTR const **pda, char const **error)
245 {
246         char const *p = start;
247         char const *q;
248         PW_TYPE cast;
249
250         while (isspace((int) *p)) p++; /* skip spaces before condition */
251
252         if (*p != '<') return 0;
253         p++;
254
255         q = p;
256         while (*q && *q != '>') q++;
257
258         cast = fr_substr2int(dict_attr_types, p, PW_TYPE_INVALID, q - p);
259         if (cast == PW_TYPE_INVALID) {
260                 *error = "Invalid data type in cast";
261                 return -(p - start);
262         }
263
264         *pda = dict_attrbyvalue(1850 + cast, 0);
265         if (!*pda) {
266                 *error = "Cannot cast to this data type";
267                 return -(p - start);
268         }
269
270         q++;
271
272         while (isspace((int) *q)) q++; /* skip spaces after cast */
273
274         return q - start;
275 }
276
277 /*
278  *      Less code means less bugs
279  */
280 #define return_P(_x) talloc_free(c);*error = _x;return -(p - start)
281 #define return_SLEN talloc_free(c);return slen -(p - start)
282
283
284 /** Tokenize a conditional check
285  *
286  *  @param[in] ctx for talloc
287  *  @param[in] start the start of the string to process.  Should be "(..."
288  *  @param[in] brace look for a closing brace
289  *  @param[out] pcond pointer to the returned condition structure
290  *  @param[out] error the parse error (if any)
291  *  @return length of the string skipped, or when negative, the offset to the offending error
292  */
293 static ssize_t condition_tokenize(TALLOC_CTX *ctx, char const *start, int brace, fr_cond_t **pcond, char const **error)
294 {
295         ssize_t slen;
296         const char *p = start;
297         fr_cond_t *c;
298         char *lhs, *rhs;
299         FR_TOKEN op, lhs_type, rhs_type;
300
301         c = talloc_zero(ctx, fr_cond_t);
302
303         rad_assert(c != NULL);
304
305         while (isspace((int) *p)) p++; /* skip spaces before condition */
306
307         if (!*p) {
308                 return_P("Empty condition is invalid");
309         }
310
311         /*
312          *      !COND
313          */
314         if (*p == '!') {
315                 p++;
316                 c->negate = true;
317                 while (isspace((int) *p)) p++; /* skip spaces after negation */
318
319                 /*
320                  *  Just for stupidity
321                  */
322                 if (*p == '!') {
323                         return_P("Double negation is invalid");
324                 }
325         }
326
327         /*
328          *      (COND)
329          */
330         if (*p == '(') {
331                 p++;
332
333                 /*
334                  *      We've already eaten one layer of
335                  *      brackets.  Go recurse to get more.
336                  */
337                 c->type = COND_TYPE_CHILD;
338                 slen = condition_tokenize(c, p, true, &c->data.child, error);
339                 if (slen <= 0) {
340                         return_SLEN;
341                 }
342
343                 if (!c->data.child) {
344                         return_P("Empty condition is invalid");
345                 }
346
347                 p += slen;
348                 while (isspace((int) *p)) p++; /* skip spaces after (COND)*/
349
350         } else { /* it's a bare FOO==BAR */
351                 /*
352                  *      We didn't see anything special.  The condition must be one of
353                  *
354                  *      FOO
355                  *      FOO OP BAR
356                  */
357
358                 /*
359                  *      Grab the LHS
360                  */
361                 if (*p == '/') {
362                         return_P("Conditional check cannot begin with a regular expression");
363                 }
364
365                 slen = condition_tokenize_cast(p, &c->cast, error);
366                 if (slen < 0) {
367                         return_SLEN;
368                 }
369                 p += slen;
370
371                 slen = condition_tokenize_word(c, p, &lhs, &lhs_type, error);
372                 if (slen <= 0) {
373                         return_SLEN;
374                 }
375                 p += slen;
376
377                 while (isspace((int)*p)) p++; /* skip spaces after LHS */
378
379                 /*
380                  *      We may (or not) have an operator
381                  */
382
383
384                 /*
385                  *      (FOO)
386                  */
387                 if (*p == ')') {
388                         /*
389                          *      don't skip the brace.  We'll look for it later.
390                          */
391                         goto exists;
392
393                         /*
394                          *      FOO
395                          */
396                 } else if (!*p) {
397                         if (brace) {
398                                 return_P("No closing brace at end of string");
399                         }
400
401                         goto exists;
402
403                         /*
404                          *      FOO && ...
405                          */
406                 } else if (((p[0] == '&') && (p[1] == '&')) ||
407                            ((p[0] == '|') && (p[1] == '|'))) {
408
409                 exists:
410                         if (c->cast) {
411                                 talloc_free(c);
412                                 *error = "Cannot do cast for existence check";
413                                 return 0;
414                         }
415
416                         c->type = COND_TYPE_EXISTS;
417                         c->data.vpt = radius_str2tmpl(c, lhs, lhs_type);
418                         if (!c->data.vpt) {
419                                 return_P("Failed creating exists");
420                         }
421
422                 } else { /* it's an operator */
423                         int regex;
424
425                         /*
426                          *      The next thing should now be a comparison operator.
427                          */
428                         regex = false;
429                         c->type = COND_TYPE_MAP;
430                         switch (*p) {
431                         default:
432                                 return_P("Invalid text. Expected comparison operator");
433
434                         case '!':
435                                 if (p[1] == '=') {
436                                         op = T_OP_NE;
437                                         p += 2;
438
439                                 } else if (p[1] == '~') {
440                                 regex = true;
441
442                                 op = T_OP_REG_NE;
443                                 p += 2;
444
445                                 } else if (p[1] == '*') {
446                                         /*
447                                          *      FOO !* BAR
448                                          *
449                                          *      is really !(FOO)
450                                          *
451                                          *      FIXME: we should
452                                          *      really re-write it...
453                                          */
454                                         op = T_OP_CMP_FALSE;
455                                         p += 2;
456
457                                 } else {
458                                         goto invalid_operator;
459                                 }
460                                 break;
461
462                         case '=':
463                                 if (p[1] == '=') {
464                                         op = T_OP_CMP_EQ;
465                                         p += 2;
466
467                                 } else if (p[1] == '~') {
468                                         regex = true;
469
470                                         op = T_OP_REG_EQ;
471                                         p += 2;
472
473                                 } else if (p[1] == '*') {
474                                         op = T_OP_CMP_TRUE;
475                                         p += 2;
476
477                                 } else {
478                                 invalid_operator:
479                                         return_P("Invalid operator");
480                                 }
481
482                                 break;
483
484                         case '<':
485                                 if (p[1] == '=') {
486                                         op = T_OP_LE;
487                                         p += 2;
488
489                                 } else {
490                                         op = T_OP_LT;
491                                         p++;
492                                 }
493                                 break;
494
495                         case '>':
496                                 if (p[1] == '=') {
497                                         op = T_OP_GE;
498                                         p += 2;
499
500                                 } else {
501                                         op = T_OP_GT;
502                                         p++;
503                                 }
504                                 break;
505                         }
506
507                         while (isspace((int) *p)) p++; /* skip spaces after operator */
508
509                         if (!*p) {
510                                 return_P("Expected text after operator");
511                         }
512
513                         /*
514                          *      Cannot have a cast on the RHS
515                          */
516                         if (*p == '<') {
517                                 return_P("Unexpected cast");
518                         }
519
520                         /*
521                          *      Grab the RHS
522                          */
523                         slen = condition_tokenize_word(c, p, &rhs, &rhs_type, error);
524                         if (slen <= 0) {
525                                 return_SLEN;
526                         }
527
528                         /*
529                          *      Sanity checks for regexes.
530                          */
531                         if (regex) {
532                                 if (*p != '/') {
533                                         return_P("Expected regular expression");
534                                 }
535
536                                 /*
537                                  *      Allow /foo/i
538                                  */
539                                 if (p[slen] == 'i') {
540                                         c->regex_i = true;
541                                         slen++;
542                                 }
543
544                         } else if (!regex && (*p == '/')) {
545                                 return_P("Unexpected regular expression");
546                         }
547
548                         c->data.map = radius_str2map(c, lhs, lhs_type, op, rhs, rhs_type,
549                                                      REQUEST_CURRENT, PAIR_LIST_REQUEST,
550                                                      REQUEST_CURRENT, PAIR_LIST_REQUEST);
551                         if (!c->data.map) {
552                                 return_P("Failed creating check");
553                         }
554
555                         /*
556                          *      @todo: check LHS and RHS separately, to
557                          *      get better errors
558                          */
559                         if ((c->data.map->src->type == VPT_TYPE_LIST) ||
560                             (c->data.map->dst->type == VPT_TYPE_LIST)) {
561                                 talloc_free(c);
562                                 *error = "Cannot use list references in condition";
563                                 return 0;
564                         }
565
566                         /*
567                          *      Check cast type.  We can have the RHS
568                          *      a string if the LHS has a cast.  But
569                          *      if the RHS is an attr, it MUST be the
570                          *      same type as the LHS.
571                          */
572                         if (c->cast) {
573                                 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
574                                     (c->cast->type != c->data.map->src->da->type)) {
575                                         goto same_type;
576                                 }
577
578                                 if (c->data.map->src->type == VPT_TYPE_REGEX) {
579                                         talloc_free(c);
580                                         *error = "Cannot use cast with regex comparison";
581                                         return 0;
582                                 }
583
584                         } else {
585                                 /*
586                                  *      Without a cast, we can't compare "foo" to User-Name,
587                                  *      it has to be done the other way around.
588                                  */
589                                 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
590                                     (c->data.map->dst->type != VPT_TYPE_ATTR)) {
591                                         talloc_free(c);
592                                         *error = "Cannot use attribute reference on right side of condition";
593                                         return 0;
594                                 }
595
596                                 /*
597                                  *      Two attributes?  They must be of the same type
598                                  */
599                                 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
600                                     (c->data.map->dst->type == VPT_TYPE_ATTR) &&
601                                     (c->data.map->dst->da->type != c->data.map->src->da->type)) {
602                                 same_type:
603                                         talloc_free(c);
604                                         *error = "Attribute comparisons must be of the same attribute type";
605                                         return 0;
606                                 }
607                         }
608                         p += slen;
609
610                         while (isspace((int) *p)) p++; /* skip spaces after RHS */
611                 } /* parse OP RHS */
612         } /* parse a condition (COND) or FOO OP BAR*/
613
614         /*
615          *      ...COND)
616          */
617         if (*p == ')') {
618                 if (!brace) {
619                         return_P("Unexpected closing brace");
620                 }
621
622                 p++;
623                 while (isspace((int) *p)) p++; /* skip spaces after closing brace */
624                 brace = false;
625                 goto done;
626         }
627
628         /*
629          *      End of string is now allowed.
630          */
631         if (!*p) {
632                 if (brace) {
633                         return_P("No closing brace at end of string");
634                 }
635
636                 goto done;
637         }
638
639         if (!(((p[0] == '&') && (p[1] == '&')) ||
640               ((p[0] == '|') && (p[1] == '|')))) {
641                 return_P("Unexpected text after condition");
642         }
643
644         /*
645          *      Recurse to parse the next condition.
646          */
647         c->next_op = p[0];
648         p += 2;
649
650         /*
651          *      May still be looking for a closing brace.
652          */
653         slen = condition_tokenize(c, p, brace, &c->next, error);
654         if (slen <= 0) {
655                 return_SLEN;
656         }
657         p += slen;
658
659 done:
660         /*
661          *      Normalize it before returning it.
662          */
663
664         /*
665          *      (FOO)     --> FOO
666          *      (FOO) ... --> FOO ...
667          */
668         if ((c->type == COND_TYPE_CHILD) && !c->data.child->next) {
669                 fr_cond_t *child;
670
671                 child = talloc_steal(ctx, c->data.child);
672                 c->data.child = NULL;
673
674                 child->next = talloc_steal(child, c->next);
675                 c->next = NULL;
676
677                 child->next_op = c->next_op;
678
679                 /*
680                  *      Set the negation properly
681                  */
682                 if ((c->negate && !child->negate) ||
683                     (!c->negate && child->negate)) {
684                         child->negate = true;
685                 } else {
686                         child->negate = false;
687                 }
688
689                 talloc_free(c);
690                 c = child;
691         }
692
693         /*
694          *      (FOO ...) --> FOO ...
695          *
696          *      But don't do !(FOO || BAR) --> !FOO || BAR
697          *      Because that's different.
698          */
699         if ((c->type == COND_TYPE_CHILD) &&
700             !c->next && !c->negate) {
701                 fr_cond_t *child;
702
703                 child = talloc_steal(ctx, c->data.child);
704                 c->data.child = NULL;
705
706                 talloc_free(c);
707                 c = child;
708         }
709
710         /*
711          *      Normalize negation.  This doesn't really make any
712          *      difference, but it simplifies the run-time code in
713          *      evaluate.c
714          */
715         if (c->type == COND_TYPE_MAP) {
716                 /*
717                  *      !FOO !~ BAR --> FOO =~ BAR
718                  */
719                 if (c->negate && (c->data.map->op == T_OP_REG_NE)) {
720                         c->negate = false;
721                         c->data.map->op = T_OP_REG_EQ;
722                 }
723
724                 /*
725                  *      FOO !~ BAR --> !FOO =~ BAR
726                  */
727                 if (!c->negate && (c->data.map->op == T_OP_REG_NE)) {
728                         c->negate = true;
729                         c->data.map->op = T_OP_REG_EQ;
730                 }
731
732                 /*
733                  *      !FOO != BAR --> FOO == BAR
734                  */
735                 if (c->negate && (c->data.map->op == T_OP_NE)) {
736                         c->negate = false;
737                         c->data.map->op = T_OP_CMP_EQ;
738                 }
739
740                 /*
741                  *      This next one catches "LDAP-Group != foo",
742                  *      which doesn't really work, but this hack fixes it.
743                  *
744                  *      FOO != BAR --> !FOO == BAR
745                  */
746                 if (!c->negate && (c->data.map->op == T_OP_NE)) {
747                         c->negate = true;
748                         c->data.map->op = T_OP_CMP_EQ;
749                 }
750         }
751
752         *pcond = c;
753         return p - start;
754 }
755
756 /** Tokenize a conditional check
757  *
758  *  @param[in] ctx for talloc
759  *  @param[in] start the start of the string to process.  Should be "(..."
760  *  @param[out] head the parsed condition structure
761  *  @param[out] error the parse error (if any)
762  *  @return length of the string skipped, or when negative, the offset to the offending error
763  */
764 ssize_t fr_condition_tokenize(TALLOC_CTX *ctx, char const *start, fr_cond_t **head, char const **error)
765 {
766         return condition_tokenize(ctx, start, false, head, error);
767 }