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.
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.
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
19 * @brief Register many xlat expansions including the expr expansion.
21 * @copyright 2001,2006 The FreeRADIUS server project
22 * @copyright 2002 Alan DeKok <aland@ox.org>
25 USES_APPLE_DEPRECATED_API
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>
33 #ifdef HAVE_OPENSSL_EVP_H
34 # include <openssl/evp.h>
42 * Define a structure for our module configuration.
44 typedef struct rlm_expr_t {
45 char const *xlat_name;
46 char const *allowed_chars;
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}
55 * Lookup tables for randstr char classes
57 static char randstr_punc[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
58 static char randstr_salt[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz/.";
61 * Characters humans rarely confuse. Reduces char set considerably
62 * should only be used for things such as one time passwords.
64 static char randstr_otp[] = "469ACGHJKLMNPQRUVWXYabdfhijkprstuvwxyz";
66 static char const hextab[] = "0123456789abcdef";
70 * @author Orson Peters
71 * @note Borrowed from the gist here: https://gist.github.com/nightcracker/3551590.
73 * @param base a 32bit signed integer.
74 * @param exp amount to raise base by.
75 * @return base ^ pow, or 0 on underflow/overflow.
77 static int64_t fr_pow(int64_t base, int64_t exp)
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
98 return 1 - 2 * (exp & 1);
100 return 0; /* overflow */
103 switch (highest_bit_set[exp]) {
105 if (exp & 1) result *= base;
109 if (exp & 1) result *= base;
113 if (exp & 1) result *= base;
117 if (exp & 1) result *= base;
121 if (exp & 1) result *= base;
125 if (exp & 1) result *= base;
132 * Start of expression calculator.
134 typedef enum expr_token_t {
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 */
162 typedef struct expr_map_t {
167 static expr_map_t map[] =
170 {'-', TOKEN_SUBTRACT },
171 {'/', TOKEN_DIVIDE },
172 {'*', TOKEN_MULTIPLY },
173 {'%', TOKEN_REMAINDER },
180 static bool get_expression(REQUEST *request, char const **string, int64_t *answer, expr_token_t prev);
182 static bool get_number(REQUEST *request, char const **string, int64_t *answer)
186 bool negative = false;
187 char const *p = *string;
192 while (isspace((int) *p)) p++;
203 * No algrebraic operator found, the next thing
206 * If it isn't, then we die.
208 if ((*p == '0') && (p[1] == 'x')) {
211 x = strtoul(p, &end, 16);
217 * Look for an attribute.
222 value_pair_tmpl_t vpt;
226 slen = tmpl_from_attr_substr(&vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false);
228 RDEBUG("Failed parsing attribute name '%s': %s", p, fr_strerror());
234 if (tmpl_find_vp(&vp, request, &vpt) < 0) {
235 RDEBUG("Can't find &%s", vpt.tmpl_da->name);
240 switch (vp->da->type) {
242 RDEBUG("WARNING: Non-integer attribute %s", vp->da->name);
246 case PW_TYPE_INTEGER64:
248 * FIXME: error out if the number is too large.
250 x = vp->vp_integer64;
253 case PW_TYPE_INTEGER:
274 * Do brackets recursively
278 if (!get_expression(request, &p, &x, TOKEN_NONE)) return false;
281 RDEBUG("No trailing ')'");
293 if ((*p < '0') || (*p > '9')) {
294 RDEBUG2("Not a number at \"%s\"", p);
299 * This is doing it the hard way, but it also allows
300 * us to increment 'p'.
303 while ((*p >= '0') && (*p <= '9')) {
309 if (negative) x = -x;
319 static bool calc_result(REQUEST *request, int64_t lhs, expr_token_t op, int64_t rhs, int64_t *answer)
333 RDEBUG("Division by zero!");
340 case TOKEN_REMAINDER:
342 RDEBUG("Division by zero!");
355 RDEBUG("Shift must be less than 63 (was %lld)", (long long int) rhs);
359 *answer = lhs << rhs;
364 RDEBUG("Shift must be less than 63 (was %lld)", (long long int) rhs);
368 *answer = lhs >> rhs;
381 REDEBUG("Exponent must be between 0-63 (was %lld)", (long long int) rhs);
386 REDEBUG("Base must be between 0-65535 (was %lld)", (long long int) lhs);
390 *answer = fr_pow(lhs, rhs);
397 static bool get_operator(REQUEST *request, char const **string, expr_token_t *op)
400 char const *p = *string;
403 * All tokens are one character.
405 for (i = 0; map[i].token != TOKEN_LAST; i++) {
406 if (*p == map[i].op) {
413 if ((p[0] == '<') && (p[1] == '<')) {
419 if ((p[0] == '>') && (p[1] == '>')) {
425 RDEBUG("Expected operator at \"%s\"", p);
430 static bool get_expression(REQUEST *request, char const **string, int64_t *answer, expr_token_t prev)
433 char const *p, *op_p;
438 if (!get_number(request, &p, &lhs)) return false;
441 while (isspace((int) *p)) p++;
444 * A number by itself is OK.
446 if (!*p || (*p == ')')) {
453 * Peek at the operator.
456 if (!get_operator(request, &p, &this)) return false;
459 * a + b + c ... = (a + b) + c ...
460 * a * b + c ... = (a * b) + c ...
462 * Feed the current number to the caller, who will take
463 * care of continuing.
465 if (precedence[this] <= precedence[prev]) {
472 * a + b * c ... = a + (b * c) ...
474 if (!get_expression(request, &p, &rhs, this)) return false;
476 if (!calc_result(request, lhs, this, rhs, answer)) return false;
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.
483 * a * b + c ... = (a * b) + c ...
491 * Do xlat of strings!
493 static ssize_t expr_xlat(UNUSED void *instance, REQUEST *request, char const *fmt,
494 char *out, size_t outlen)
501 if (!get_expression(request, &p, &result, TOKEN_NONE)) {
506 RDEBUG("Invalid text after expression: %s", p);
510 snprintf(out, outlen, "%lld", (long long int) result);
514 /** Generate a random integer value
517 static ssize_t rand_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt,
518 char *out, size_t outlen)
525 * Too small or too big.
531 if (result >= (1 << 30)) result = (1 << 30);
533 result *= fr_rand(); /* 0..2^32-1 */
536 snprintf(out, outlen, "%ld", (long int) result);
540 /** Generate a string of random chars
542 * Build strings of random chars, useful for generating tokens and passcodes
543 * Format similar to String::Random.
545 static ssize_t randstr_xlat(UNUSED void *instance, UNUSED REQUEST *request,
546 char const *fmt, char *out, size_t outlen)
550 size_t freespace = outlen;
552 if (outlen <= 1) return 0;
557 while (*p && (--freespace > 0)) {
564 *out++ = 'a' + (result % 26);
571 *out++ = 'A' + (result % 26);
578 *out++ = '0' + (result % 10);
585 *out++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
592 *out++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
596 * Alpa numeric + punctuation
599 *out++ = '!' + (result % 95);
603 * Alpha numeric + salt chars './'
606 *out++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
610 * Chars suitable for One Time Password tokens.
611 * Alpha numeric with easily confused char pairs removed.
614 *out++ = randstr_otp[result % (sizeof(randstr_otp) - 1)];
618 * Binary data as hexits (we don't really support
619 * non printable chars).
626 snprintf(out, 3, "%02x", result % 256);
628 /* Already decremented */
634 * Binary data with uppercase hexits
641 snprintf(out, 3, "%02X", result % 256);
643 /* Already decremented */
649 ERROR("rlm_expr: invalid character class '%c'", *p);
659 return outlen - freespace;
662 /** URLencode special characters
664 * Example: "%{urlquote:http://example.org/}" == "http%3A%47%47example.org%47"
666 static ssize_t urlquote_xlat(UNUSED void *instance, UNUSED REQUEST *request,
667 char const *fmt, char *out, size_t outlen)
670 size_t freespace = outlen;
672 if (outlen <= 1) return 0;
675 while (*p && (--freespace > 0)) {
693 /* MUST be upper case hex to be compliant */
694 snprintf(out, 4, "%%%02X", (uint8_t) *p++); /* %XX */
696 /* Already decremented */
704 return outlen - freespace;
707 /** URLdecode special characters
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
712 static ssize_t urlunquote_xlat(UNUSED void *instance, REQUEST *request,
713 char const *fmt, char *out, size_t outlen)
717 size_t freespace = outlen;
719 if (outlen <= 1) return 0;
722 while (*p && (--freespace > 0)) {
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");
736 *out++ = ((c1 - hextab) << 4) + (c2 - hextab);
741 return outlen - freespace;
744 /** Equivalent to the old safe_characters functionality in rlm_sql but with utf8 support
746 * @verbatim Example: "%{escape:<img>foo.jpg</img>}" == "=60img=62foo.jpg=60/img=62" @endverbatim
748 static ssize_t escape_xlat(void *instance, UNUSED REQUEST *request,
749 char const *fmt, char *out, size_t outlen)
751 rlm_expr_t *inst = instance;
753 size_t freespace = outlen;
757 int ret = 1; /* -Werror=uninitialized */
759 if (fr_utf8_strchr(&chr_len, inst->allowed_chars, p) == NULL) {
761 * '=' 1 + ([hex]{2}) * chr_len)
763 if (freespace <= (size_t)(1 + (chr_len * 3))) break;
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]);
772 ret = snprintf(out, freespace, "=%02X=%02X=%02X",
773 (uint8_t)p[0], (uint8_t)p[1], (uint8_t)p[2]);
777 ret = snprintf(out, freespace, "=%02X=%02X", (uint8_t)p[0], (uint8_t)p[1]);
781 ret = snprintf(out, freespace, "=%02X", (uint8_t)p[0]);
792 * Only one byte left.
794 if (freespace <= 1) break;
797 * Allowed character (copy whole mb chars at once)
799 memcpy(out, p, chr_len);
802 freespace -= chr_len;
806 return outlen - freespace;
809 /** Equivalent to the old safe_characters functionality in rlm_sql
811 * @verbatim Example: "%{unescape:=60img=62foo.jpg=60/img=62}" == "<img>foo.jpg</img>" @endverbatim
813 static ssize_t unescape_xlat(UNUSED void *instance, UNUSED REQUEST *request,
814 char const *fmt, char *out, size_t outlen)
818 size_t freespace = outlen;
820 if (outlen <= 1) return 0;
823 while (*p && (--freespace > 0)) {
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);
843 return outlen - freespace;
846 /** Convert a string to lowercase
848 * Example: "%{tolower:Bar}" == "bar"
850 * Probably only works for ASCII
852 static ssize_t lc_xlat(UNUSED void *instance, UNUSED REQUEST *request,
853 char const *fmt, char *out, size_t outlen)
858 if (outlen <= 1) return 0;
860 for (p = fmt, q = out; *p != '\0'; p++, outlen--) {
861 if (outlen <= 1) break;
863 *(q++) = tolower((int) *p);
871 /** Convert a string to uppercase
873 * Example: "%{toupper:Foo}" == "FOO"
875 * Probably only works for ASCII
877 static ssize_t uc_xlat(UNUSED void *instance, UNUSED REQUEST *request,
878 char const *fmt, char *out, size_t outlen)
883 if (outlen <= 1) return 0;
885 for (p = fmt, q = out; *p != '\0'; p++, outlen--) {
886 if (outlen <= 1) break;
888 *(q++) = toupper((int) *p);
896 /** Calculate the MD5 hash of a string or attribute.
898 * Example: "%{md5:foo}" == "acbd18db4cc2f85cedef654fccc4a4d8"
900 static ssize_t md5_xlat(UNUSED void *instance, REQUEST *request,
901 char const *fmt, char *out, size_t outlen)
904 ssize_t i, len, inlen;
909 * We need room for at least one octet of output.
916 inlen = xlat_fmt_to_ref(&p, request, fmt);
922 fr_md5_update(&ctx, p, inlen);
923 fr_md5_final(digest, &ctx);
926 * Each digest octet takes two hex digits, plus one for
927 * the terminating NUL.
929 len = (outlen / 2) - 1;
930 if (len > 16) len = 16;
932 for (i = 0; i < len; i++) {
933 snprintf(out + i * 2, 3, "%02x", digest[i]);
939 /** Calculate the SHA1 hash of a string or attribute.
941 * Example: "%{sha1:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
943 static ssize_t sha1_xlat(UNUSED void *instance, REQUEST *request,
944 char const *fmt, char *out, size_t outlen)
947 ssize_t i, len, inlen;
952 * We need room for at least one octet of output.
959 inlen = xlat_fmt_to_ref(&p, request, fmt);
965 fr_sha1_update(&ctx, p, inlen);
966 fr_sha1_final(digest, &ctx);
969 * Each digest octet takes two hex digits, plus one for
970 * the terminating NUL. SHA1 is 160 bits (20 bytes)
972 len = (outlen / 2) - 1;
973 if (len > 20) len = 20;
975 for (i = 0; i < len; i++) {
976 snprintf(out + i * 2, 3, "%02x", digest[i]);
982 /** Calculate any digest supported by OpenSSL EVP_MD
984 * Example: "%{sha256:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
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)
990 uint8_t digest[EVP_MAX_MD_SIZE];
991 unsigned int digestlen, i, len;
998 * We need room for at least one octet of output.
1005 inlen = xlat_fmt_to_ref(&p, request, fmt);
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);
1017 * Each digest octet takes two hex digits, plus one for
1018 * the terminating NUL.
1020 len = (outlen / 2) - 1;
1021 if (len > digestlen) len = digestlen;
1023 for (i = 0; i < len; i++) {
1024 snprintf(out + i * 2, 3, "%02x", digest[i]);
1029 # define EVP_MD_XLAT(_md) \
1030 static ssize_t _md##_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen)\
1032 return evp_md_xlat(instance, request, fmt, out, outlen, EVP_##_md());\
1039 /** Generate the HMAC-MD5 of a string or attribute
1041 * Example: "%{hmacmd5:foo bar}" == "Zm9v"
1043 static ssize_t hmac_md5_xlat(UNUSED void *instance, REQUEST *request,
1044 char const *fmt, char *out, size_t outlen)
1046 uint8_t const *data, *key;
1048 ssize_t data_len, key_len;
1049 uint8_t digest[MD5_DIGEST_LENGTH];
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);
1058 p = strchr(fmt, ' ');
1060 REDEBUG("HMAC requires exactly two arguments (&data &key)");
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));
1070 strlcpy(data_ref, fmt, (p - fmt) + 1);
1072 data_len = xlat_fmt_to_ref(&data, request, data_ref);
1073 if (data_len < 0) return -1;
1075 while (isspace(*p) && p++);
1077 key_len = xlat_fmt_to_ref(&key, request, p);
1078 if (key_len < 0) return -1;
1080 fr_hmac_md5(digest, data, data_len, key, key_len);
1082 return fr_bin2hex(out, digest, sizeof(digest));
1085 /** Generate the HMAC-SHA1 of a string or attribute
1087 * Example: "%{hmacsha1:foo bar}" == "Zm9v"
1089 static ssize_t hmac_sha1_xlat(UNUSED void *instance, REQUEST *request,
1090 char const *fmt, char *out, size_t outlen)
1092 uint8_t const *data, *key;
1094 ssize_t data_len, key_len;
1095 uint8_t digest[SHA1_DIGEST_LENGTH];
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);
1104 p = strchr(fmt, ' ');
1106 REDEBUG("HMAC requires exactly two arguments (&data &key)");
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));
1116 strlcpy(data_ref, fmt, (p - fmt) + 1);
1118 data_len = xlat_fmt_to_ref(&data, request, data_ref);
1119 if (data_len < 0) return -1;
1121 while (isspace(*p) && p++);
1123 key_len = xlat_fmt_to_ref(&key, request, p);
1124 if (key_len < 0) return -1;
1126 fr_hmac_sha1(digest, data, data_len, key, key_len);
1128 return fr_bin2hex(out, digest, sizeof(digest));
1131 /** Encode attributes as a series of string attribute/value pairs
1133 * This is intended to serialize one or more attributes as a comma
1136 * Example: "%{pairs:request:}" == "User-Name = 'foo', User-Password = 'bar'"
1138 static ssize_t pairs_xlat(UNUSED void *instance, REQUEST *request,
1139 char const *fmt, char *out, size_t outlen)
1141 value_pair_tmpl_t vpt;
1143 size_t len, freespace = outlen;
1148 if (tmpl_from_attr_str(&vpt, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST, false) <= 0) {
1149 REDEBUG("%s", fr_strerror());
1153 for (vp = tmpl_cursor_init(NULL, &cursor, request, &vpt);
1155 vp = tmpl_cursor_next(&cursor, &vpt)) {
1156 FR_TOKEN op = vp->op;
1159 len = vp_prints(p, freespace, vp);
1162 if (is_truncated(len, freespace)) {
1164 REDEBUG("Insufficient space to store pair string, needed %zu bytes have %zu bytes",
1165 (p - out) + len, outlen);
1172 if (freespace < 2) {
1182 /* Trim the trailing ', ' */
1183 if (p != out) p -= 2;
1189 /** Encode string or attribute as base64
1191 * Example: "%{base64:foo}" == "Zm9v"
1193 static ssize_t base64_xlat(UNUSED void *instance, REQUEST *request,
1194 char const *fmt, char *out, size_t outlen)
1199 inlen = xlat_fmt_to_ref(&p, request, fmt);
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.
1208 if ((inlen < 0) || ((FR_BASE64_ENC_LENGTH(inlen) + 1) > (ssize_t) outlen)) {
1209 REDEBUG("xlat failed");
1214 return fr_base64_encode(out, outlen, p, inlen);
1217 /** Convert base64 to hex
1219 * Example: "%{base64tohex:Zm9v}" == "666f6f"
1221 static ssize_t base64_to_hex_xlat(UNUSED void *instance, REQUEST *request,
1222 char const *fmt, char *out, size_t outlen)
1224 uint8_t decbuf[1024];
1227 ssize_t len = strlen(fmt);
1231 declen = fr_base64_decode(decbuf, sizeof(decbuf), fmt, len);
1233 REDEBUG("Base64 string invalid");
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);
1243 return fr_bin2hex(out, decbuf, declen);
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.
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.
1257 static int mod_instantiate(CONF_SECTION *conf, void *instance)
1259 rlm_expr_t *inst = instance;
1261 inst->xlat_name = cf_section_name2(conf);
1262 if (!inst->xlat_name) {
1263 inst->xlat_name = cf_section_name1(conf);
1266 xlat_register(inst->xlat_name, expr_xlat, NULL, inst);
1269 * FIXME: unregister these, too
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);
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);
1289 xlat_register("base64", base64_xlat, NULL, inst);
1290 xlat_register("base64tohex", base64_to_hex_xlat, NULL, inst);
1293 * Initialize various paircompare functions
1295 pair_builtincompare_add(instance);
1300 * The module name should be the only globally exported symbol.
1301 * That is, everything else should be 'static'.
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.
1308 extern module_t rlm_expr;
1309 module_t rlm_expr = {
1315 mod_instantiate, /* instantiation */
1318 NULL, /* authentication */
1319 NULL, /* authorization */
1320 NULL, /* pre-accounting */
1321 NULL /* accounting */