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}
54 typedef enum expr_token_t {
68 typedef struct expr_map_t {
73 static expr_map_t map[] =
76 {'-', TOKEN_SUBTRACT },
78 {'*', TOKEN_MULTIPLY },
79 {'%', TOKEN_REMAINDER },
87 * Lookup tables for randstr char classes
89 static char randstr_punc[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
90 static char randstr_salt[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz/.";
93 * Characters humans rarely confuse. Reduces char set considerably
94 * should only be used for things such as one time passwords.
96 static char randstr_otp[] = "469ACGHJKLMNPQRUVWXYabdfhijkprstuvwxyz";
98 static char const hextab[] = "0123456789abcdef";
100 static int get_number(REQUEST *request, char const **string, int64_t *answer)
109 * Loop over the input.
114 for (p = *string; *p != '\0'; /* nothing */) {
122 * Discover which token it is.
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);
139 * Found the algebraic operator. Get the next number.
146 * End of a group. Stop.
149 if (this != TOKEN_NONE) {
150 RDEBUG2("Trailing operator before end sub-expression at \"%s\"", p);
158 * Start of a group. Call ourselves recursively.
163 found = get_number(request, &p, &x);
169 * No algrebraic operator found, the next thing
172 * If it isn't, then we die.
174 if ((*p == '0') && (p[1] == 'x')) {
177 x = strtoul(p, &end, 16);
183 if ((*p < '0') || (*p > '9')) {
184 RDEBUG2("Not a number at \"%s\"", p);
189 * This is doing it the hard way, but it also allows
190 * us to increment 'p'.
193 while ((*p >= '0') && (*p <= '9')) {
217 result = 0; /* we don't have NaN for integers */
223 case TOKEN_REMAINDER:
225 result = 0; /* we don't have NaN for integers */
244 if ((x > 255) || (x < 0)) {
245 REDEBUG("Exponent must be between 0-255");
248 x = fr_pow((int32_t) result, (uint8_t) x);
253 * We've used this token.
259 * And return the answer to the caller.
267 * Do xlat of strings!
269 static ssize_t expr_xlat(UNUSED void *instance, REQUEST *request, char const *fmt,
270 char *out, size_t outlen)
277 rcode = get_number(request, &p, &result);
283 * We MUST have eaten the entire input string.
286 RDEBUG2("Failed at %s", p);
290 snprintf(out, outlen, "%ld", (long int) result);
294 /** Generate a random integer value
297 static ssize_t rand_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt,
298 char *out, size_t outlen)
305 * Too small or too big.
311 if (result >= (1 << 30)) result = (1 << 30);
313 result *= fr_rand(); /* 0..2^32-1 */
316 snprintf(out, outlen, "%ld", (long int) result);
320 /** Generate a string of random chars
322 * Build strings of random chars, useful for generating tokens and passcodes
323 * Format similar to String::Random.
325 static ssize_t randstr_xlat(UNUSED void *instance, UNUSED REQUEST *request,
326 char const *fmt, char *out, size_t outlen)
330 size_t freespace = outlen;
332 if (outlen <= 1) return 0;
337 while (*p && (--freespace > 0)) {
344 *out++ = 'a' + (result % 26);
351 *out++ = 'A' + (result % 26);
358 *out++ = '0' + (result % 10);
365 *out++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
372 *out++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
376 * Alpa numeric + punctuation
379 *out++ = '!' + (result % 95);
383 * Alpha numeric + salt chars './'
386 *out++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
390 * Chars suitable for One Time Password tokens.
391 * Alpha numeric with easily confused char pairs removed.
394 *out++ = randstr_otp[result % (sizeof(randstr_otp) - 1)];
398 * Binary data as hexits (we don't really support
399 * non printable chars).
406 snprintf(out, 3, "%02x", result % 256);
408 /* Already decremented */
414 * Binary data with uppercase hexits
421 snprintf(out, 3, "%02X", result % 256);
423 /* Already decremented */
429 ERROR("rlm_expr: invalid character class '%c'", *p);
439 return outlen - freespace;
442 /** URLencode special characters
444 * Example: "%{urlquote:http://example.org/}" == "http%3A%47%47example.org%47"
446 static ssize_t urlquote_xlat(UNUSED void *instance, UNUSED REQUEST *request,
447 char const *fmt, char *out, size_t outlen)
450 size_t freespace = outlen;
452 if (outlen <= 1) return 0;
455 while (*p && (--freespace > 0)) {
472 /* MUST be upper case hex to be compliant */
473 snprintf(out, 4, "%%%02X", (uint8_t) *p++); /* %XX */
475 /* Already decremented */
483 return outlen - freespace;
486 /** URLdecode special characters
488 * Example: "%{urlunquote:http%3A%47%47example.org%47}" == "http://example.org/"
490 static ssize_t urlunquote_xlat(UNUSED void *instance, UNUSED REQUEST *request,
491 char const *fmt, char *out, size_t outlen)
495 size_t freespace = outlen;
497 if (outlen <= 1) return 0;
500 while (*p && (--freespace > 0)) {
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");
514 *out++ = ((c1 - hextab) << 4) + (c2 - hextab);
519 return outlen - freespace;
522 /** Equivalent to the old safe_characters functionality in rlm_sql
524 * @verbatim Example: "%{escape:<img>foo.jpg</img>}" == "=60img=62foo.jpg=60/img=62" @endverbatim
526 static ssize_t escape_xlat(UNUSED void *instance, UNUSED REQUEST *request,
527 char const *fmt, char *out, size_t outlen)
529 rlm_expr_t *inst = instance;
531 size_t freespace = outlen;
533 if (outlen <= 1) return 0;
536 while (*p && (--freespace > 0)) {
538 * Non-printable characters get replaced with their
539 * mime-encoded equivalents.
541 if ((*p > 31) && strchr(inst->allowed_chars, *p)) {
549 snprintf(out, 4, "=%02X", (uint8_t)*p++);
551 /* Already decremented */
558 return outlen - freespace;
561 /** Equivalent to the old safe_characters functionality in rlm_sql
563 * @verbatim Example: "%{escape:=60img=62foo.jpg=60/img=62}" == "<img>foo.jpg</img>" @endverbatim
565 static ssize_t unescape_xlat(UNUSED void *instance, UNUSED REQUEST *request,
566 char const *fmt, char *out, size_t outlen)
568 rlm_expr_t *inst = instance;
571 size_t freespace = outlen;
573 if (outlen <= 1) return 0;
576 while (*p && (--freespace > 0)) {
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);
591 * It was just random occurrence which just happens
592 * to match the escape sequence for a safe character.
593 * Copy it across verbatim.
595 if (strchr(inst->allowed_chars, c3)) goto next;
602 return outlen - freespace;
605 /** Convert a string to lowercase
607 * Example "%{tolower:Bar}" == "bar"
609 * Probably only works for ASCII
611 static ssize_t lc_xlat(UNUSED void *instance, UNUSED REQUEST *request,
612 char const *fmt, char *out, size_t outlen)
617 if (outlen <= 1) return 0;
619 for (p = fmt, q = out; *p != '\0'; p++, outlen--) {
620 if (outlen <= 1) break;
622 *(q++) = tolower((int) *p);
630 /** Convert a string to uppercase
632 * Example: "%{toupper:Foo}" == "FOO"
634 * Probably only works for ASCII
636 static ssize_t uc_xlat(UNUSED void *instance, UNUSED REQUEST *request,
637 char const *fmt, char *out, size_t outlen)
642 if (outlen <= 1) return 0;
644 for (p = fmt, q = out; *p != '\0'; p++, outlen--) {
645 if (outlen <= 1) break;
647 *(q++) = toupper((int) *p);
655 /** Calculate the MD5 hash of a string or attribute.
657 * Example: "%{md5:foo}" == "acbd18db4cc2f85cedef654fccc4a4d8"
659 static ssize_t md5_xlat(UNUSED void *instance, UNUSED REQUEST *request,
660 char const *fmt, char *out, size_t outlen)
663 ssize_t i, len, inlen;
668 * We need room for at least one octet of output.
675 inlen = xlat_fmt_to_ref(&p, request, fmt);
681 fr_md5_update(&ctx, p, inlen);
682 fr_md5_final(digest, &ctx);
685 * Each digest octet takes two hex digits, plus one for
686 * the terminating NUL.
688 len = (outlen / 2) - 1;
689 if (len > 16) len = 16;
691 for (i = 0; i < len; i++) {
692 snprintf(out + i * 2, 3, "%02x", digest[i]);
698 /** Calculate the SHA1 hash of a string or attribute.
700 * Example: "%{sha1:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
702 static ssize_t sha1_xlat(UNUSED void *instance, UNUSED REQUEST *request,
703 char const *fmt, char *out, size_t outlen)
706 ssize_t i, len, inlen;
711 * We need room for at least one octet of output.
718 inlen = xlat_fmt_to_ref(&p, request, fmt);
724 fr_sha1_update(&ctx, p, inlen);
725 fr_sha1_final(digest, &ctx);
728 * Each digest octet takes two hex digits, plus one for
729 * the terminating NUL. SHA1 is 160 bits (20 bytes)
731 len = (outlen / 2) - 1;
732 if (len > 20) len = 20;
734 for (i = 0; i < len; i++) {
735 snprintf(out + i * 2, 3, "%02x", digest[i]);
741 /** Calculate any digest supported by OpenSSL EVP_MD
743 * Example: "%{sha256:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
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)
749 uint8_t digest[EVP_MAX_MD_SIZE];
750 unsigned int digestlen, i, len;
757 * We need room for at least one octet of output.
764 inlen = xlat_fmt_to_ref(&p, request, fmt);
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);
776 * Each digest octet takes two hex digits, plus one for
777 * the terminating NUL.
779 len = (outlen / 2) - 1;
780 if (len > digestlen) len = digestlen;
782 for (i = 0; i < len; i++) {
783 snprintf(out + i * 2, 3, "%02x", digest[i]);
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)\
791 return evp_md_xlat(instance, request, fmt, out, outlen, EVP_##_md());\
798 /** Generate the HMAC-MD5 of a string or attribute
800 * Example: "%{hmacmd5:foo bar}" == "Zm9v"
802 static ssize_t hmac_md5_xlat(UNUSED void *instance, UNUSED REQUEST *request,
803 char const *fmt, char *out, size_t outlen)
805 uint8_t const *data, *key;
807 ssize_t data_len, key_len;
808 uint8_t digest[MD5_DIGEST_LENGTH];
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);
817 p = strchr(fmt, ' ');
819 REDEBUG("HMAC requires exactly two arguments (&data &key)");
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));
829 strlcpy(data_ref, fmt, (p - fmt) + 1);
831 data_len = xlat_fmt_to_ref(&data, request, data_ref);
832 if (data_len < 0) return -1;
834 while (isspace(*p) && p++);
836 key_len = xlat_fmt_to_ref(&key, request, p);
837 if (key_len < 0) return -1;
839 fr_hmac_md5(digest, data, data_len, key, key_len);
841 return fr_bin2hex(out, digest, sizeof(digest));
844 /** Generate the HMAC-SHA1 of a string or attribute
846 * Example: "%{hmacsha1:foo bar}" == "Zm9v"
848 static ssize_t hmac_sha1_xlat(UNUSED void *instance, UNUSED REQUEST *request,
849 char const *fmt, char *out, size_t outlen)
851 uint8_t const *data, *key;
853 ssize_t data_len, key_len;
854 uint8_t digest[SHA1_DIGEST_LENGTH];
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);
863 p = strchr(fmt, ' ');
865 REDEBUG("HMAC requires exactly two arguments (&data &key)");
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));
875 strlcpy(data_ref, fmt, (p - fmt) + 1);
877 data_len = xlat_fmt_to_ref(&data, request, data_ref);
878 if (data_len < 0) return -1;
880 while (isspace(*p) && p++);
882 key_len = xlat_fmt_to_ref(&key, request, p);
883 if (key_len < 0) return -1;
885 fr_hmac_sha1(digest, data, data_len, key, key_len);
887 return fr_bin2hex(out, digest, sizeof(digest));
890 /** Encode string or attribute as base64
892 * Example: "%{base64:foo}" == "Zm9v"
894 static ssize_t base64_xlat(UNUSED void *instance, UNUSED REQUEST *request,
895 char const *fmt, char *out, size_t outlen)
900 inlen = xlat_fmt_to_ref(&p, request, fmt);
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.
909 if ((inlen < 0) || ((FR_BASE64_ENC_LENGTH(inlen) + 1) > (ssize_t) outlen)) {
910 REDEBUG("xlat failed");
915 return fr_base64_encode(out, outlen, p, inlen);
918 /** Convert base64 to hex
920 * Example: "%{base64tohex:Zm9v}" == "666f6f"
922 static ssize_t base64_to_hex_xlat(UNUSED void *instance, UNUSED REQUEST *request,
923 char const *fmt, char *out, size_t outlen)
925 uint8_t decbuf[1024];
928 ssize_t len = strlen(fmt);
932 declen = fr_base64_decode(decbuf, sizeof(decbuf), fmt, len);
934 REDEBUG("Base64 string invalid");
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);
944 return fr_bin2hex(out, decbuf, declen);
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.
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.
958 static int mod_instantiate(CONF_SECTION *conf, void *instance)
960 rlm_expr_t *inst = instance;
962 inst->xlat_name = cf_section_name2(conf);
963 if (!inst->xlat_name) {
964 inst->xlat_name = cf_section_name1(conf);
967 xlat_register(inst->xlat_name, expr_xlat, NULL, inst);
970 * FIXME: unregister these, too
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);
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);
992 * Initialize various paircompare functions
994 pair_builtincompare_add(instance);
999 * The module name should be the only globally exported symbol.
1000 * That is, everything else should be 'static'.
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.
1007 module_t rlm_expr = {
1013 mod_instantiate, /* instantiation */
1016 NULL, /* authentication */
1017 NULL, /* authorization */
1018 NULL, /* pre-accounting */
1019 NULL /* accounting */