Redundant casts get squashed
[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) *error = _x;goto return_p
281 #define return_SLEN goto return_slen
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         lhs = rhs = NULL;
305
306         while (isspace((int) *p)) p++; /* skip spaces before condition */
307
308         if (!*p) {
309                 return_P("Empty condition is invalid");
310         }
311
312         /*
313          *      !COND
314          */
315         if (*p == '!') {
316                 p++;
317                 c->negate = true;
318                 while (isspace((int) *p)) p++; /* skip spaces after negation */
319
320                 /*
321                  *  Just for stupidity
322                  */
323                 if (*p == '!') {
324                         return_P("Double negation is invalid");
325                 }
326         }
327
328         /*
329          *      (COND)
330          */
331         if (*p == '(') {
332                 p++;
333
334                 /*
335                  *      We've already eaten one layer of
336                  *      brackets.  Go recurse to get more.
337                  */
338                 c->type = COND_TYPE_CHILD;
339                 slen = condition_tokenize(c, p, true, &c->data.child, error);
340                 if (slen <= 0) {
341                         return_SLEN;
342                 }
343
344                 if (!c->data.child) {
345                         return_P("Empty condition is invalid");
346                 }
347
348                 p += slen;
349                 while (isspace((int) *p)) p++; /* skip spaces after (COND)*/
350
351         } else { /* it's a bare FOO==BAR */
352                 /*
353                  *      We didn't see anything special.  The condition must be one of
354                  *
355                  *      FOO
356                  *      FOO OP BAR
357                  */
358
359                 /*
360                  *      Grab the LHS
361                  */
362                 if (*p == '/') {
363                         return_P("Conditional check cannot begin with a regular expression");
364                 }
365
366                 slen = condition_tokenize_cast(p, &c->cast, error);
367                 if (slen < 0) {
368                         return_SLEN;
369                 }
370                 p += slen;
371
372                 slen = condition_tokenize_word(c, p, &lhs, &lhs_type, error);
373                 if (slen <= 0) {
374                         return_SLEN;
375                 }
376                 p += slen;
377
378                 while (isspace((int)*p)) p++; /* skip spaces after LHS */
379
380                 /*
381                  *      We may (or not) have an operator
382                  */
383
384
385                 /*
386                  *      (FOO)
387                  */
388                 if (*p == ')') {
389                         /*
390                          *      don't skip the brace.  We'll look for it later.
391                          */
392                         goto exists;
393
394                         /*
395                          *      FOO
396                          */
397                 } else if (!*p) {
398                         if (brace) {
399                                 return_P("No closing brace at end of string");
400                         }
401
402                         goto exists;
403
404                         /*
405                          *      FOO && ...
406                          */
407                 } else if (((p[0] == '&') && (p[1] == '&')) ||
408                            ((p[0] == '|') && (p[1] == '|'))) {
409
410                 exists:
411                         if (c->cast) {
412                                 talloc_free(c);
413                                 *error = "Cannot do cast for existence check";
414                                 return 0;
415                         }
416
417                         c->type = COND_TYPE_EXISTS;
418                         c->data.vpt = radius_str2tmpl(c, lhs, lhs_type);
419                         if (!c->data.vpt) {
420                                 return_P("Failed creating exists");
421                         }
422
423                 } else { /* it's an operator */
424                         int regex;
425
426                         /*
427                          *      The next thing should now be a comparison operator.
428                          */
429                         regex = false;
430                         c->type = COND_TYPE_MAP;
431                         switch (*p) {
432                         default:
433                                 return_P("Invalid text. Expected comparison operator");
434
435                         case '!':
436                                 if (p[1] == '=') {
437                                         op = T_OP_NE;
438                                         p += 2;
439
440                                 } else if (p[1] == '~') {
441                                 regex = true;
442
443                                 op = T_OP_REG_NE;
444                                 p += 2;
445
446                                 } else if (p[1] == '*') {
447                                         if (lhs_type != T_BARE_WORD) {
448                                                 return_P("Cannot use !* on a string");
449                                         }
450
451                                         op = T_OP_CMP_FALSE;
452                                         p += 2;
453
454                                 } else {
455                                         goto invalid_operator;
456                                 }
457                                 break;
458
459                         case '=':
460                                 if (p[1] == '=') {
461                                         op = T_OP_CMP_EQ;
462                                         p += 2;
463
464                                 } else if (p[1] == '~') {
465                                         regex = true;
466
467                                         op = T_OP_REG_EQ;
468                                         p += 2;
469
470                                 } else if (p[1] == '*') {
471                                         if (lhs_type != T_BARE_WORD) {
472                                                 return_P("Cannot use =* on a string");
473                                         }
474
475                                         op = T_OP_CMP_TRUE;
476                                         p += 2;
477
478                                 } else {
479                                 invalid_operator:
480                                         return_P("Invalid operator");
481                                 }
482
483                                 break;
484
485                         case '<':
486                                 if (p[1] == '=') {
487                                         op = T_OP_LE;
488                                         p += 2;
489
490                                 } else {
491                                         op = T_OP_LT;
492                                         p++;
493                                 }
494                                 break;
495
496                         case '>':
497                                 if (p[1] == '=') {
498                                         op = T_OP_GE;
499                                         p += 2;
500
501                                 } else {
502                                         op = T_OP_GT;
503                                         p++;
504                                 }
505                                 break;
506                         }
507
508                         while (isspace((int) *p)) p++; /* skip spaces after operator */
509
510                         if (!*p) {
511                                 return_P("Expected text after operator");
512                         }
513
514                         /*
515                          *      Cannot have a cast on the RHS
516                          */
517                         if (*p == '<') {
518                                 return_P("Unexpected cast");
519                         }
520
521                         /*
522                          *      Grab the RHS
523                          */
524                         slen = condition_tokenize_word(c, p, &rhs, &rhs_type, error);
525                         if (slen <= 0) {
526                                 return_SLEN;
527                         }
528
529                         /*
530                          *      Sanity checks for regexes.
531                          */
532                         if (regex) {
533                                 if (*p != '/') {
534                                         return_P("Expected regular expression");
535                                 }
536
537                                 /*
538                                  *      Allow /foo/i
539                                  */
540                                 if (p[slen] == 'i') {
541                                         c->regex_i = true;
542                                         slen++;
543                                 }
544
545                         } else if (!regex && (*p == '/')) {
546                                 return_P("Unexpected regular expression");
547                         }
548
549                         c->data.map = radius_str2map(c, lhs, lhs_type, op, rhs, rhs_type,
550                                                      REQUEST_CURRENT, PAIR_LIST_REQUEST,
551                                                      REQUEST_CURRENT, PAIR_LIST_REQUEST);
552                         if (!c->data.map) {
553                                 return_P("Failed creating check");
554                         }
555
556                         /*
557                          *      foo =* bar is just (foo)
558                          *      foo !* bar is just (!foo)
559                          */
560                         if ((op == T_OP_CMP_TRUE) || (op == T_OP_CMP_FALSE)) {
561                                 value_pair_tmpl_t *vpt;
562
563                                 vpt = talloc_steal(c, c->data.map->dst);
564                                 c->data.map->dst = NULL;
565
566                                 talloc_free(c->data.map);
567                                 c->type = COND_TYPE_EXISTS;
568                                 c->data.vpt = vpt;
569
570                                 /*
571                                  *      Invert the negation bit.
572                                  */
573                                 if (op == T_OP_CMP_FALSE) {
574                                         c->negate = !c->negate;
575                                 }
576
577                                 goto done_cond;
578                         }
579
580                         /*
581                          *      @todo: check LHS and RHS separately, to
582                          *      get better errors
583                          */
584                         if ((c->data.map->src->type == VPT_TYPE_LIST) ||
585                             (c->data.map->dst->type == VPT_TYPE_LIST)) {
586                                 talloc_free(c);
587                                 *error = "Cannot use list references in condition";
588                                 return 0;
589                         }
590
591                         /*
592                          *      Check cast type.  We can have the RHS
593                          *      a string if the LHS has a cast.  But
594                          *      if the RHS is an attr, it MUST be the
595                          *      same type as the LHS.
596                          */
597                         if (c->cast) {
598                                 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
599                                     (c->cast->type != c->data.map->src->da->type)) {
600                                         goto same_type;
601                                 }
602
603                                 if (c->data.map->src->type == VPT_TYPE_REGEX) {
604                                         talloc_free(c);
605                                         *error = "Cannot use cast with regex comparison";
606                                         return 0;
607                                 }
608
609                                 /*
610                                  *      Casting to a redundant type means we don't need the cast.
611                                  */
612                                 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
613                                     (c->cast->type == c->data.map->dst->da->type)) {
614                                         c->cast = NULL;
615                                 }
616
617                         } else {
618                                 /*
619                                  *      Without a cast, we can't compare "foo" to User-Name,
620                                  *      it has to be done the other way around.
621                                  */
622                                 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
623                                     (c->data.map->dst->type != VPT_TYPE_ATTR)) {
624                                         talloc_free(c);
625                                         *error = "Cannot use attribute reference on right side of condition";
626                                         return 0;
627                                 }
628
629                                 /*
630                                  *      Two attributes?  They must be of the same type
631                                  */
632                                 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
633                                     (c->data.map->dst->type == VPT_TYPE_ATTR) &&
634                                     (c->data.map->dst->da->type != c->data.map->src->da->type)) {
635                                 same_type:
636                                         talloc_free(c);
637                                         *error = "Attribute comparisons must be of the same attribute type";
638                                         return 0;
639                                 }
640                         }
641
642                 done_cond:
643                         p += slen;
644
645                         while (isspace((int) *p)) p++; /* skip spaces after RHS */
646                 } /* parse OP RHS */
647         } /* parse a condition (COND) or FOO OP BAR*/
648
649         /*
650          *      ...COND)
651          */
652         if (*p == ')') {
653                 if (!brace) {
654                         return_P("Unexpected closing brace");
655                 }
656
657                 p++;
658                 while (isspace((int) *p)) p++; /* skip spaces after closing brace */
659                 brace = false;
660                 goto done;
661         }
662
663         /*
664          *      End of string is now allowed.
665          */
666         if (!*p) {
667                 if (brace) {
668                         return_P("No closing brace at end of string");
669                 }
670
671                 goto done;
672         }
673
674         if (!(((p[0] == '&') && (p[1] == '&')) ||
675               ((p[0] == '|') && (p[1] == '|')))) {
676                 *error = "Unexpected text after condition";
677         return_p:
678                 if (lhs) talloc_free(lhs);
679                 if (rhs) talloc_free(rhs);
680                 talloc_free(c);
681                 return -(p - start);
682         }
683
684         /*
685          *      Recurse to parse the next condition.
686          */
687         c->next_op = p[0];
688         p += 2;
689
690         /*
691          *      May still be looking for a closing brace.
692          */
693         slen = condition_tokenize(c, p, brace, &c->next, error);
694         if (slen <= 0) {
695         return_slen:
696                 if (lhs) talloc_free(lhs);
697                 if (rhs) talloc_free(rhs);
698                 talloc_free(c);
699                 return slen - (p - start);
700         }
701         p += slen;
702
703 done:
704         /*
705          *      Normalize it before returning it.
706          */
707
708         /*
709          *      (FOO)     --> FOO
710          *      (FOO) ... --> FOO ...
711          */
712         if ((c->type == COND_TYPE_CHILD) && !c->data.child->next) {
713                 fr_cond_t *child;
714
715                 child = talloc_steal(ctx, c->data.child);
716                 c->data.child = NULL;
717
718                 child->next = talloc_steal(child, c->next);
719                 c->next = NULL;
720
721                 child->next_op = c->next_op;
722
723                 /*
724                  *      Set the negation properly
725                  */
726                 if ((c->negate && !child->negate) ||
727                     (!c->negate && child->negate)) {
728                         child->negate = true;
729                 } else {
730                         child->negate = false;
731                 }
732
733                 talloc_free(c);
734                 c = child;
735         }
736
737         /*
738          *      (FOO ...) --> FOO ...
739          *
740          *      But don't do !(FOO || BAR) --> !FOO || BAR
741          *      Because that's different.
742          */
743         if ((c->type == COND_TYPE_CHILD) &&
744             !c->next && !c->negate) {
745                 fr_cond_t *child;
746
747                 child = talloc_steal(ctx, c->data.child);
748                 c->data.child = NULL;
749
750                 talloc_free(c);
751                 c = child;
752         }
753
754         /*
755          *      Normalize negation.  This doesn't really make any
756          *      difference, but it simplifies the run-time code in
757          *      evaluate.c
758          */
759         if (c->type == COND_TYPE_MAP) {
760                 /*
761                  *      !FOO !~ BAR --> FOO =~ BAR
762                  */
763                 if (c->negate && (c->data.map->op == T_OP_REG_NE)) {
764                         c->negate = false;
765                         c->data.map->op = T_OP_REG_EQ;
766                 }
767
768                 /*
769                  *      FOO !~ BAR --> !FOO =~ BAR
770                  */
771                 if (!c->negate && (c->data.map->op == T_OP_REG_NE)) {
772                         c->negate = true;
773                         c->data.map->op = T_OP_REG_EQ;
774                 }
775
776                 /*
777                  *      !FOO != BAR --> FOO == BAR
778                  */
779                 if (c->negate && (c->data.map->op == T_OP_NE)) {
780                         c->negate = false;
781                         c->data.map->op = T_OP_CMP_EQ;
782                 }
783
784                 /*
785                  *      This next one catches "LDAP-Group != foo",
786                  *      which doesn't really work, but this hack fixes it.
787                  *
788                  *      FOO != BAR --> !FOO == BAR
789                  */
790                 if (!c->negate && (c->data.map->op == T_OP_NE)) {
791                         c->negate = true;
792                         c->data.map->op = T_OP_CMP_EQ;
793                 }
794         }
795
796         if (lhs) talloc_free(lhs);
797         if (rhs) talloc_free(rhs);
798
799         *pcond = c;
800         return p - start;
801 }
802
803 /** Tokenize a conditional check
804  *
805  *  @param[in] ctx for talloc
806  *  @param[in] start the start of the string to process.  Should be "(..."
807  *  @param[out] head the parsed condition structure
808  *  @param[out] error the parse error (if any)
809  *  @return length of the string skipped, or when negative, the offset to the offending error
810  */
811 ssize_t fr_condition_tokenize(TALLOC_CTX *ctx, char const *start, fr_cond_t **head, char const **error)
812 {
813         return condition_tokenize(ctx, start, false, head, error);
814 }