Turn on super pedantic warnings in CLANG
[freeradius.git] / src / modules / rlm_expr / rlm_expr.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15
16 /**
17  * $Id$
18  * @file rlm_expr.c
19  * @brief Register many xlat expansions including the expr expansion.
20  *
21  * @copyright 2001,2006  The FreeRADIUS server project
22  * @copyright 2002  Alan DeKok <aland@ox.org>
23  */
24 RCSID("$Id$")
25 USES_APPLE_DEPRECATED_API
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/md5.h>
29 #include <freeradius-devel/sha1.h>
30 #include <freeradius-devel/base64.h>
31 #include <freeradius-devel/modules.h>
32
33 #ifdef HAVE_OPENSSL_EVP_H
34 #  include <openssl/evp.h>
35 #endif
36
37 #include <ctype.h>
38
39 #include "rlm_expr.h"
40
41 /*
42  *      Define a structure for our module configuration.
43  */
44 typedef struct rlm_expr_t {
45         char const *xlat_name;
46         char const *allowed_chars;
47 } rlm_expr_t;
48
49 static const CONF_PARSER module_config[] = {
50         { "safe_characters", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_expr_t, allowed_chars), "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" },
51         {NULL, -1, 0, NULL, NULL}
52 };
53
54 /*
55  *      Lookup tables for randstr char classes
56  */
57 static char randstr_punc[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
58 static char randstr_salt[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz/.";
59
60 /*
61  *      Characters humans rarely confuse. Reduces char set considerably
62  *      should only be used for things such as one time passwords.
63  */
64 static char randstr_otp[] = "469ACGHJKLMNPQRUVWXYabdfhijkprstuvwxyz";
65
66 static char const hextab[] = "0123456789abcdef";
67
68 /** Calculate powers
69  *
70  * @author Orson Peters
71  * @note Borrowed from the gist here: https://gist.github.com/nightcracker/3551590.
72  *
73  * @param base a 32bit signed integer.
74  * @param exp amount to raise base by.
75  * @return base ^ pow, or 0 on underflow/overflow.
76  */
77 static int64_t fr_pow(int64_t base, int64_t exp)
78 {
79         static const uint8_t highest_bit_set[] = {
80                 0, 1, 2, 2, 3, 3, 3, 3,
81                 4, 4, 4, 4, 4, 4, 4, 4,
82                 5, 5, 5, 5, 5, 5, 5, 5,
83                 5, 5, 5, 5, 5, 5, 5, 5,
84                 6, 6, 6, 6, 6, 6, 6, 6,
85                 6, 6, 6, 6, 6, 6, 6, 6,
86                 6, 6, 6, 6, 6, 6, 6, 6,
87                 6, 6, 6, 6, 6, 6, 6, 6 // anything past 63 is a guaranteed overflow with base > 1
88         };
89
90         int64_t result = 1;
91
92         if (exp > 63) {
93                 if (base == 1) {
94                         return 1;
95                 }
96
97                 if (base == -1) {
98                         return 1 - 2 * (exp & 1);
99                 }
100                 return 0;       /* overflow */
101         }
102
103         switch (highest_bit_set[exp]) {
104         case 6:
105                 if (exp & 1) result *= base;
106                 exp >>= 1;
107                 base *= base;
108         case 5:
109                 if (exp & 1) result *= base;
110                 exp >>= 1;
111                 base *= base;
112         case 4:
113                 if (exp & 1) result *= base;
114                 exp >>= 1;
115                 base *= base;
116         case 3:
117                 if (exp & 1) result *= base;
118                 exp >>= 1;
119                 base *= base;
120         case 2:
121                 if (exp & 1) result *= base;
122                 exp >>= 1;
123                 base *= base;
124         case 1:
125                 if (exp & 1) result *= base;
126         default:
127                 return result;
128         }
129 }
130
131 /*
132  *      Start of expression calculator.
133  */
134 typedef enum expr_token_t {
135         TOKEN_NONE = 0,
136         TOKEN_INTEGER,
137
138         TOKEN_AND,
139         TOKEN_OR,
140
141         TOKEN_LSHIFT,
142         TOKEN_RSHIFT,
143
144         TOKEN_ADD,
145         TOKEN_SUBTRACT,
146
147         TOKEN_DIVIDE,
148         TOKEN_REMAINDER,
149         TOKEN_MULTIPLY,
150
151         TOKEN_POWER,
152         TOKEN_LAST
153 } expr_token_t;
154
155 static int precedence[TOKEN_LAST + 1] = {
156         0, 0, 1, 1,             /* and or */
157         2, 2, 3, 3,             /* shift add */
158         4, 4, 4, 5,             /* mul, pow */
159         0
160 };
161
162 typedef struct expr_map_t {
163         char op;
164         expr_token_t token;
165 } expr_map_t;
166
167 static expr_map_t map[] =
168 {
169         {'+',   TOKEN_ADD },
170         {'-',   TOKEN_SUBTRACT },
171         {'/',   TOKEN_DIVIDE },
172         {'*',   TOKEN_MULTIPLY },
173         {'%',   TOKEN_REMAINDER },
174         {'&',   TOKEN_AND },
175         {'|',   TOKEN_OR },
176         {'^',   TOKEN_POWER },
177         {0,     TOKEN_LAST}
178 };
179
180 static bool get_expression(REQUEST *request, char const **string, int64_t *answer, expr_token_t prev);
181
182 static bool get_number(REQUEST *request, char const **string, int64_t *answer)
183 {
184         int64_t x;
185         bool invert = false;
186         bool negative = false;
187         char const *p = *string;
188
189         /*
190          *      Look for a number.
191          */
192         while (isspace((int) *p)) p++;
193
194         /*
195          *      ~1 == 0xff...ffe
196          */
197         if (*p == '~') {
198                 invert = true;
199                 p++;
200         }
201
202         /*
203          *  No algrebraic operator found, the next thing
204          *  MUST be a number.
205          *
206          *  If it isn't, then we die.
207          */
208         if ((*p == '0') && (p[1] == 'x')) {
209                 char *end;
210
211                 x = strtoul(p, &end, 16);
212                 p = end;
213                 goto done;
214         }
215
216         /*
217          *      Look for an attribute.
218          */
219         if (*p == '&') {
220                 ssize_t slen;
221                 VALUE_PAIR *vp;
222                 value_pair_tmpl_t vpt;
223
224                 p += 1;
225
226                 slen = tmpl_from_attr_substr(&vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false);
227                 if (slen < 0) {
228                         RDEBUG("Failed parsing attribute name '%s': %s", p, fr_strerror());
229                         return false;
230                 }
231
232                 p += slen;
233
234                 if (tmpl_find_vp(&vp, request, &vpt) < 0) {
235                         RDEBUG("Can't find &%s", vpt.tmpl_da->name);
236                         x = 0;
237                         goto done;
238                 }
239
240                 switch (vp->da->type) {
241                 default:
242                         RDEBUG("WARNING: Non-integer attribute %s", vp->da->name);
243                         x = 0;
244                         break;
245
246                 case PW_TYPE_INTEGER64:
247                         /*
248                          *      FIXME: error out if the number is too large.
249                          */
250                         x = vp->vp_integer64;
251                         break;
252
253                 case PW_TYPE_INTEGER:
254                         x = vp->vp_integer;
255                         break;
256
257                 case PW_TYPE_SIGNED:
258                         x = vp->vp_signed;
259                         break;
260
261                 case PW_TYPE_SHORT:
262                         x = vp->vp_short;
263                         break;
264
265                 case PW_TYPE_BYTE:
266                         x = vp->vp_byte;
267                         break;
268                 }
269
270                 goto done;
271         }
272
273         /*
274          *      Do brackets recursively
275          */
276         if (*p == '(') {
277                 p++;
278                 if (!get_expression(request, &p, &x, TOKEN_NONE)) return false;
279
280                 if (*p != ')') {
281                         RDEBUG("No trailing ')'");
282                         return false;
283                 }
284                 p++;
285                 goto done;
286         }
287
288         if (*p == '-') {
289                 negative = true;
290                 p++;
291         }
292
293         if ((*p < '0') || (*p > '9')) {
294                 RDEBUG2("Not a number at \"%s\"", p);
295                 return false;
296         }
297
298         /*
299          *  This is doing it the hard way, but it also allows
300          *  us to increment 'p'.
301          */
302         x = 0;
303         while ((*p >= '0') && (*p <= '9')) {
304                 x *= 10;
305                 x += (*p - '0');
306                 p++;
307         }
308
309         if (negative) x = -x;
310
311         if (invert) x = ~x;
312
313 done:
314         *string = p;
315         *answer = x;
316         return true;
317 }
318
319 static bool calc_result(REQUEST *request, int64_t lhs, expr_token_t op, int64_t rhs, int64_t *answer)
320 {
321         switch (op) {
322         default:
323         case TOKEN_ADD:
324                 *answer = lhs + rhs;
325                 break;
326
327         case TOKEN_SUBTRACT:
328                 *answer = lhs - rhs;
329                 break;
330
331         case TOKEN_DIVIDE:
332                 if (rhs == 0) {
333                         RDEBUG("Division by zero!");
334                         return false;
335                 }
336
337                 *answer = lhs / rhs;
338                 break;
339
340         case TOKEN_REMAINDER:
341                 if (rhs == 0) {
342                         RDEBUG("Division by zero!");
343                         return false;
344                 }
345
346                 *answer = lhs % rhs;
347                 break;
348
349         case TOKEN_MULTIPLY:
350                 *answer = lhs * rhs;
351                 break;
352
353         case TOKEN_LSHIFT:
354                 if (rhs > 63) {
355                         RDEBUG("Shift must be less than 63 (was %lld)", (long long int) rhs);
356                         return false;
357                 }
358
359                 *answer = lhs << rhs;
360                 break;
361
362         case TOKEN_RSHIFT:
363                 if (rhs > 63) {
364                         RDEBUG("Shift must be less than 63 (was %lld)", (long long int) rhs);
365                         return false;
366                 }
367
368                 *answer = lhs >> rhs;
369                 break;
370
371         case TOKEN_AND:
372                 *answer = lhs & rhs;
373                 break;
374
375         case TOKEN_OR:
376                 *answer = lhs | rhs;
377                 break;
378
379         case TOKEN_POWER:
380                 if (rhs > 63) {
381                         REDEBUG("Exponent must be between 0-63 (was %lld)", (long long int) rhs);
382                         return false;
383                 }
384
385                 if (lhs > 65535) {
386                         REDEBUG("Base must be between 0-65535 (was %lld)", (long long int) lhs);
387                         return false;
388                 }
389
390                 *answer = fr_pow(lhs, rhs);
391                 break;
392         }
393
394         return true;
395 }
396
397 static bool get_operator(REQUEST *request, char const **string, expr_token_t *op)
398 {
399         int             i;
400         char const      *p = *string;
401
402         /*
403          *      All tokens are one character.
404          */
405         for (i = 0; map[i].token != TOKEN_LAST; i++) {
406                 if (*p == map[i].op) {
407                         *op = map[i].token;
408                         *string = p + 1;
409                         return true;
410                 }
411         }
412
413         if ((p[0] == '<') && (p[1] == '<')) {
414                 *op = TOKEN_LSHIFT;
415                 *string = p + 2;
416                 return true;
417         }
418
419         if ((p[0] == '>') && (p[1] == '>')) {
420                 *op = TOKEN_RSHIFT;
421                 *string = p + 2;
422                 return true;
423         }
424
425         RDEBUG("Expected operator at \"%s\"", p);
426         return false;
427 }
428
429
430 static bool get_expression(REQUEST *request, char const **string, int64_t *answer, expr_token_t prev)
431 {
432         int64_t         lhs, rhs;
433         char const      *p, *op_p;
434         expr_token_t    this;
435
436         p = *string;
437
438         if (!get_number(request, &p, &lhs)) return false;
439
440 redo:
441         while (isspace((int) *p)) p++;
442
443         /*
444          *      A number by itself is OK.
445          */
446         if (!*p || (*p == ')')) {
447                 *answer = lhs;
448                 *string = p;
449                 return true;
450         }
451
452         /*
453          *      Peek at the operator.
454          */
455         op_p = p;
456         if (!get_operator(request, &p, &this)) return false;
457
458         /*
459          *      a + b + c ... = (a + b) + c ...
460          *      a * b + c ... = (a * b) + c ...
461          *
462          *      Feed the current number to the caller, who will take
463          *      care of continuing.
464          */
465         if (precedence[this] <= precedence[prev]) {
466                 *answer = lhs;
467                 *string = op_p;
468                 return true;
469         }
470
471         /*
472          *      a + b * c ... = a + (b * c) ...
473          */
474         if (!get_expression(request, &p, &rhs, this)) return false;
475
476         if (!calc_result(request, lhs, this, rhs, answer)) return false;
477
478         /*
479          *      There may be more to calculate.  The answer we
480          *      calculated here is now the LHS of the lower priority
481          *      operation which follows the current expression.  e.g.
482          *
483          *      a * b + c ... = (a * b) + c ...
484          *                    =       d + c ...
485          */
486         lhs = *answer;
487         goto redo;
488 }
489
490 /*
491  *  Do xlat of strings!
492  */
493 static ssize_t expr_xlat(UNUSED void *instance, REQUEST *request, char const *fmt,
494                          char *out, size_t outlen)
495 {
496         int64_t         result;
497         char const      *p;
498
499         p = fmt;
500
501         if (!get_expression(request, &p, &result, TOKEN_NONE)) {
502                 return -1;
503         }
504
505         if (*p) {
506                 RDEBUG("Invalid text after expression: %s", p);
507                 return -1;
508         }
509
510         snprintf(out, outlen, "%lld", (long long int) result);
511         return strlen(out);
512 }
513
514 /** Generate a random integer value
515  *
516  */
517 static ssize_t rand_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt,
518                          char *out, size_t outlen)
519 {
520         int64_t         result;
521
522         result = atoi(fmt);
523
524         /*
525          *      Too small or too big.
526          */
527         if (result <= 0) {
528                 *out = '\0';
529                 return -1;
530         }
531         if (result >= (1 << 30)) result = (1 << 30);
532
533         result *= fr_rand();    /* 0..2^32-1 */
534         result >>= 32;
535
536         snprintf(out, outlen, "%ld", (long int) result);
537         return strlen(out);
538 }
539
540 /** Generate a string of random chars
541  *
542  *  Build strings of random chars, useful for generating tokens and passcodes
543  *  Format similar to String::Random.
544  */
545 static ssize_t randstr_xlat(UNUSED void *instance, UNUSED REQUEST *request,
546                             char const *fmt, char *out, size_t outlen)
547 {
548         char const      *p;
549         unsigned int    result;
550         size_t          freespace = outlen;
551
552         if (outlen <= 1) return 0;
553
554         *out = '\0';
555
556         p = fmt;
557         while (*p && (--freespace > 0)) {
558                 result = fr_rand();
559                 switch (*p) {
560                 /*
561                  *  Lowercase letters
562                  */
563                 case 'c':
564                         *out++ = 'a' + (result % 26);
565                         break;
566
567                 /*
568                  *  Uppercase letters
569                  */
570                 case 'C':
571                         *out++ = 'A' + (result % 26);
572                         break;
573
574                 /*
575                  *  Numbers
576                  */
577                 case 'n':
578                         *out++ = '0' + (result % 10);
579                         break;
580
581                 /*
582                  *  Alpha numeric
583                  */
584                 case 'a':
585                         *out++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
586                         break;
587
588                 /*
589                  *  Punctuation
590                  */
591                 case '!':
592                         *out++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
593                         break;
594
595                 /*
596                  *  Alpa numeric + punctuation
597                  */
598                 case '.':
599                         *out++ = '!' + (result % 95);
600                         break;
601
602                 /*
603                  *  Alpha numeric + salt chars './'
604                  */
605                 case 's':
606                         *out++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
607                         break;
608
609                 /*
610                  *  Chars suitable for One Time Password tokens.
611                  *  Alpha numeric with easily confused char pairs removed.
612                  */
613                 case 'o':
614                         *out++ = randstr_otp[result % (sizeof(randstr_otp) - 1)];
615                         break;
616
617                 /*
618                  *  Binary data as hexits (we don't really support
619                  *  non printable chars).
620                  */
621                 case 'h':
622                         if (freespace < 2) {
623                                 break;
624                         }
625
626                         snprintf(out, 3, "%02x", result % 256);
627
628                         /* Already decremented */
629                         freespace -= 1;
630                         out += 2;
631                         break;
632
633                 /*
634                  *  Binary data with uppercase hexits
635                  */
636                 case 'H':
637                         if (freespace < 2) {
638                                 break;
639                         }
640
641                         snprintf(out, 3, "%02X", result % 256);
642
643                         /* Already decremented */
644                         freespace -= 1;
645                         out += 2;
646                         break;
647
648                 default:
649                         ERROR("rlm_expr: invalid character class '%c'", *p);
650
651                         return -1;
652                 }
653
654                 p++;
655         }
656
657         *out++ = '\0';
658
659         return outlen - freespace;
660 }
661
662 /** URLencode special characters
663  *
664  * Example: "%{urlquote:http://example.org/}" == "http%3A%47%47example.org%47"
665  */
666 static ssize_t urlquote_xlat(UNUSED void *instance, UNUSED REQUEST *request,
667                              char const *fmt, char *out, size_t outlen)
668 {
669         char const      *p;
670         size_t  freespace = outlen;
671
672         if (outlen <= 1) return 0;
673
674         p = fmt;
675         while (*p && (--freespace > 0)) {
676                 if (isalnum(*p)) {
677                         *out++ = *p++;
678                         continue;
679                 }
680
681                 switch (*p) {
682                 case '-':
683                 case '_':
684                 case '.':
685                 case '~':
686                         *out++ = *p++;
687                         break;
688
689                 default:
690                         if (freespace < 3)
691                                 break;
692
693                         /* MUST be upper case hex to be compliant */
694                         snprintf(out, 4, "%%%02X", (uint8_t) *p++); /* %XX */
695
696                         /* Already decremented */
697                         freespace -= 2;
698                         out += 3;
699                 }
700         }
701
702         *out = '\0';
703
704         return outlen - freespace;
705 }
706
707 /** URLdecode special characters
708  *
709  * Example: "%{urlunquote:http%%3A%%47%%47example.org%%47}" == "http://example.org/"
710  * Mind the double % in the quoted string, otherwise unlang would start parsing it
711  */
712 static ssize_t urlunquote_xlat(UNUSED void *instance, REQUEST *request,
713                                char const *fmt, char *out, size_t outlen)
714 {
715         char const *p;
716         char *c1, *c2;
717         size_t  freespace = outlen;
718
719         if (outlen <= 1) return 0;
720
721         p = fmt;
722         while (*p && (--freespace > 0)) {
723                 if (*p != '%') {
724                         *out++ = *p++;
725                         continue;
726                 }
727                 /* Is a % char */
728
729                 /* Don't need \0 check, as it won't be in the hextab */
730                 if (!(c1 = memchr(hextab, tolower(*++p), 16)) ||
731                     !(c2 = memchr(hextab, tolower(*++p), 16))) {
732                         REMARKER(fmt, p - fmt, "None hex char in % sequence");
733                         return -1;
734                 }
735                 p++;
736                 *out++ = ((c1 - hextab) << 4) + (c2 - hextab);
737         }
738
739         *out = '\0';
740
741         return outlen - freespace;
742 }
743
744 /** Equivalent to the old safe_characters functionality in rlm_sql but with utf8 support
745  *
746  * @verbatim Example: "%{escape:<img>foo.jpg</img>}" == "=60img=62foo.jpg=60/img=62" @endverbatim
747  */
748 static ssize_t escape_xlat(void *instance, UNUSED REQUEST *request,
749                            char const *fmt, char *out, size_t outlen)
750 {
751         rlm_expr_t *inst = instance;
752         char const *p = fmt;
753         size_t freespace = outlen;
754
755         while (p[0]) {
756                 int chr_len = 1;
757                 int ret = 1;    /* -Werror=uninitialized */
758
759                 if (fr_utf8_strchr(&chr_len, inst->allowed_chars, p) == NULL) {
760                         /*
761                          *      '=' 1 + ([hex]{2}) * chr_len)
762                          */
763                         if (freespace <= (size_t)(1 + (chr_len * 3))) break;
764
765                         switch (chr_len) {
766                         case 4:
767                                 ret = snprintf(out, freespace, "=%02X=%02X=%02X=%02X",
768                                                (uint8_t)p[0], (uint8_t)p[1], (uint8_t)p[2], (uint8_t)p[3]);
769                                 break;
770
771                         case 3:
772                                 ret = snprintf(out, freespace, "=%02X=%02X=%02X",
773                                                (uint8_t)p[0], (uint8_t)p[1], (uint8_t)p[2]);
774                                 break;
775
776                         case 2:
777                                 ret = snprintf(out, freespace, "=%02X=%02X", (uint8_t)p[0], (uint8_t)p[1]);
778                                 break;
779
780                         case 1:
781                                 ret = snprintf(out, freespace, "=%02X", (uint8_t)p[0]);
782                                 break;
783                         }
784
785                         p += chr_len;
786                         out += ret;
787                         freespace -= ret;
788                         continue;
789                 }
790
791                 /*
792                  *      Only one byte left.
793                  */
794                 if (freespace <= 1) break;
795
796                 /*
797                  *      Allowed character (copy whole mb chars at once)
798                  */
799                 memcpy(out, p, chr_len);
800                 out += chr_len;
801                 p += chr_len;
802                 freespace -= chr_len;
803         }
804         *out = '\0';
805
806         return outlen - freespace;
807 }
808
809 /** Equivalent to the old safe_characters functionality in rlm_sql
810  *
811  * @verbatim Example: "%{unescape:=60img=62foo.jpg=60/img=62}" == "<img>foo.jpg</img>" @endverbatim
812  */
813 static ssize_t unescape_xlat(UNUSED void *instance, UNUSED REQUEST *request,
814                              char const *fmt, char *out, size_t outlen)
815 {
816         char const *p;
817         char *c1, *c2, c3;
818         size_t  freespace = outlen;
819
820         if (outlen <= 1) return 0;
821
822         p = fmt;
823         while (*p && (--freespace > 0)) {
824                 if (*p != '=') {
825                 next:
826
827                         *out++ = *p++;
828                         continue;
829                 }
830
831                 /* Is a = char */
832
833                 if (!(c1 = memchr(hextab, tolower(*(p + 1)), 16)) ||
834                     !(c2 = memchr(hextab, tolower(*(p + 2)), 16))) goto next;
835                 c3 = ((c1 - hextab) << 4) + (c2 - hextab);
836
837                 *out++ = c3;
838                 p += 3;
839         }
840
841         *out = '\0';
842
843         return outlen - freespace;
844 }
845
846 /** Convert a string to lowercase
847  *
848  * Example: "%{tolower:Bar}" == "bar"
849  *
850  * Probably only works for ASCII
851  */
852 static ssize_t lc_xlat(UNUSED void *instance, UNUSED REQUEST *request,
853                        char const *fmt, char *out, size_t outlen)
854 {
855         char *q;
856         char const *p;
857
858         if (outlen <= 1) return 0;
859
860         for (p = fmt, q = out; *p != '\0'; p++, outlen--) {
861                 if (outlen <= 1) break;
862
863                 *(q++) = tolower((int) *p);
864         }
865
866         *q = '\0';
867
868         return strlen(out);
869 }
870
871 /** Convert a string to uppercase
872  *
873  * Example: "%{toupper:Foo}" == "FOO"
874  *
875  * Probably only works for ASCII
876  */
877 static ssize_t uc_xlat(UNUSED void *instance, UNUSED REQUEST *request,
878                        char const *fmt, char *out, size_t outlen)
879 {
880         char *q;
881         char const *p;
882
883         if (outlen <= 1) return 0;
884
885         for (p = fmt, q = out; *p != '\0'; p++, outlen--) {
886                 if (outlen <= 1) break;
887
888                 *(q++) = toupper((int) *p);
889         }
890
891         *q = '\0';
892
893         return strlen(out);
894 }
895
896 /** Calculate the MD5 hash of a string or attribute.
897  *
898  * Example: "%{md5:foo}" == "acbd18db4cc2f85cedef654fccc4a4d8"
899  */
900 static ssize_t md5_xlat(UNUSED void *instance, REQUEST *request,
901                         char const *fmt, char *out, size_t outlen)
902 {
903         uint8_t digest[16];
904         ssize_t i, len, inlen;
905         uint8_t const *p;
906         FR_MD5_CTX ctx;
907
908         /*
909          *      We need room for at least one octet of output.
910          */
911         if (outlen < 3) {
912                 *out = '\0';
913                 return 0;
914         }
915
916         inlen = xlat_fmt_to_ref(&p, request, fmt);
917         if (inlen < 0) {
918                 return -1;
919         }
920
921         fr_md5_init(&ctx);
922         fr_md5_update(&ctx, p, inlen);
923         fr_md5_final(digest, &ctx);
924
925         /*
926          *      Each digest octet takes two hex digits, plus one for
927          *      the terminating NUL.
928          */
929         len = (outlen / 2) - 1;
930         if (len > 16) len = 16;
931
932         for (i = 0; i < len; i++) {
933                 snprintf(out + i * 2, 3, "%02x", digest[i]);
934         }
935
936         return strlen(out);
937 }
938
939 /** Calculate the SHA1 hash of a string or attribute.
940  *
941  * Example: "%{sha1:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
942  */
943 static ssize_t sha1_xlat(UNUSED void *instance, REQUEST *request,
944                          char const *fmt, char *out, size_t outlen)
945 {
946         uint8_t digest[20];
947         ssize_t i, len, inlen;
948         uint8_t const *p;
949         fr_SHA1_CTX ctx;
950
951         /*
952          *      We need room for at least one octet of output.
953          */
954         if (outlen < 3) {
955                 *out = '\0';
956                 return 0;
957         }
958
959         inlen = xlat_fmt_to_ref(&p, request, fmt);
960         if (inlen < 0) {
961                 return -1;
962         }
963
964         fr_sha1_init(&ctx);
965         fr_sha1_update(&ctx, p, inlen);
966         fr_sha1_final(digest, &ctx);
967
968         /*
969          *      Each digest octet takes two hex digits, plus one for
970          *      the terminating NUL. SHA1 is 160 bits (20 bytes)
971          */
972         len = (outlen / 2) - 1;
973         if (len > 20) len = 20;
974
975         for (i = 0; i < len; i++) {
976                 snprintf(out + i * 2, 3, "%02x", digest[i]);
977         }
978
979         return strlen(out);
980 }
981
982 /** Calculate any digest supported by OpenSSL EVP_MD
983  *
984  * Example: "%{sha256:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
985  */
986 #ifdef HAVE_OPENSSL_EVP_H
987 static ssize_t evp_md_xlat(UNUSED void *instance, REQUEST *request,
988                            char const *fmt, char *out, size_t outlen, EVP_MD const *md)
989 {
990         uint8_t digest[EVP_MAX_MD_SIZE];
991         unsigned int digestlen, i, len;
992         ssize_t inlen;
993         uint8_t const *p;
994
995         EVP_MD_CTX *ctx;
996
997         /*
998          *      We need room for at least one octet of output.
999          */
1000         if (outlen < 3) {
1001                 *out = '\0';
1002                 return 0;
1003         }
1004
1005         inlen = xlat_fmt_to_ref(&p, request, fmt);
1006         if (inlen < 0) {
1007                 return -1;
1008         }
1009
1010         ctx = EVP_MD_CTX_create();
1011         EVP_DigestInit_ex(ctx, md, NULL);
1012         EVP_DigestUpdate(ctx, p, inlen);
1013         EVP_DigestFinal_ex(ctx, digest, &digestlen);
1014         EVP_MD_CTX_destroy(ctx);
1015
1016         /*
1017          *      Each digest octet takes two hex digits, plus one for
1018          *      the terminating NUL.
1019          */
1020         len = (outlen / 2) - 1;
1021         if (len > digestlen) len = digestlen;
1022
1023         for (i = 0; i < len; i++) {
1024                 snprintf(out + i * 2, 3, "%02x", digest[i]);
1025         }
1026         return strlen(out);
1027 }
1028
1029 #  define EVP_MD_XLAT(_md) \
1030 static ssize_t _md##_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen)\
1031 {\
1032         return evp_md_xlat(instance, request, fmt, out, outlen, EVP_##_md());\
1033 }
1034
1035 EVP_MD_XLAT(sha256)
1036 EVP_MD_XLAT(sha512)
1037 #endif
1038
1039 /** Generate the HMAC-MD5 of a string or attribute
1040  *
1041  * Example: "%{hmacmd5:foo bar}" == "Zm9v"
1042  */
1043 static ssize_t hmac_md5_xlat(UNUSED void *instance, REQUEST *request,
1044                              char const *fmt, char *out, size_t outlen)
1045 {
1046         uint8_t const *data, *key;
1047         char const *p;
1048         ssize_t data_len, key_len;
1049         uint8_t digest[MD5_DIGEST_LENGTH];
1050         char data_ref[256];
1051
1052         if (outlen <= (sizeof(digest) * 2)) {
1053                 REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes",
1054                         (sizeof(digest) * 2) + 1, outlen);
1055                 return -1;
1056         }
1057
1058         p = strchr(fmt, ' ');
1059         if (!p) {
1060                 REDEBUG("HMAC requires exactly two arguments (&data &key)");
1061                 return -1;
1062         }
1063
1064         if ((size_t)(p - fmt) >= sizeof(data_ref)) {
1065                 REDEBUG("Insufficient space to store HMAC input data, needed %zu bytes, have %zu bytes",
1066                        (p - fmt) + 1, sizeof(data_ref));
1067
1068                 return -1;
1069         }
1070         strlcpy(data_ref, fmt, (p - fmt) + 1);
1071
1072         data_len = xlat_fmt_to_ref(&data, request, data_ref);
1073         if (data_len < 0) return -1;
1074
1075         while (isspace(*p) && p++);
1076
1077         key_len = xlat_fmt_to_ref(&key, request, p);
1078         if (key_len < 0) return -1;
1079
1080         fr_hmac_md5(digest, data, data_len, key, key_len);
1081
1082         return fr_bin2hex(out, digest, sizeof(digest));
1083 }
1084
1085 /** Generate the HMAC-SHA1 of a string or attribute
1086  *
1087  * Example: "%{hmacsha1:foo bar}" == "Zm9v"
1088  */
1089 static ssize_t hmac_sha1_xlat(UNUSED void *instance, REQUEST *request,
1090                               char const *fmt, char *out, size_t outlen)
1091 {
1092         uint8_t const *data, *key;
1093         char const *p;
1094         ssize_t data_len, key_len;
1095         uint8_t digest[SHA1_DIGEST_LENGTH];
1096         char data_ref[256];
1097
1098         if (outlen <= (sizeof(digest) * 2)) {
1099                 REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes",
1100                         (sizeof(digest) * 2) + 1, outlen);
1101                 return -1;
1102         }
1103
1104         p = strchr(fmt, ' ');
1105         if (!p) {
1106                 REDEBUG("HMAC requires exactly two arguments (&data &key)");
1107                 return -1;
1108         }
1109
1110         if ((size_t)(p - fmt) >= sizeof(data_ref)) {
1111                 REDEBUG("Insufficient space to store HMAC input data, needed %zu bytes, have %zu bytes",
1112                         (p - fmt) + 1, sizeof(data_ref));
1113
1114                 return -1;
1115         }
1116         strlcpy(data_ref, fmt, (p - fmt) + 1);
1117
1118         data_len = xlat_fmt_to_ref(&data, request, data_ref);
1119         if (data_len < 0) return -1;
1120
1121         while (isspace(*p) && p++);
1122
1123         key_len = xlat_fmt_to_ref(&key, request, p);
1124         if (key_len < 0) return -1;
1125
1126         fr_hmac_sha1(digest, data, data_len, key, key_len);
1127
1128         return fr_bin2hex(out, digest, sizeof(digest));
1129 }
1130
1131 /** Encode attributes as a series of string attribute/value pairs
1132  *
1133  * This is intended to serialize one or more attributes as a comma
1134  * delimited string.
1135  *
1136  * Example: "%{pairs:request:}" == "User-Name = 'foo', User-Password = 'bar'"
1137  */
1138 static ssize_t pairs_xlat(UNUSED void *instance, REQUEST *request,
1139                           char const *fmt, char *out, size_t outlen)
1140 {
1141         value_pair_tmpl_t vpt;
1142         vp_cursor_t cursor;
1143         size_t len, freespace = outlen;
1144         char *p = out;
1145
1146         VALUE_PAIR *vp;
1147
1148         if (tmpl_from_attr_str(&vpt, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST, false) <= 0) {
1149                 REDEBUG("%s", fr_strerror());
1150                 return -1;
1151         }
1152
1153         for (vp = tmpl_cursor_init(NULL, &cursor, request, &vpt);
1154              vp;
1155              vp = tmpl_cursor_next(&cursor, &vpt)) {
1156                 FR_TOKEN op = vp->op;
1157
1158                 vp->op = T_OP_EQ;
1159                 len = vp_prints(p, freespace, vp);
1160                 vp->op = op;
1161
1162                 if (is_truncated(len, freespace)) {
1163                 no_space:
1164                         REDEBUG("Insufficient space to store pair string, needed %zu bytes have %zu bytes",
1165                                 (p - out) + len, outlen);
1166                         *out = '\0';
1167                         return -1;
1168                 }
1169                 p += len;
1170                 freespace -= len;
1171
1172                 if (freespace < 2) {
1173                         len = 2;
1174                         goto no_space;
1175                 }
1176
1177                 *p++ = ',';
1178                 *p++ = ' ';
1179                 freespace -= 2;
1180         }
1181
1182         /* Trim the trailing ', ' */
1183         if (p != out) p -= 2;
1184         *p = '\0';
1185
1186         return (p - out);
1187 }
1188
1189 /** Encode string or attribute as base64
1190  *
1191  * Example: "%{base64:foo}" == "Zm9v"
1192  */
1193 static ssize_t base64_xlat(UNUSED void *instance, REQUEST *request,
1194                            char const *fmt, char *out, size_t outlen)
1195 {
1196         ssize_t inlen;
1197         uint8_t const *p;
1198
1199         inlen = xlat_fmt_to_ref(&p, request, fmt);
1200         if (inlen < 0) {
1201                 return -1;
1202         }
1203
1204         /*
1205          *  We can accurately calculate the length of the output string
1206          *  if it's larger than outlen, the output would be useless so abort.
1207          */
1208         if ((inlen < 0) || ((FR_BASE64_ENC_LENGTH(inlen) + 1) > (ssize_t) outlen)) {
1209                 REDEBUG("xlat failed");
1210                 *out = '\0';
1211                 return -1;
1212         }
1213
1214         return fr_base64_encode(out, outlen, p, inlen);
1215 }
1216
1217 /** Convert base64 to hex
1218  *
1219  * Example: "%{base64tohex:Zm9v}" == "666f6f"
1220  */
1221 static ssize_t base64_to_hex_xlat(UNUSED void *instance, REQUEST *request,
1222                                   char const *fmt, char *out, size_t outlen)
1223 {
1224         uint8_t decbuf[1024];
1225
1226         ssize_t declen;
1227         ssize_t len = strlen(fmt);
1228
1229         *out = '\0';
1230
1231         declen = fr_base64_decode(decbuf, sizeof(decbuf), fmt, len);
1232         if (declen < 0) {
1233                 REDEBUG("Base64 string invalid");
1234                 return -1;
1235         }
1236
1237         if ((size_t)((declen * 2) + 1) > outlen) {
1238                 REDEBUG("Base64 conversion failed, output buffer exhausted, needed %zd bytes, have %zd bytes",
1239                         (declen * 2) + 1, outlen);
1240                 return -1;
1241         }
1242
1243         return fr_bin2hex(out, decbuf, declen);
1244 }
1245
1246
1247 /*
1248  *      Do any per-module initialization that is separate to each
1249  *      configured instance of the module.  e.g. set up connections
1250  *      to external databases, read configuration files, set up
1251  *      dictionary entries, etc.
1252  *
1253  *      If configuration information is given in the config section
1254  *      that must be referenced in later calls, store a handle to it
1255  *      in *instance otherwise put a null pointer there.
1256  */
1257 static int mod_instantiate(CONF_SECTION *conf, void *instance)
1258 {
1259         rlm_expr_t *inst = instance;
1260
1261         inst->xlat_name = cf_section_name2(conf);
1262         if (!inst->xlat_name) {
1263                 inst->xlat_name = cf_section_name1(conf);
1264         }
1265
1266         xlat_register(inst->xlat_name, expr_xlat, NULL, inst);
1267
1268         /*
1269          *      FIXME: unregister these, too
1270          */
1271         xlat_register("rand", rand_xlat, NULL, inst);
1272         xlat_register("randstr", randstr_xlat, NULL, inst);
1273         xlat_register("urlquote", urlquote_xlat, NULL, inst);
1274         xlat_register("urlunquote", urlunquote_xlat, NULL, inst);
1275         xlat_register("escape", escape_xlat, NULL, inst);
1276         xlat_register("unescape", unescape_xlat, NULL, inst);
1277         xlat_register("tolower", lc_xlat, NULL, inst);
1278         xlat_register("toupper", uc_xlat, NULL, inst);
1279         xlat_register("md5", md5_xlat, NULL, inst);
1280         xlat_register("sha1", sha1_xlat, NULL, inst);
1281 #ifdef HAVE_OPENSSL_EVP_H
1282         xlat_register("sha256", sha256_xlat, NULL, inst);
1283         xlat_register("sha512", sha512_xlat, NULL, inst);
1284 #endif
1285         xlat_register("hmacmd5", hmac_md5_xlat, NULL, inst);
1286         xlat_register("hmacsha1", hmac_sha1_xlat, NULL, inst);
1287         xlat_register("pairs", pairs_xlat, NULL, inst);
1288
1289         xlat_register("base64", base64_xlat, NULL, inst);
1290         xlat_register("base64tohex", base64_to_hex_xlat, NULL, inst);
1291
1292         /*
1293          *      Initialize various paircompare functions
1294          */
1295         pair_builtincompare_add(instance);
1296         return 0;
1297 }
1298
1299 /*
1300  *      The module name should be the only globally exported symbol.
1301  *      That is, everything else should be 'static'.
1302  *
1303  *      If the module needs to temporarily modify it's instantiation
1304  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
1305  *      The server will then take care of ensuring that the module
1306  *      is single-threaded.
1307  */
1308 extern module_t rlm_expr;
1309 module_t rlm_expr = {
1310         RLM_MODULE_INIT,
1311         "expr",                         /* Name */
1312         0,      /* type */
1313         sizeof(rlm_expr_t),
1314         module_config,
1315         mod_instantiate,                /* instantiation */
1316         NULL,                           /* detach */
1317         {
1318                 NULL,                   /* authentication */
1319                 NULL,                   /* authorization */
1320                 NULL,                   /* pre-accounting */
1321                 NULL                    /* accounting */
1322         },
1323 };