97f2a52db94318eade73cffd03fcbcea36381dd9
[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 typedef enum expr_token_t {
55         TOKEN_NONE = 0,
56         TOKEN_INTEGER,
57         TOKEN_ADD,
58         TOKEN_SUBTRACT,
59         TOKEN_DIVIDE,
60         TOKEN_REMAINDER,
61         TOKEN_MULTIPLY,
62         TOKEN_AND,
63         TOKEN_OR,
64         TOKEN_POWER,
65         TOKEN_LAST
66 } expr_token_t;
67
68 typedef struct expr_map_t {
69         char op;
70         expr_token_t token;
71 } expr_map_t;
72
73 static expr_map_t map[] =
74 {
75         {'+',   TOKEN_ADD },
76         {'-',   TOKEN_SUBTRACT },
77         {'/',   TOKEN_DIVIDE },
78         {'*',   TOKEN_MULTIPLY },
79         {'%',   TOKEN_REMAINDER },
80         {'&',   TOKEN_AND },
81         {'|',   TOKEN_OR },
82         {'^',   TOKEN_POWER },
83         {0,     TOKEN_LAST}
84 };
85
86 /*
87  *      Lookup tables for randstr char classes
88  */
89 static char randstr_punc[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
90 static char randstr_salt[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz/.";
91
92 /*
93  *      Characters humans rarely confuse. Reduces char set considerably
94  *      should only be used for things such as one time passwords.
95  */
96 static char randstr_otp[] = "469ACGHJKLMNPQRUVWXYabdfhijkprstuvwxyz";
97
98 static char const *hextab = "0123456789abcdef";
99
100 static int get_number(REQUEST *request, char const **string, int64_t *answer)
101 {
102         int             i, found;
103         int64_t         result;
104         int64_t         x;
105         char const      *p;
106         expr_token_t    this;
107
108         /*
109          *  Loop over the input.
110          */
111         result = 0;
112         this = TOKEN_NONE;
113
114         for (p = *string; *p != '\0'; /* nothing */) {
115                 if ((*p == ' ') ||
116                     (*p == '\t')) {
117                         p++;
118                         continue;
119                 }
120
121                 /*
122                  *  Discover which token it is.
123                  */
124                 found = false;
125                 for (i = 0; map[i].token != TOKEN_LAST; i++) {
126                         if (*p == map[i].op) {
127                                 if (this != TOKEN_NONE) {
128                                         RDEBUG2("Invalid operator at \"%s\"", p);
129                                         return -1;
130                                 }
131                                 this = map[i].token;
132                                 p++;
133                                 found = true;
134                                 break;
135                         }
136                 }
137
138                 /*
139                  *  Found the algebraic operator.  Get the next number.
140                  */
141                 if (found) {
142                         continue;
143                 }
144
145                 /*
146                  *  End of a group.  Stop.
147                  */
148                 if (*p == ')') {
149                         if (this != TOKEN_NONE) {
150                                 RDEBUG2("Trailing operator before end sub-expression at \"%s\"", p);
151                                 return -1;
152                         }
153                         p++;
154                         break;
155                 }
156
157                 /*
158                  *  Start of a group.  Call ourselves recursively.
159                  */
160                 if (*p == '(') {
161                         p++;
162
163                         found = get_number(request, &p, &x);
164                         if (found < 0) {
165                                 return -1;
166                         }
167                 } else {
168                         /*
169                          *  No algrebraic operator found, the next thing
170                          *  MUST be a number.
171                          *
172                          *  If it isn't, then we die.
173                          */
174                         if ((*p == '0') && (p[1] == 'x')) {
175                                 char *end;
176
177                                 x = strtoul(p, &end, 16);
178                                 p = end;
179                                 goto calc;
180                         }
181
182
183                         if ((*p < '0') || (*p > '9')) {
184                                 RDEBUG2("Not a number at \"%s\"", p);
185                                 return -1;
186                         }
187
188                         /*
189                          *  This is doing it the hard way, but it also allows
190                          *  us to increment 'p'.
191                          */
192                         x = 0;
193                         while ((*p >= '0') && (*p <= '9')) {
194                                 x *= 10;
195                                 x += (*p - '0');
196                                 p++;
197                         }
198                 }
199
200         calc:
201                 switch (this) {
202                 default:
203                 case TOKEN_NONE:
204                         result = x;
205                         break;
206
207                 case TOKEN_ADD:
208                         result += x;
209                         break;
210
211                 case TOKEN_SUBTRACT:
212                         result -= x;
213                         break;
214
215                 case TOKEN_DIVIDE:
216                         if (x == 0) {
217                                 result = 0; /* we don't have NaN for integers */
218                         } else {
219                                 result /= x;
220                         }
221                         break;
222
223                 case TOKEN_REMAINDER:
224                         if (x == 0) {
225                                 result = 0; /* we don't have NaN for integers */
226                                 break;
227                         }
228                         result %= x;
229                         break;
230
231                 case TOKEN_MULTIPLY:
232                         result *= x;
233                         break;
234
235                 case TOKEN_AND:
236                         result &= x;
237                         break;
238
239                 case TOKEN_OR:
240                         result |= x;
241                         break;
242
243                 case TOKEN_POWER:
244                         if ((x > 255) || (x < 0)) {
245                                 REDEBUG("Exponent must be between 0-255");
246                                 return -1;
247                         }
248                         x = fr_pow((int32_t) result, (uint8_t) x);
249                         break;
250                 }
251
252                 /*
253                  *  We've used this token.
254                  */
255                 this = TOKEN_NONE;
256         }
257
258         /*
259          *  And return the answer to the caller.
260          */
261         *string = p;
262         *answer = result;
263         return 0;
264 }
265
266 /*
267  *  Do xlat of strings!
268  */
269 static ssize_t expr_xlat(UNUSED void *instance, REQUEST *request, char const *fmt,
270                          char *out, size_t outlen)
271 {
272         int             rcode;
273         int64_t         result;
274         char const      *p;
275
276         p = fmt;
277         rcode = get_number(request, &p, &result);
278         if (rcode < 0) {
279                 return -1;
280         }
281
282         /*
283          *  We MUST have eaten the entire input string.
284          */
285         if (*p != '\0') {
286                 RDEBUG2("Failed at %s", p);
287                 return -1;
288         }
289
290         snprintf(out, outlen, "%ld", (long int) result);
291         return strlen(out);
292 }
293
294 /** Generate a random integer value
295  *
296  */
297 static ssize_t rand_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt,
298                          char *out, size_t outlen)
299 {
300         int64_t         result;
301
302         result = atoi(fmt);
303
304         /*
305          *      Too small or too big.
306          */
307         if (result <= 0) {
308                 *out = '\0';
309                 return -1;
310         }
311         if (result >= (1 << 30)) result = (1 << 30);
312
313         result *= fr_rand();    /* 0..2^32-1 */
314         result >>= 32;
315
316         snprintf(out, outlen, "%ld", (long int) result);
317         return strlen(out);
318 }
319
320 /** Generate a string of random chars
321  *
322  *  Build strings of random chars, useful for generating tokens and passcodes
323  *  Format similar to String::Random.
324  */
325 static ssize_t randstr_xlat(UNUSED void *instance, UNUSED REQUEST *request,
326                             char const *fmt, char *out, size_t outlen)
327 {
328         char const      *p;
329         unsigned int    result;
330         size_t          freespace = outlen;
331
332         if (outlen <= 1) return 0;
333
334         *out = '\0';
335
336         p = fmt;
337         while (*p && (--freespace > 0)) {
338                 result = fr_rand();
339                 switch (*p) {
340                 /*
341                  *  Lowercase letters
342                  */
343                 case 'c':
344                         *out++ = 'a' + (result % 26);
345                         break;
346
347                 /*
348                  *  Uppercase letters
349                  */
350                 case 'C':
351                         *out++ = 'A' + (result % 26);
352                         break;
353
354                 /*
355                  *  Numbers
356                  */
357                 case 'n':
358                         *out++ = '0' + (result % 10);
359                         break;
360
361                 /*
362                  *  Alpha numeric
363                  */
364                 case 'a':
365                         *out++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
366                         break;
367
368                 /*
369                  *  Punctuation
370                  */
371                 case '!':
372                         *out++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
373                         break;
374
375                 /*
376                  *  Alpa numeric + punctuation
377                  */
378                 case '.':
379                         *out++ = '!' + (result % 95);
380                         break;
381
382                 /*
383                  *  Alpha numeric + salt chars './'
384                  */
385                 case 's':
386                         *out++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
387                         break;
388
389                 /*
390                  *  Chars suitable for One Time Password tokens.
391                  *  Alpha numeric with easily confused char pairs removed.
392                  */
393                 case 'o':
394                         *out++ = randstr_otp[result % (sizeof(randstr_otp) - 1)];
395                         break;
396
397                 /*
398                  *  Binary data as hexits (we don't really support
399                  *  non printable chars).
400                  */
401                 case 'h':
402                         if (freespace < 2) {
403                                 break;
404                         }
405
406                         snprintf(out, 3, "%02x", result % 256);
407
408                         /* Already decremented */
409                         freespace -= 1;
410                         out += 2;
411                         break;
412
413                 /*
414                  *  Binary data with uppercase hexits
415                  */
416                 case 'H':
417                         if (freespace < 2) {
418                                 break;
419                         }
420
421                         snprintf(out, 3, "%02X", result % 256);
422
423                         /* Already decremented */
424                         freespace -= 1;
425                         out += 2;
426                         break;
427
428                 default:
429                         ERROR("rlm_expr: invalid character class '%c'", *p);
430
431                         return -1;
432                 }
433
434                 p++;
435         }
436
437         *out++ = '\0';
438
439         return outlen - freespace;
440 }
441
442 /** URLencode special characters
443  *
444  * Example: "%{urlquote:http://example.org/}" == "http%3A%47%47example.org%47"
445  */
446 static ssize_t urlquote_xlat(UNUSED void *instance, UNUSED REQUEST *request,
447                              char const *fmt, char *out, size_t outlen)
448 {
449         char const      *p;
450         size_t  freespace = outlen;
451
452         if (outlen <= 1) return 0;
453
454         p = fmt;
455         while (*p && (--freespace > 0)) {
456                 if (isalnum(*p)) {
457                         *out++ = *p++;
458                         continue;
459                 }
460
461                 switch (*p) {
462                 case '-':
463                 case '_':
464                 case '.':
465                 case '~':
466                         *out++ = *p++;
467                         break;
468                 default:
469                         if (freespace < 3)
470                                 break;
471
472                         /* MUST be upper case hex to be compliant */
473                         snprintf(out, 4, "%%%02X", (uint8_t) *p++); /* %XX */
474
475                         /* Already decremented */
476                         freespace -= 2;
477                         out += 3;
478                 }
479         }
480
481         *out = '\0';
482
483         return outlen - freespace;
484 }
485
486 /** URLdecode special characters
487  *
488  * Example: "%{urlunquote:http%3A%47%47example.org%47}" == "http://example.org/"
489  */
490 static ssize_t urlunquote_xlat(UNUSED void *instance, UNUSED REQUEST *request,
491                                char const *fmt, char *out, size_t outlen)
492 {
493         char const *p;
494         char *c1, *c2;
495         size_t  freespace = outlen;
496
497         if (outlen <= 1) return 0;
498
499         p = fmt;
500         while (*p && (--freespace > 0)) {
501                 if (*p != '%') {
502                         *out++ = *p++;
503                         continue;
504                 }
505                 /* Is a % char */
506
507                 /* Don't need \0 check, as it wont' be in the hextab */
508                 if (!(c1 = memchr(hextab, tolower(*++p), 16)) ||
509                     !(c2 = memchr(hextab, tolower(*++p), 16))) {
510                         REMARKER(fmt, p - fmt, "None hex char in % sequence");
511                         return -1;
512                 }
513                 p++;
514                 *out++ = ((c1 - hextab) << 4) + (c2 - hextab);
515         }
516
517         *out = '\0';
518
519         return outlen - freespace;
520 }
521
522 /** Equivalent to the old safe_characters functionality in rlm_sql
523  *
524  * @verbatim Example: "%{escape:<img>foo.jpg</img>}" == "=60img=62foo.jpg=60/img=62" @endverbatim
525  */
526 static ssize_t escape_xlat(UNUSED void *instance, UNUSED REQUEST *request,
527                            char const *fmt, char *out, size_t outlen)
528 {
529         rlm_expr_t *inst = instance;
530         char const *p;
531         size_t freespace = outlen;
532
533         if (outlen <= 1) return 0;
534
535         p = fmt;
536         while (*p && (--freespace > 0)) {
537                 /*
538                  *      Non-printable characters get replaced with their
539                  *      mime-encoded equivalents.
540                  */
541                 if ((*p > 31) && strchr(inst->allowed_chars, *p)) {
542                         *out++ = *p++;
543                         continue;
544                 }
545
546                 if (freespace < 3)
547                         break;
548
549                 snprintf(out, 4, "=%02X", (uint8_t)*p++);
550
551                 /* Already decremented */
552                 freespace -= 2;
553                 out += 3;
554         }
555
556         *out = '\0';
557
558         return outlen - freespace;
559 }
560
561 /** Equivalent to the old safe_characters functionality in rlm_sql
562  *
563  * @verbatim Example: "%{escape:=60img=62foo.jpg=60/img=62}" == "<img>foo.jpg</img>" @endverbatim
564  */
565 static ssize_t unescape_xlat(UNUSED void *instance, UNUSED REQUEST *request,
566                                char const *fmt, char *out, size_t outlen)
567 {
568         rlm_expr_t *inst = instance;
569         char const *p;
570         char *c1, *c2, c3;
571         size_t  freespace = outlen;
572
573         if (outlen <= 1) return 0;
574
575         p = fmt;
576         while (*p && (--freespace > 0)) {
577                 if (*p != '=') {
578                 next:
579
580                         *out++ = *p++;
581                         continue;
582                 }
583
584                 /* Is a = char */
585
586                 if (!(c1 = memchr(hextab, tolower(*(p + 1)), 16)) ||
587                     !(c2 = memchr(hextab, tolower(*(p + 2)), 16))) goto next;
588                 c3 = ((c1 - hextab) << 4) + (c2 - hextab);
589
590                 /*
591                  *      It was just random occurrence which just happens
592                  *      to match the escape sequence for a safe character.
593                  *      Copy it across verbatim.
594                  */
595                 if (strchr(inst->allowed_chars, c3)) goto next;
596                 *out++ = c3;
597                 p += 3;
598         }
599
600         *out = '\0';
601
602         return outlen - freespace;
603 }
604
605 /** Convert a string to lowercase
606  *
607  * Example "%{tolower:Bar}" == "bar"
608  *
609  * Probably only works for ASCII
610  */
611 static ssize_t lc_xlat(UNUSED void *instance, UNUSED REQUEST *request,
612                        char const *fmt, char *out, size_t outlen)
613 {
614         char *q;
615         char const *p;
616
617         if (outlen <= 1) return 0;
618
619         for (p = fmt, q = out; *p != '\0'; p++, outlen--) {
620                 if (outlen <= 1) break;
621
622                 *(q++) = tolower((int) *p);
623         }
624
625         *q = '\0';
626
627         return strlen(out);
628 }
629
630 /** Convert a string to uppercase
631  *
632  * Example: "%{toupper:Foo}" == "FOO"
633  *
634  * Probably only works for ASCII
635  */
636 static ssize_t uc_xlat(UNUSED void *instance, UNUSED REQUEST *request,
637                        char const *fmt, char *out, size_t outlen)
638 {
639         char *q;
640         char const *p;
641
642         if (outlen <= 1) return 0;
643
644         for (p = fmt, q = out; *p != '\0'; p++, outlen--) {
645                 if (outlen <= 1) break;
646
647                 *(q++) = toupper((int) *p);
648         }
649
650         *q = '\0';
651
652         return strlen(out);
653 }
654
655 /** Calculate the MD5 hash of a string or attribute.
656  *
657  * Example: "%{md5:foo}" == "acbd18db4cc2f85cedef654fccc4a4d8"
658  */
659 static ssize_t md5_xlat(UNUSED void *instance, UNUSED REQUEST *request,
660                         char const *fmt, char *out, size_t outlen)
661 {
662         uint8_t digest[16];
663         ssize_t i, len, inlen;
664         uint8_t const *p;
665         FR_MD5_CTX ctx;
666
667         /*
668          *      We need room for at least one octet of output.
669          */
670         if (outlen < 3) {
671                 *out = '\0';
672                 return 0;
673         }
674
675         inlen = xlat_fmt_to_ref(&p, request, fmt);
676         if (inlen < 0) {
677                 return -1;
678         }
679
680         fr_md5_init(&ctx);
681         fr_md5_update(&ctx, p, inlen);
682         fr_md5_final(digest, &ctx);
683
684         /*
685          *      Each digest octet takes two hex digits, plus one for
686          *      the terminating NUL.
687          */
688         len = (outlen / 2) - 1;
689         if (len > 16) len = 16;
690
691         for (i = 0; i < len; i++) {
692                 snprintf(out + i * 2, 3, "%02x", digest[i]);
693         }
694
695         return strlen(out);
696 }
697
698 /** Calculate the SHA1 hash of a string or attribute.
699  *
700  * Example: "%{sha1:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
701  */
702 static ssize_t sha1_xlat(UNUSED void *instance, UNUSED REQUEST *request,
703                          char const *fmt, char *out, size_t outlen)
704 {
705         uint8_t digest[20];
706         ssize_t i, len, inlen;
707         uint8_t const *p;
708         fr_SHA1_CTX ctx;
709
710         /*
711          *      We need room for at least one octet of output.
712          */
713         if (outlen < 3) {
714                 *out = '\0';
715                 return 0;
716         }
717
718         inlen = xlat_fmt_to_ref(&p, request, fmt);
719         if (inlen < 0) {
720                 return -1;
721         }
722
723         fr_sha1_init(&ctx);
724         fr_sha1_update(&ctx, p, inlen);
725         fr_sha1_final(digest, &ctx);
726
727         /*
728          *      Each digest octet takes two hex digits, plus one for
729          *      the terminating NUL. SHA1 is 160 bits (20 bytes)
730          */
731         len = (outlen / 2) - 1;
732         if (len > 20) len = 20;
733
734         for (i = 0; i < len; i++) {
735                 snprintf(out + i * 2, 3, "%02x", digest[i]);
736         }
737
738         return strlen(out);
739 }
740
741 /** Calculate any digest supported by OpenSSL EVP_MD
742  *
743  * Example: "%{sha256:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
744  */
745 #ifdef HAVE_OPENSSL_EVP_H
746 static ssize_t evp_md_xlat(UNUSED void *instance, UNUSED REQUEST *request,
747                            char const *fmt, char *out, size_t outlen, EVP_MD const *md)
748 {
749         uint8_t digest[EVP_MAX_MD_SIZE];
750         unsigned int digestlen, i, len;
751         ssize_t inlen;
752         uint8_t const *p;
753
754         EVP_MD_CTX *ctx;
755
756         /*
757          *      We need room for at least one octet of output.
758          */
759         if (outlen < 3) {
760                 *out = '\0';
761                 return 0;
762         }
763
764         inlen = xlat_fmt_to_ref(&p, request, fmt);
765         if (inlen < 0) {
766                 return -1;
767         }
768
769         ctx = EVP_MD_CTX_create();
770         EVP_DigestInit_ex(ctx, md, NULL);
771         EVP_DigestUpdate(ctx, p, inlen);
772         EVP_DigestFinal_ex(ctx, digest, &digestlen);
773         EVP_MD_CTX_destroy(ctx);
774
775         /*
776          *      Each digest octet takes two hex digits, plus one for
777          *      the terminating NUL.
778          */
779         len = (outlen / 2) - 1;
780         if (len > digestlen) len = digestlen;
781
782         for (i = 0; i < len; i++) {
783                 snprintf(out + i * 2, 3, "%02x", digest[i]);
784         }
785         return strlen(out);
786 }
787
788 #  define EVP_MD_XLAT(_md) \
789 static ssize_t _md##_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt, char *out, size_t outlen)\
790 {\
791         return evp_md_xlat(instance, request, fmt, out, outlen, EVP_##_md());\
792 }
793
794 EVP_MD_XLAT(sha256);
795 EVP_MD_XLAT(sha512);
796 #endif
797
798 /** Generate the HMAC-MD5 of a string or attribute
799  *
800  * Example: "%{hmacmd5:foo bar}" == "Zm9v"
801  */
802 static ssize_t hmac_md5_xlat(UNUSED void *instance, UNUSED REQUEST *request,
803                              char const *fmt, char *out, size_t outlen)
804 {
805         uint8_t const *data, *key;
806         char const *p;
807         ssize_t data_len, key_len;
808         uint8_t digest[MD5_DIGEST_LENGTH];
809         char data_ref[256];
810
811         if (outlen <= (sizeof(digest) * 2)) {
812                 REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes",
813                         (sizeof(digest) * 2) + 1, outlen);
814                 return -1;
815         }
816
817         p = strchr(fmt, ' ');
818         if (!p) {
819                 REDEBUG("HMAC requires exactly two arguments (&data &key)");
820                 return -1;
821         }
822
823         if ((size_t)(p - fmt) >= sizeof(data_ref)) {
824                 REDEBUG("Insufficient space to store HMAC input data, needed %zu bytes, have %zu bytes",
825                        (p - fmt) + 1, sizeof(data_ref));
826
827                 return -1;
828         }
829         strlcpy(data_ref, fmt, (p - fmt) + 1);
830
831         data_len = xlat_fmt_to_ref(&data, request, data_ref);
832         if (data_len < 0) return -1;
833
834         while (isspace(*p) && p++);
835
836         key_len = xlat_fmt_to_ref(&key, request, p);
837         if (key_len < 0) return -1;
838
839         fr_hmac_md5(digest, data, data_len, key, key_len);
840
841         return fr_bin2hex(out, digest, sizeof(digest));
842 }
843
844 /** Generate the HMAC-SHA1 of a string or attribute
845  *
846  * Example: "%{hmacsha1:foo bar}" == "Zm9v"
847  */
848 static ssize_t hmac_sha1_xlat(UNUSED void *instance, UNUSED REQUEST *request,
849                               char const *fmt, char *out, size_t outlen)
850 {
851         uint8_t const *data, *key;
852         char const *p;
853         ssize_t data_len, key_len;
854         uint8_t digest[SHA1_DIGEST_LENGTH];
855         char data_ref[256];
856
857         if (outlen <= (sizeof(digest) * 2)) {
858                 REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes",
859                         (sizeof(digest) * 2) + 1, outlen);
860                 return -1;
861         }
862
863         p = strchr(fmt, ' ');
864         if (!p) {
865                 REDEBUG("HMAC requires exactly two arguments (&data &key)");
866                 return -1;
867         }
868
869         if ((size_t)(p - fmt) >= sizeof(data_ref)) {
870                 REDEBUG("Insufficient space to store HMAC input data, needed %zu bytes, have %zu bytes",
871                         (p - fmt) + 1, sizeof(data_ref));
872
873                 return -1;
874         }
875         strlcpy(data_ref, fmt, (p - fmt) + 1);
876
877         data_len = xlat_fmt_to_ref(&data, request, data_ref);
878         if (data_len < 0) return -1;
879
880         while (isspace(*p) && p++);
881
882         key_len = xlat_fmt_to_ref(&key, request, p);
883         if (key_len < 0) return -1;
884
885         fr_hmac_sha1(digest, data, data_len, key, key_len);
886
887         return fr_bin2hex(out, digest, sizeof(digest));
888 }
889
890 /** Encode string or attribute as base64
891  *
892  * Example: "%{base64:foo}" == "Zm9v"
893  */
894 static ssize_t base64_xlat(UNUSED void *instance, UNUSED REQUEST *request,
895                            char const *fmt, char *out, size_t outlen)
896 {
897         ssize_t inlen;
898         uint8_t const *p;
899
900         inlen = xlat_fmt_to_ref(&p, request, fmt);
901         if (inlen < 0) {
902                 return -1;
903         }
904
905         /*
906          *  We can accurately calculate the length of the output string
907          *  if it's larger than outlen, the output would be useless so abort.
908          */
909         if ((inlen < 0) || ((FR_BASE64_ENC_LENGTH(inlen) + 1) > (ssize_t) outlen)) {
910                 REDEBUG("xlat failed");
911                 *out = '\0';
912                 return -1;
913         }
914
915         return fr_base64_encode(out, outlen, p, inlen);
916 }
917
918 /** Convert base64 to hex
919  *
920  * Example: "%{base64tohex:Zm9v}" == "666f6f"
921  */
922 static ssize_t base64_to_hex_xlat(UNUSED void *instance, UNUSED REQUEST *request,
923                                   char const *fmt, char *out, size_t outlen)
924 {
925         uint8_t decbuf[1024];
926
927         ssize_t declen;
928         ssize_t len = strlen(fmt);
929
930         *out = '\0';
931
932         declen = fr_base64_decode(decbuf, sizeof(decbuf), fmt, len);
933         if (declen < 0) {
934                 REDEBUG("Base64 string invalid");
935                 return -1;
936         }
937
938         if ((size_t)((declen * 2) + 1) > outlen) {
939                 REDEBUG("Base64 conversion failed, output buffer exhausted, needed %zd bytes, have %zd bytes",
940                         (declen * 2) + 1, outlen);
941                 return -1;
942         }
943
944         return fr_bin2hex(out, decbuf, declen);
945 }
946
947
948 /*
949  *      Do any per-module initialization that is separate to each
950  *      configured instance of the module.  e.g. set up connections
951  *      to external databases, read configuration files, set up
952  *      dictionary entries, etc.
953  *
954  *      If configuration information is given in the config section
955  *      that must be referenced in later calls, store a handle to it
956  *      in *instance otherwise put a null pointer there.
957  */
958 static int mod_instantiate(CONF_SECTION *conf, void *instance)
959 {
960         rlm_expr_t *inst = instance;
961
962         inst->xlat_name = cf_section_name2(conf);
963         if (!inst->xlat_name) {
964                 inst->xlat_name = cf_section_name1(conf);
965         }
966
967         xlat_register(inst->xlat_name, expr_xlat, NULL, inst);
968
969         /*
970          *      FIXME: unregister these, too
971          */
972         xlat_register("rand", rand_xlat, NULL, inst);
973         xlat_register("randstr", randstr_xlat, NULL, inst);
974         xlat_register("urlquote", urlquote_xlat, NULL, inst);
975         xlat_register("urlunquote", urlunquote_xlat, NULL, inst);
976         xlat_register("escape", escape_xlat, NULL, inst);
977         xlat_register("unescape", unescape_xlat, NULL, inst);
978         xlat_register("tolower", lc_xlat, NULL, inst);
979         xlat_register("toupper", uc_xlat, NULL, inst);
980         xlat_register("md5", md5_xlat, NULL, inst);
981         xlat_register("sha1", sha1_xlat, NULL, inst);
982 #ifdef HAVE_OPENSSL_EVP_H
983         xlat_register("sha256", sha256_xlat, NULL, inst);
984         xlat_register("sha512", sha512_xlat, NULL, inst);
985 #endif
986         xlat_register("hmacmd5", hmac_md5_xlat, NULL, inst);
987         xlat_register("hmacsha1", hmac_sha1_xlat, NULL, inst);
988         xlat_register("base64", base64_xlat, NULL, inst);
989         xlat_register("base64tohex", base64_to_hex_xlat, NULL, inst);
990
991         /*
992          *      Initialize various paircompare functions
993          */
994         pair_builtincompare_add(instance);
995         return 0;
996 }
997
998 /*
999  *      The module name should be the only globally exported symbol.
1000  *      That is, everything else should be 'static'.
1001  *
1002  *      If the module needs to temporarily modify it's instantiation
1003  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
1004  *      The server will then take care of ensuring that the module
1005  *      is single-threaded.
1006  */
1007 module_t rlm_expr = {
1008         RLM_MODULE_INIT,
1009         "expr",                         /* Name */
1010         0,      /* type */
1011         sizeof(rlm_expr_t),
1012         module_config,
1013         mod_instantiate,                /* instantiation */
1014         NULL,                           /* detach */
1015         {
1016                 NULL,                   /* authentication */
1017                 NULL,                   /* authorization */
1018                 NULL,                   /* pre-accounting */
1019                 NULL                    /* accounting */
1020         },
1021 };