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 as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * @brief Register many xlat expansions including the expr expansion.
22 * @copyright 2001,2006 The FreeRADIUS server project
23 * @copyright 2002 Alan DeKok <aland@ox.org>
26 USES_APPLE_DEPRECATED_API
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/md5.h>
30 #include <freeradius-devel/sha1.h>
31 #include <freeradius-devel/base64.h>
32 #include <freeradius-devel/modules.h>
33 #include <freeradius-devel/rad_assert.h>
35 #ifdef HAVE_OPENSSL_EVP_H
36 # include <openssl/evp.h>
44 * Define a structure for our module configuration.
46 typedef struct rlm_expr_t {
47 char const *xlat_name;
48 char const *allowed_chars;
51 static const CONF_PARSER module_config[] = {
52 { "safe_characters", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_expr_t, allowed_chars), "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" },
53 CONF_PARSER_TERMINATOR
57 * Lookup tables for randstr char classes
59 static char randstr_punc[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
60 static char randstr_salt[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz/.";
63 * Characters humans rarely confuse. Reduces char set considerably
64 * should only be used for things such as one time passwords.
66 static char randstr_otp[] = "469ACGHJKLMNPQRUVWXYabdfhijkprstuvwxyz";
68 static char const hextab[] = "0123456789abcdef";
72 * @author Orson Peters
73 * @note Borrowed from the gist here: https://gist.github.com/nightcracker/3551590.
75 * @param base a 32bit signed integer.
76 * @param exp amount to raise base by.
77 * @return base ^ pow, or 0 on underflow/overflow.
79 static int64_t fr_pow(int64_t base, int64_t exp)
81 static const uint8_t highest_bit_set[] = {
82 0, 1, 2, 2, 3, 3, 3, 3,
83 4, 4, 4, 4, 4, 4, 4, 4,
84 5, 5, 5, 5, 5, 5, 5, 5,
85 5, 5, 5, 5, 5, 5, 5, 5,
86 6, 6, 6, 6, 6, 6, 6, 6,
87 6, 6, 6, 6, 6, 6, 6, 6,
88 6, 6, 6, 6, 6, 6, 6, 6,
89 6, 6, 6, 6, 6, 6, 6, 6 // anything past 63 is a guaranteed overflow with base > 1
100 return 1 - 2 * (exp & 1);
102 return 0; /* overflow */
105 switch (highest_bit_set[exp]) {
107 if (exp & 1) result *= base;
111 if (exp & 1) result *= base;
115 if (exp & 1) result *= base;
119 if (exp & 1) result *= base;
123 if (exp & 1) result *= base;
127 if (exp & 1) result *= base;
134 * Start of expression calculator.
136 typedef enum expr_token_t {
157 static int precedence[TOKEN_LAST + 1] = {
158 0, 0, 1, 1, /* and or */
159 2, 2, 3, 3, /* shift add */
160 4, 4, 4, 5, /* mul, pow */
164 typedef struct expr_map_t {
169 static expr_map_t map[] =
172 {'-', TOKEN_SUBTRACT },
173 {'/', TOKEN_DIVIDE },
174 {'*', TOKEN_MULTIPLY },
175 {'%', TOKEN_REMAINDER },
182 static bool get_expression(REQUEST *request, char const **string, int64_t *answer, expr_token_t prev);
184 static bool get_number(REQUEST *request, char const **string, int64_t *answer)
188 bool negative = false;
189 char const *p = *string;
194 while (isspace((int) *p)) p++;
205 * No algrebraic operator found, the next thing
208 * If it isn't, then we die.
210 if ((*p == '0') && (p[1] == 'x')) {
213 x = strtoul(p, &end, 16);
224 * Look for an attribute.
233 slen = tmpl_from_attr_substr(&vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
235 REDEBUG("Failed parsing attribute name '%s': %s", p, fr_strerror());
241 if (tmpl_find_vp(&vp, request, &vpt) < 0) {
242 RWDEBUG("Can't find &%.*s. Using 0 as operand value", (int)vpt.len, vpt.name);
247 if (vp->da->type != PW_TYPE_INTEGER64) {
250 if (value_data_cast(vp, &value, PW_TYPE_INTEGER64, NULL, vp->da->type, vp->da, &vp->data, vp->vp_length) < 0) {
251 REDEBUG("Failed converting &%.*s to an integer value: %s", (int) vpt.len,
252 vpt.name, fr_strerror());
255 if (value.integer64 > INT64_MAX) {
257 REDEBUG("Value of &%.*s (%"PRIu64 ") would overflow a signed 64bit integer "
258 "(our internal arithmetic type)", (int)vpt.len, vpt.name, value.integer64);
261 x = (int64_t)value.integer64;
264 RDEBUG3("&%.*s --> %" PRIu64, (int)vpt.len, vpt.name, x);
267 if (vp->vp_integer64 > INT64_MAX) goto overflow;
268 x = (int64_t)vp->vp_integer64;
275 * Do brackets recursively
279 if (!get_expression(request, &p, &x, TOKEN_NONE)) return false;
282 RDEBUG("No trailing ')'");
289 if ((*p < '0') || (*p > '9')) {
290 RDEBUG2("Not a number at \"%s\"", p);
295 * This is doing it the hard way, but it also allows
296 * us to increment 'p'.
299 while ((*p >= '0') && (*p <= '9')) {
308 if (negative) x = -x;
315 static bool calc_result(REQUEST *request, int64_t lhs, expr_token_t op, int64_t rhs, int64_t *answer)
329 RDEBUG("Division by zero!");
336 case TOKEN_REMAINDER:
338 RDEBUG("Division by zero!");
351 RDEBUG("Shift must be less than 63 (was %lld)", (long long int) rhs);
355 *answer = lhs << rhs;
360 RDEBUG("Shift must be less than 63 (was %lld)", (long long int) rhs);
364 *answer = lhs >> rhs;
377 REDEBUG("Exponent must be between 0-63 (was %lld)", (long long int) rhs);
382 REDEBUG("Base must be between 0-65535 (was %lld)", (long long int) lhs);
386 *answer = fr_pow(lhs, rhs);
393 static bool get_operator(REQUEST *request, char const **string, expr_token_t *op)
396 char const *p = *string;
399 * All tokens are one character.
401 for (i = 0; map[i].token != TOKEN_LAST; i++) {
402 if (*p == map[i].op) {
409 if ((p[0] == '<') && (p[1] == '<')) {
415 if ((p[0] == '>') && (p[1] == '>')) {
421 RDEBUG("Expected operator at \"%s\"", p);
426 static bool get_expression(REQUEST *request, char const **string, int64_t *answer, expr_token_t prev)
429 char const *p, *op_p;
434 if (!get_number(request, &p, &lhs)) return false;
437 while (isspace((int) *p)) p++;
440 * A number by itself is OK.
442 if (!*p || (*p == ')')) {
449 * Peek at the operator.
452 if (!get_operator(request, &p, &this)) return false;
455 * a + b + c ... = (a + b) + c ...
456 * a * b + c ... = (a * b) + c ...
458 * Feed the current number to the caller, who will take
459 * care of continuing.
461 if (precedence[this] <= precedence[prev]) {
468 * a + b * c ... = a + (b * c) ...
470 if (!get_expression(request, &p, &rhs, this)) return false;
472 if (!calc_result(request, lhs, this, rhs, answer)) return false;
475 * There may be more to calculate. The answer we
476 * calculated here is now the LHS of the lower priority
477 * operation which follows the current expression. e.g.
479 * a * b + c ... = (a * b) + c ...
487 * Do xlat of strings!
489 static ssize_t expr_xlat(UNUSED void *instance, REQUEST *request, char const *fmt,
490 char *out, size_t outlen)
497 if (!get_expression(request, &p, &result, TOKEN_NONE)) {
502 RDEBUG("Invalid text after expression: %s", p);
506 snprintf(out, outlen, "%lld", (long long int) result);
510 /** Generate a random integer value
513 static ssize_t rand_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt,
514 char *out, size_t outlen)
521 * Too small or too big.
527 if (result >= (1 << 30)) result = (1 << 30);
529 result *= fr_rand(); /* 0..2^32-1 */
532 snprintf(out, outlen, "%ld", (long int) result);
536 /** Generate a string of random chars
538 * Build strings of random chars, useful for generating tokens and passcodes
539 * Format similar to String::Random.
541 static ssize_t randstr_xlat(UNUSED void *instance, UNUSED REQUEST *request,
542 char const *fmt, char *out, size_t outlen)
547 size_t freespace = outlen;
549 if (outlen <= 1) return 0;
554 while (*p && (--freespace > 0)) {
558 * Modifiers are polite.
560 * But we limit it to 100, because we don't want
563 while (isdigit((int) *p)) {
582 *out++ = 'a' + (result % 26);
589 *out++ = 'A' + (result % 26);
596 *out++ = '0' + (result % 10);
603 *out++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
610 *out++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
614 * Alpa numeric + punctuation
617 *out++ = '!' + (result % 95);
621 * Alpha numeric + salt chars './'
624 *out++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
628 * Chars suitable for One Time Password tokens.
629 * Alpha numeric with easily confused char pairs removed.
632 *out++ = randstr_otp[result % (sizeof(randstr_otp) - 1)];
636 * Binary data as hexits (we don't really support
637 * non printable chars).
644 snprintf(out, 3, "%02x", result % 256);
646 /* Already decremented */
652 * Binary data with uppercase hexits
659 snprintf(out, 3, "%02X", result % 256);
661 /* Already decremented */
667 ERROR("rlm_expr: invalid character class '%c'", *p);
682 return outlen - freespace;
685 /** URLencode special characters
687 * Example: "%{urlquote:http://example.org/}" == "http%3A%47%47example.org%47"
689 static ssize_t urlquote_xlat(UNUSED void *instance, UNUSED REQUEST *request,
690 char const *fmt, char *out, size_t outlen)
693 size_t freespace = outlen;
695 if (outlen <= 1) return 0;
698 while (*p && (--freespace > 0)) {
716 /* MUST be upper case hex to be compliant */
717 snprintf(out, 4, "%%%02X", (uint8_t) *p++); /* %XX */
719 /* Already decremented */
727 return outlen - freespace;
730 /** URLdecode special characters
732 * Example: "%{urlunquote:http%%3A%%47%%47example.org%%47}" == "http://example.org/"
734 * Remember to escape % with %% in strings, else xlat will try to parse it.
736 static ssize_t urlunquote_xlat(UNUSED void *instance, REQUEST *request,
737 char const *fmt, char *out, size_t outlen)
741 size_t freespace = outlen;
743 if (outlen <= 1) return 0;
746 while (*p && (--freespace > 0)) {
753 /* Don't need \0 check, as it won't be in the hextab */
754 if (!(c1 = memchr(hextab, tolower(*++p), 16)) ||
755 !(c2 = memchr(hextab, tolower(*++p), 16))) {
756 REMARKER(fmt, p - fmt, "None hex char in % sequence");
760 *out++ = ((c1 - hextab) << 4) + (c2 - hextab);
765 return outlen - freespace;
768 /** Equivalent to the old safe_characters functionality in rlm_sql but with utf8 support
770 * @verbatim Example: "%{escape:<img>foo.jpg</img>}" == "=60img=62foo.jpg=60/img=62" @endverbatim
772 static ssize_t escape_xlat(void *instance, UNUSED REQUEST *request,
773 char const *fmt, char *out, size_t outlen)
775 rlm_expr_t *inst = instance;
777 size_t freespace = outlen;
781 int ret = 1; /* -Werror=uninitialized */
783 if (fr_utf8_strchr(&chr_len, inst->allowed_chars, p) == NULL) {
785 * '=' 1 + ([hex]{2}) * chr_len)
787 if (freespace <= (size_t)(1 + (chr_len * 3))) break;
791 ret = snprintf(out, freespace, "=%02X=%02X=%02X=%02X",
792 (uint8_t)p[0], (uint8_t)p[1], (uint8_t)p[2], (uint8_t)p[3]);
796 ret = snprintf(out, freespace, "=%02X=%02X=%02X",
797 (uint8_t)p[0], (uint8_t)p[1], (uint8_t)p[2]);
801 ret = snprintf(out, freespace, "=%02X=%02X", (uint8_t)p[0], (uint8_t)p[1]);
805 ret = snprintf(out, freespace, "=%02X", (uint8_t)p[0]);
816 * Only one byte left.
818 if (freespace <= 1) break;
821 * Allowed character (copy whole mb chars at once)
823 memcpy(out, p, chr_len);
826 freespace -= chr_len;
830 return outlen - freespace;
833 /** Equivalent to the old safe_characters functionality in rlm_sql
835 * @verbatim Example: "%{unescape:=60img=62foo.jpg=60/img=62}" == "<img>foo.jpg</img>" @endverbatim
837 static ssize_t unescape_xlat(UNUSED void *instance, UNUSED REQUEST *request,
838 char const *fmt, char *out, size_t outlen)
842 size_t freespace = outlen;
844 if (outlen <= 1) return 0;
847 while (*p && (--freespace > 0)) {
857 if (!(c1 = memchr(hextab, tolower(*(p + 1)), 16)) ||
858 !(c2 = memchr(hextab, tolower(*(p + 2)), 16))) goto next;
859 c3 = ((c1 - hextab) << 4) + (c2 - hextab);
867 return outlen - freespace;
870 /** Convert a string to lowercase
872 * Example: "%{tolower:Bar}" == "bar"
874 * Probably only works for ASCII
876 static ssize_t lc_xlat(UNUSED void *instance, UNUSED REQUEST *request,
877 char const *fmt, char *out, size_t outlen)
882 if (outlen <= 1) return 0;
884 for (p = fmt, q = out; *p != '\0'; p++, outlen--) {
885 if (outlen <= 1) break;
887 *(q++) = tolower((int) *p);
895 /** Convert a string to uppercase
897 * Example: "%{toupper:Foo}" == "FOO"
899 * Probably only works for ASCII
901 static ssize_t uc_xlat(UNUSED void *instance, UNUSED REQUEST *request,
902 char const *fmt, char *out, size_t outlen)
907 if (outlen <= 1) return 0;
909 for (p = fmt, q = out; *p != '\0'; p++, outlen--) {
910 if (outlen <= 1) break;
912 *(q++) = toupper((int) *p);
920 /** Calculate the MD5 hash of a string or attribute.
922 * Example: "%{md5:foo}" == "acbd18db4cc2f85cedef654fccc4a4d8"
924 static ssize_t md5_xlat(UNUSED void *instance, REQUEST *request,
925 char const *fmt, char *out, size_t outlen)
928 ssize_t i, len, inlen;
933 * We need room for at least one octet of output.
940 inlen = xlat_fmt_to_ref(&p, request, fmt);
946 fr_md5_update(&ctx, p, inlen);
947 fr_md5_final(digest, &ctx);
950 * Each digest octet takes two hex digits, plus one for
951 * the terminating NUL.
953 len = (outlen / 2) - 1;
954 if (len > 16) len = 16;
956 for (i = 0; i < len; i++) {
957 snprintf(out + i * 2, 3, "%02x", digest[i]);
963 /** Calculate the SHA1 hash of a string or attribute.
965 * Example: "%{sha1:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
967 static ssize_t sha1_xlat(UNUSED void *instance, REQUEST *request,
968 char const *fmt, char *out, size_t outlen)
971 ssize_t i, len, inlen;
976 * We need room for at least one octet of output.
983 inlen = xlat_fmt_to_ref(&p, request, fmt);
989 fr_sha1_update(&ctx, p, inlen);
990 fr_sha1_final(digest, &ctx);
993 * Each digest octet takes two hex digits, plus one for
994 * the terminating NUL. SHA1 is 160 bits (20 bytes)
996 len = (outlen / 2) - 1;
997 if (len > 20) len = 20;
999 for (i = 0; i < len; i++) {
1000 snprintf(out + i * 2, 3, "%02x", digest[i]);
1006 /** Calculate any digest supported by OpenSSL EVP_MD
1008 * Example: "%{sha256:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
1010 #ifdef HAVE_OPENSSL_EVP_H
1011 static ssize_t evp_md_xlat(UNUSED void *instance, REQUEST *request,
1012 char const *fmt, char *out, size_t outlen, EVP_MD const *md)
1014 uint8_t digest[EVP_MAX_MD_SIZE];
1015 unsigned int digestlen, i, len;
1022 * We need room for at least one octet of output.
1029 inlen = xlat_fmt_to_ref(&p, request, fmt);
1034 ctx = EVP_MD_CTX_create();
1035 EVP_DigestInit_ex(ctx, md, NULL);
1036 EVP_DigestUpdate(ctx, p, inlen);
1037 EVP_DigestFinal_ex(ctx, digest, &digestlen);
1038 EVP_MD_CTX_destroy(ctx);
1041 * Each digest octet takes two hex digits, plus one for
1042 * the terminating NUL.
1044 len = (outlen / 2) - 1;
1045 if (len > digestlen) len = digestlen;
1047 for (i = 0; i < len; i++) {
1048 snprintf(out + i * 2, 3, "%02x", digest[i]);
1053 # define EVP_MD_XLAT(_md) \
1054 static ssize_t _md##_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen)\
1056 return evp_md_xlat(instance, request, fmt, out, outlen, EVP_##_md());\
1063 /** Generate the HMAC-MD5 of a string or attribute
1065 * Example: "%{hmacmd5:foo bar}" == "Zm9v"
1067 static ssize_t hmac_md5_xlat(UNUSED void *instance, REQUEST *request,
1068 char const *fmt, char *out, size_t outlen)
1070 uint8_t const *data, *key;
1072 ssize_t data_len, key_len;
1073 uint8_t digest[MD5_DIGEST_LENGTH];
1076 if (outlen <= (sizeof(digest) * 2)) {
1077 REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes",
1078 (sizeof(digest) * 2) + 1, outlen);
1082 p = strchr(fmt, ' ');
1084 REDEBUG("HMAC requires exactly two arguments (&data &key)");
1088 if ((size_t)(p - fmt) >= sizeof(data_ref)) {
1089 REDEBUG("Insufficient space to store HMAC input data, needed %zu bytes, have %zu bytes",
1090 (p - fmt) + 1, sizeof(data_ref));
1094 strlcpy(data_ref, fmt, (p - fmt) + 1);
1096 data_len = xlat_fmt_to_ref(&data, request, data_ref);
1097 if (data_len < 0) return -1;
1099 while (isspace(*p) && p++);
1101 key_len = xlat_fmt_to_ref(&key, request, p);
1102 if (key_len < 0) return -1;
1104 fr_hmac_md5(digest, data, data_len, key, key_len);
1106 return fr_bin2hex(out, digest, sizeof(digest));
1109 /** Generate the HMAC-SHA1 of a string or attribute
1111 * Example: "%{hmacsha1:foo bar}" == "Zm9v"
1113 static ssize_t hmac_sha1_xlat(UNUSED void *instance, REQUEST *request,
1114 char const *fmt, char *out, size_t outlen)
1116 uint8_t const *data, *key;
1118 ssize_t data_len, key_len;
1119 uint8_t digest[SHA1_DIGEST_LENGTH];
1122 if (outlen <= (sizeof(digest) * 2)) {
1123 REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes",
1124 (sizeof(digest) * 2) + 1, outlen);
1128 p = strchr(fmt, ' ');
1130 REDEBUG("HMAC requires exactly two arguments (&data &key)");
1134 if ((size_t)(p - fmt) >= sizeof(data_ref)) {
1135 REDEBUG("Insufficient space to store HMAC input data, needed %zu bytes, have %zu bytes",
1136 (p - fmt) + 1, sizeof(data_ref));
1140 strlcpy(data_ref, fmt, (p - fmt) + 1);
1142 data_len = xlat_fmt_to_ref(&data, request, data_ref);
1143 if (data_len < 0) return -1;
1145 while (isspace(*p) && p++);
1147 key_len = xlat_fmt_to_ref(&key, request, p);
1148 if (key_len < 0) return -1;
1150 fr_hmac_sha1(digest, data, data_len, key, key_len);
1152 return fr_bin2hex(out, digest, sizeof(digest));
1155 /** Encode attributes as a series of string attribute/value pairs
1157 * This is intended to serialize one or more attributes as a comma
1160 * Example: "%{pairs:request:}" == "User-Name = 'foo', User-Password = 'bar'"
1162 static ssize_t pairs_xlat(UNUSED void *instance, REQUEST *request,
1163 char const *fmt, char *out, size_t outlen)
1167 size_t len, freespace = outlen;
1172 if (tmpl_from_attr_str(&vpt, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) <= 0) {
1173 REDEBUG("%s", fr_strerror());
1177 for (vp = tmpl_cursor_init(NULL, &cursor, request, &vpt);
1179 vp = tmpl_cursor_next(&cursor, &vpt)) {
1180 FR_TOKEN op = vp->op;
1183 len = vp_prints(p, freespace, vp);
1186 if (is_truncated(len, freespace)) {
1188 REDEBUG("Insufficient space to store pair string, needed %zu bytes have %zu bytes",
1189 (p - out) + len, outlen);
1196 if (freespace < 2) {
1206 /* Trim the trailing ', ' */
1207 if (p != out) p -= 2;
1213 /** Encode string or attribute as base64
1215 * Example: "%{base64:foo}" == "Zm9v"
1217 static ssize_t base64_xlat(UNUSED void *instance, REQUEST *request,
1218 char const *fmt, char *out, size_t outlen)
1223 inlen = xlat_fmt_to_ref(&p, request, fmt);
1229 * We can accurately calculate the length of the output string
1230 * if it's larger than outlen, the output would be useless so abort.
1232 if ((inlen < 0) || ((FR_BASE64_ENC_LENGTH(inlen) + 1) > (ssize_t) outlen)) {
1233 REDEBUG("xlat failed");
1238 return fr_base64_encode(out, outlen, p, inlen);
1241 /** Convert base64 to hex
1243 * Example: "%{base64tohex:Zm9v}" == "666f6f"
1245 static ssize_t base64_to_hex_xlat(UNUSED void *instance, REQUEST *request,
1246 char const *fmt, char *out, size_t outlen)
1248 uint8_t decbuf[1024];
1251 ssize_t len = strlen(fmt);
1255 declen = fr_base64_decode(decbuf, sizeof(decbuf), fmt, len);
1257 REDEBUG("Base64 string invalid");
1261 if ((size_t)((declen * 2) + 1) > outlen) {
1262 REDEBUG("Base64 conversion failed, output buffer exhausted, needed %zd bytes, have %zd bytes",
1263 (declen * 2) + 1, outlen);
1267 return fr_bin2hex(out, decbuf, declen);
1270 /** Split an attribute into multiple new attributes based on a delimiter
1272 * @todo should support multibyte delimiter for string types.
1274 * Example: "%{explode:&ref <delim>}"
1276 static ssize_t explode_xlat(UNUSED void *instance, REQUEST *request,
1277 char const *fmt, char *out, size_t outlen)
1280 vp_cursor_t cursor, to_merge;
1281 VALUE_PAIR *vp, *head = NULL;
1284 char const *p = fmt;
1290 while (isspace(*p) && p++);
1292 slen = tmpl_from_attr_substr(&vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
1294 REDEBUG("%s", fr_strerror());
1302 REDEBUG("explode needs exactly two arguments: &ref <delim>");
1306 if (*p == '\0') goto arg_error;
1310 fr_cursor_init(&to_merge, &head);
1312 for (vp = tmpl_cursor_init(NULL, &cursor, request, &vpt);
1314 vp = tmpl_cursor_next(&cursor, &vpt)) {
1320 * This can theoretically operate on lists too
1321 * so we need to check the type of each attribute.
1323 switch (vp->da->type) {
1324 case PW_TYPE_OCTETS:
1325 case PW_TYPE_STRING:
1333 end = p + vp->vp_length;
1335 q = memchr(p, delim, end - p);
1337 /* Delimiter not present in attribute */
1338 if (p == vp->data.ptr) goto next;
1342 /* Skip zero length */
1348 new = fr_pair_afrom_da(talloc_parent(vp), vp->da);
1350 fr_pair_list_free(&head);
1355 switch (vp->da->type) {
1356 case PW_TYPE_OCTETS:
1360 buff = talloc_array(new, uint8_t, q - p);
1361 memcpy(buff, p, q - p);
1362 fr_pair_value_memsteal(new, buff);
1366 case PW_TYPE_STRING:
1370 buff = talloc_array(new, char, (q - p) + 1);
1371 memcpy(buff, p, q - p);
1373 fr_pair_value_strsteal(new, (char *)buff);
1381 fr_cursor_insert(&to_merge, new);
1383 p = q + 1; /* next */
1389 * Remove the unexploded version
1391 vp = fr_cursor_remove(&cursor);
1395 continue; /* Apparently goto labels aren't allowed at the end of loops? */
1398 fr_cursor_merge(&cursor, head);
1400 return snprintf(out, outlen, "%i", count);
1403 /** Calculate number of seconds until the next n hour(s), day(s), week(s), year(s).
1405 * For example, if it were 16:18 %{nexttime:1h} would expand to 2520.
1407 * The envisaged usage for this function is to limit sessions so that they don't
1408 * cross billing periods. The output of the xlat should be combined with %{rand:} to create
1409 * some jitter, unless the desired effect is every subscriber on the network
1410 * re-authenticating at the same time.
1412 static ssize_t next_time_xlat(UNUSED void *instance, REQUEST *request,
1413 char const *fmt, char *out, size_t outlen)
1420 struct tm *local, local_buff;
1423 local = localtime_r(&now, &local_buff);
1427 num = strtoul(p, &q, 10);
1428 if (!q || *q == '\0') {
1429 REDEBUG("nexttime: <int> must be followed by period specifier (h|d|w|m|y)");
1444 local->tm_hour += num;
1449 local->tm_mday += num;
1454 local->tm_mday += (7 - local->tm_wday) + (7 * (num-1));
1460 local->tm_mon += num;
1467 local->tm_year += num;
1471 REDEBUG("nexttime: Invalid period specifier '%c', must be h|d|w|m|y", *p);
1475 return snprintf(out, outlen, "%" PRIu64, (uint64_t)(mktime(local) - now));
1480 * Parse the 3 arguments to lpad / rpad.
1482 static bool parse_pad(REQUEST *request, char const *fmt,
1483 vp_tmpl_t **pvpt, size_t *plength,
1487 unsigned long length;
1492 *fill = ' '; /* the default */
1495 while (isspace((int) *p)) p++;
1498 RDEBUG("First argument must be an attribute reference");
1502 vpt = talloc(request, vp_tmpl_t);
1503 if (!vpt) return false;
1505 slen = tmpl_from_attr_substr(vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
1508 RDEBUG("Failed expanding string: %s", fr_strerror());
1514 while (isspace((int) *p)) p++;
1516 length = strtoul(p, &end, 10);
1517 if ((length == ULONG_MAX) || (length > 8192)) {
1519 RDEBUG("Invalid length found at: %s", p);
1526 * The fill character is optional.
1528 * But we must have a space after the previous number,
1529 * and we must have only ONE fill character.
1534 RDEBUG("Invalid text found at: %s", p);
1538 while (isspace((int) *p)) p++;
1542 RDEBUG("Invalid text found at: %s", p);
1556 /** left pad a string
1558 * %{lpad:&Attribute-Name length 'x'}
1560 static ssize_t lpad_xlat(UNUSED void *instance, REQUEST *request,
1561 char const *fmt, char *out, size_t outlen)
1569 if (!parse_pad(request, fmt, &vpt, &pad, &fill)) {
1573 if (outlen <= pad) {
1574 RWARN("Output is too short! Result will be truncated");
1579 * Print the attribute (left justified). If it's too
1582 len = tmpl_expand(NULL, out, pad + 1, request, vpt, NULL, NULL);
1583 if (len <= 0) return 0;
1585 if ((size_t) len >= pad) return pad;
1588 * We have to shift the string to the right, and pad with
1589 * "fill" characters.
1591 memmove(out + (pad - len), out, len + 1);
1592 memset(out, fill, pad - len);
1598 /** right pad a string
1600 * %{rpad:&Attribute-Name length 'x'}
1602 static ssize_t rpad_xlat(UNUSED void *instance, REQUEST *request,
1603 char const *fmt, char *out, size_t outlen)
1612 if (!parse_pad(request, fmt, &vpt, &pad, &fill)) {
1616 if (outlen <= pad) {
1617 RWARN("Output is too short! Result will be truncated");
1622 * Print the attribute (left justified). If it's too
1625 len = tmpl_expand(NULL, out, pad + 1, request, vpt, NULL, NULL);
1626 if (len <= 0) return 0;
1628 if ((size_t) len >= pad) return pad;
1631 * We have to pad with "fill" characters.
1633 memset(out + len, fill, pad - len);
1641 * Do any per-module initialization that is separate to each
1642 * configured instance of the module. e.g. set up connections
1643 * to external databases, read configuration files, set up
1644 * dictionary entries, etc.
1646 * If configuration information is given in the config section
1647 * that must be referenced in later calls, store a handle to it
1648 * in *instance otherwise put a null pointer there.
1650 static int mod_bootstrap(CONF_SECTION *conf, void *instance)
1652 rlm_expr_t *inst = instance;
1654 inst->xlat_name = cf_section_name2(conf);
1655 if (!inst->xlat_name) {
1656 inst->xlat_name = cf_section_name1(conf);
1659 xlat_register(inst->xlat_name, expr_xlat, NULL, inst);
1661 xlat_register("rand", rand_xlat, NULL, inst);
1662 xlat_register("randstr", randstr_xlat, NULL, inst);
1663 xlat_register("urlquote", urlquote_xlat, NULL, inst);
1664 xlat_register("urlunquote", urlunquote_xlat, NULL, inst);
1665 xlat_register("escape", escape_xlat, NULL, inst);
1666 xlat_register("unescape", unescape_xlat, NULL, inst);
1667 xlat_register("tolower", lc_xlat, NULL, inst);
1668 xlat_register("toupper", uc_xlat, NULL, inst);
1669 xlat_register("md5", md5_xlat, NULL, inst);
1670 xlat_register("sha1", sha1_xlat, NULL, inst);
1671 #ifdef HAVE_OPENSSL_EVP_H
1672 xlat_register("sha256", sha256_xlat, NULL, inst);
1673 xlat_register("sha512", sha512_xlat, NULL, inst);
1675 xlat_register("hmacmd5", hmac_md5_xlat, NULL, inst);
1676 xlat_register("hmacsha1", hmac_sha1_xlat, NULL, inst);
1677 xlat_register("pairs", pairs_xlat, NULL, inst);
1679 xlat_register("base64", base64_xlat, NULL, inst);
1680 xlat_register("base64tohex", base64_to_hex_xlat, NULL, inst);
1682 xlat_register("explode", explode_xlat, NULL, inst);
1684 xlat_register("nexttime", next_time_xlat, NULL, inst);
1685 xlat_register("lpad", lpad_xlat, NULL, inst);
1686 xlat_register("rpad", rpad_xlat, NULL, inst);
1689 * Initialize various paircompare functions
1691 pair_builtincompare_add(instance);
1696 * The module name should be the only globally exported symbol.
1697 * That is, everything else should be 'static'.
1699 * If the module needs to temporarily modify it's instantiation
1700 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
1701 * The server will then take care of ensuring that the module
1702 * is single-threaded.
1704 extern module_t rlm_expr;
1705 module_t rlm_expr = {
1706 .magic = RLM_MODULE_INIT,
1708 .inst_size = sizeof(rlm_expr_t),
1709 .config = module_config,
1710 .bootstrap = mod_bootstrap,