]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Move xlat functions out of rlm_expr and back into xlat_func
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 16 May 2018 03:47:23 +0000 (09:47 +0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 16 May 2018 03:47:23 +0000 (09:47 +0600)
We probably need an rlm_escape to deal with the escape expansions

src/main/xlat_func.c
src/modules/rlm_expr/rlm_expr.c

index 73d41f2b29035db5cfa6f87d6140e4f917828d1e..a2bf2aa2062a0d65e92dd5a84cb8f03cb3794826 100644 (file)
@@ -30,6 +30,11 @@ RCSID("$Id$")
 #include <freeradius-devel/parser.h>
 #include <freeradius-devel/rad_assert.h>
 #include <freeradius-devel/base64.h>
+#include <freeradius-devel/md5.h>
+#include <freeradius-devel/sha1.h>
+#ifdef HAVE_OPENSSL_EVP_H
+#  include <openssl/evp.h>
+#endif
 
 #include <ctype.h>
 #include "xlat_priv.h"
@@ -50,6 +55,20 @@ static char const * const xlat_foreach_names[] = {"Foreach-Variable-0",
                                                  NULL};
 #endif
 
+/*
+ *     Lookup tables for randstr char classes
+ */
+static char randstr_punc[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
+static char randstr_salt[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz/.";
+
+/*
+ *     Characters humans rarely confuse. Reduces char set considerably
+ *     should only be used for things such as one time passwords.
+ */
+static char randstr_otp[] = "469ACGHJKLMNPQRUVWXYabdfhijkprstuvwxyz";
+
+static char const hextab[] = "0123456789abcdef";
+
 static int xlat_foreach_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };     /* up to 10 for foreach */
 
 /** Print length of its RHS.
@@ -288,280 +307,1419 @@ static ssize_t xlat_debug_attr(UNUSED TALLOC_CTX *ctx, UNUSED char **out, UNUSED
                }
                talloc_free(value);
 
-               if (!RDEBUG_ENABLED3) continue;
+               if (!RDEBUG_ENABLED3) continue;
+
+               vendor = fr_dict_vendor_by_da(vp->da);
+               if (vendor) RIDEBUG2("Vendor : %i (%s)", vendor->pen, vendor->name);
+               RIDEBUG2("Type   : %s", fr_int2str(dict_attr_types, vp->vp_type, "<INVALID>"));
+
+               switch (vp->vp_type) {
+               case FR_TYPE_VARIABLE_SIZE:
+                       RIDEBUG2("Length : %zu", vp->vp_length);
+                       break;
+
+               default:
+                       break;
+               }
+
+               if (!RDEBUG_ENABLED4) continue;
+
+               type = dict_attr_types;
+               while (type->name) {
+                       int pad;
+
+                       fr_value_box_t *dst = NULL;
+
+                       if ((fr_type_t) type->number == vp->vp_type) goto next_type;
+
+                       switch (type->number) {
+                       case FR_TYPE_INVALID:           /* Not real type */
+                       case FR_TYPE_MAX:               /* Not real type */
+                       case FR_TYPE_COMBO_IP_ADDR:     /* Covered by IPv4 address IPv6 address */
+                       case FR_TYPE_COMBO_IP_PREFIX:   /* Covered by IPv4 address IPv6 address */
+                       case FR_TYPE_TIMEVAL:           /* Not a VALUE_PAIR type */
+                       case FR_TYPE_STRUCTURAL:
+                               goto next_type;
+
+                       default:
+                               break;
+                       }
+
+                       dst = talloc_zero(vp, fr_value_box_t);
+                       /* We expect some to fail */
+                       if (fr_value_box_cast(dst, dst, type->number, NULL, &vp->data) < 0) {
+                               goto next_type;
+                       }
+
+                       value = fr_value_box_asprint(dst, dst, '\'');
+                       if (!value) goto next_type;
+
+                       if ((pad = (11 - strlen(type->name))) < 0) {
+                               pad = 0;
+                       }
+
+                       RINDENT();
+                       RDEBUG2("as %s%*s: %s", type->name, pad, " ", value);
+                       REXDENT();
+
+               next_type:
+                       talloc_free(dst);
+                       type++;
+               }
+       }
+       REXDENT();
+
+       talloc_free(vpt);
+
+       return 0;
+}
+
+/** Processes fmt as a map string and applies it to the current request
+ *
+ * e.g. "%{map:&User-Name := 'foo'}"
+ *
+ * Allows sets of modifications to be cached and then applied.
+ * Useful for processing generic attributes from LDAP.
+ */
+static ssize_t xlat_map(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                       UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                       REQUEST *request, char const *fmt)
+{
+       vp_map_t *map = NULL;
+       int ret;
+
+       if (map_afrom_attr_str(request, &map, fmt,
+                              REQUEST_CURRENT, PAIR_LIST_REQUEST,
+                              REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+               RPEDEBUG("Failed parsing \"%s\" as map", fmt);
+               return -1;
+       }
+
+       RINDENT();
+       ret = map_to_request(request, map, map_to_vp, NULL);
+       REXDENT();
+       talloc_free(map);
+       if (ret < 0) return strlcpy(*out, "0", outlen);
+
+       return strlcpy(*out, "1", outlen);
+}
+
+/** Prints the current module processing the request
+ *
+ */
+static ssize_t xlat_module(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                          UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                          REQUEST *request, UNUSED char const *fmt)
+{
+       strlcpy(*out, request->module, outlen);
+
+       return strlen(*out);
+}
+
+#if defined(HAVE_REGEX) && defined(HAVE_PCRE)
+static ssize_t xlat_regex(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                         UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                         REQUEST *request, char const *fmt)
+{
+       char *p;
+       size_t len;
+
+       if (regex_request_to_sub_named(request, &p, request, fmt) < 0) return 0;
+
+       len = talloc_array_length(p);
+       if (len > outlen) {
+               RDEBUG("Insufficient buffer space to write subcapture value, needed %zu bytes, have %zu bytes",
+                      len, outlen);
+               return -1;
+       }
+       strlcpy(*out, p, outlen);
+
+       return len - 1; /* - \0 */
+}
+#endif
+
+#ifdef WITH_UNLANG
+/** Implements the Foreach-Variable-X
+ *
+ * @see modcall()
+ */
+static ssize_t xlat_foreach(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
+                           void const *mod_inst, UNUSED void const *xlat_inst,
+                           REQUEST *request, UNUSED char const *fmt)
+{
+       VALUE_PAIR      **pvp;
+
+       /*
+        *      See modcall, "FOREACH" for how this works.
+        */
+       pvp = (VALUE_PAIR **) request_data_reference(request, (void *)radius_get_vp, *(int const *) mod_inst);
+       if (!pvp || !*pvp) return 0;
+
+       *out = fr_pair_value_asprint(ctx, *pvp, '\0');
+       return  talloc_array_length(*out) - 1;
+}
+#endif
+
+/** Print data as string, if possible.
+ *
+ * If attribute "Foo" is defined as "octets" it will normally
+ * be printed as 0x0a0a0a. The xlat "%{string:Foo}" will instead
+ * expand to "\n\n\n"
+ */
+static ssize_t xlat_string(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                          UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                          REQUEST *request, char const *fmt)
+{
+       ssize_t         ret;
+       VALUE_PAIR      *vp;
+       uint8_t         buffer[64];
+
+       while (isspace((int) *fmt)) fmt++;
+
+       if (outlen < 3) {
+       nothing:
+               return 0;
+       }
+
+       if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
+
+       /*
+        *      These are printed specially.
+        */
+       switch (vp->vp_type) {
+       case FR_TYPE_OCTETS:
+               return fr_snprint(*out, outlen, (char const *) vp->vp_octets, vp->vp_length, '"');
+
+               /*
+                *      Note that "%{string:...}" is NOT binary safe!
+                *      It is explicitly used to get rid of embedded zeros.
+                */
+       case FR_TYPE_STRING:
+               return strlcpy(*out, vp->vp_strvalue, outlen);
+
+       default:
+               break;
+       }
+
+       ret = fr_value_box_to_network(NULL, buffer, sizeof(buffer), &vp->data);
+       if (ret < 0) return ret;
+
+       return fr_snprint(*out, outlen, (char const *) buffer, ret, '\0');
+}
+
+/** xlat expand string attribute value
+ *
+ */
+static ssize_t xlat_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,
+                        UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                        REQUEST *request, char const *fmt)
+{
+       ssize_t slen;
+       VALUE_PAIR *vp;
+
+       while (isspace((int) *fmt)) fmt++;
+
+       if (outlen < 3) {
+       nothing:
+               return 0;
+       }
+
+       if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
+
+       RDEBUG2("EXPAND %s", fmt);
+       RINDENT();
+
+       /*
+        *      If it's a string, expand it again
+        */
+       if (vp->vp_type == FR_TYPE_STRING) {
+               slen = xlat_eval(*out, outlen, request, vp->vp_strvalue, NULL, NULL);
+               if (slen <= 0) return slen;
+       /*
+        *      If it's not a string, treat it as a literal
+        */
+       } else {
+               *out = fr_pair_value_asprint(ctx, vp, '\0');
+               if (!*out) return -1;
+               slen = talloc_array_length(*out) - 1;
+       }
+
+       REXDENT();
+       RDEBUG2("--> %s", *out);
+
+       return slen;
+}
+
+/** Dynamically change the debugging level for the current request
+ *
+ * Example %{debug:3}
+ */
+static ssize_t xlat_debug(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                         UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                         REQUEST *request, char const *fmt)
+{
+       int level = 0;
+
+       /*
+        *  Expand to previous (or current) level
+        */
+       snprintf(*out, outlen, "%d", request->log.lvl);
+
+       /*
+        *  Assume we just want to get the current value and NOT set it to 0
+        */
+       if (!*fmt)
+               goto done;
+
+       level = atoi(fmt);
+       if (level == 0) {
+               request->log.lvl = RAD_REQUEST_LVL_NONE;
+       } else {
+               if (level > 4) level = 4;
+               request->log.lvl = level;
+       }
+
+done:
+       return strlen(*out);
+}
+
+/** Generate a random integer value
+ *
+ */
+static ssize_t rand_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                        UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                        UNUSED REQUEST *request, char const *fmt)
+{
+       int64_t         result;
+
+       result = atoi(fmt);
+
+       /*
+        *      Too small or too big.
+        */
+       if (result <= 0) return -1;
+       if (result >= (1 << 30)) result = (1 << 30);
+
+       result *= fr_rand();    /* 0..2^32-1 */
+       result >>= 32;
+
+       snprintf(*out, outlen, "%ld", (long int) result);
+       return strlen(*out);
+}
+
+/** Generate a string of random chars
+ *
+ *  Build strings of random chars, useful for generating tokens and passcodes
+ *  Format similar to String::Random.
+ */
+static ssize_t randstr_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                           UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                           REQUEST *request, char const *fmt)
+{
+       char const      *p;
+       char            *out_p = *out;
+       unsigned int    result;
+       unsigned int    number;
+       size_t          freespace = outlen;
+
+       if (outlen <= 1) return 0;
+
+       p = fmt;
+       while (*p && (--freespace > 0)) {
+               number = 0;
+
+               /*
+                *      Modifiers are polite.
+                *
+                *      But we limit it to 100, because we don't want
+                *      utter stupidity.
+                */
+               while (isdigit((int) *p)) {
+                       if (number >= 100) {
+                               p++;
+                               continue;
+                       }
+
+                       number *= 10;
+                       number += *p - '0';
+                       p++;
+               }
+
+       redo:
+               result = fr_rand();
+
+               switch (*p) {
+               /*
+                *  Lowercase letters
+                */
+               case 'c':
+                       *out_p++ = 'a' + (result % 26);
+                       break;
+
+               /*
+                *  Uppercase letters
+                */
+               case 'C':
+                       *out_p++ = 'A' + (result % 26);
+                       break;
+
+               /*
+                *  Numbers
+                */
+               case 'n':
+                       *out_p++ = '0' + (result % 10);
+                       break;
+
+               /*
+                *  Alpha numeric
+                */
+               case 'a':
+                       *out_p++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
+                       break;
+
+               /*
+                *  Punctuation
+                */
+               case '!':
+                       *out_p++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
+                       break;
+
+               /*
+                *  Alpa numeric + punctuation
+                */
+               case '.':
+                       *out_p++ = '!' + (result % 95);
+                       break;
+
+               /*
+                *  Alpha numeric + salt chars './'
+                */
+               case 's':
+                       *out_p++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
+                       break;
+
+               /*
+                *  Chars suitable for One Time Password tokens.
+                *  Alpha numeric with easily confused char pairs removed.
+                */
+               case 'o':
+                       *out_p++ = randstr_otp[result % (sizeof(randstr_otp) - 1)];
+                       break;
+
+               /*
+                *  Binary data as hexits (we don't really support
+                *  non printable chars).
+                */
+               case 'h':
+                       if (freespace < 2) {
+                               break;
+                       }
+
+                       snprintf(out_p, 3, "%02x", result % 256);
+
+                       /* Already decremented */
+                       freespace -= 1;
+                       out_p += 2;
+                       break;
+
+               /*
+                *  Binary data with uppercase hexits
+                */
+               case 'H':
+                       if (freespace < 2) {
+                               break;
+                       }
+
+                       snprintf(out_p, 3, "%02X", result % 256);
+
+                       /* Already decremented */
+                       freespace -= 1;
+                       out_p += 2;
+                       break;
+
+               default:
+                       REDEBUG("Invalid character class '%c'", *p);
+
+                       return -1;
+               }
+
+               if (number > 0) {
+                       number--;
+                       goto redo;
+               }
+
+               p++;
+       }
+
+       *out_p++ = '\0';
+
+       return outlen - freespace;
+}
+
+/** URLencode special characters
+ *
+ * Example: "%{urlquote:http://example.org/}" == "http%3A%47%47example.org%47"
+ */
+static ssize_t urlquote_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                            UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                            UNUSED REQUEST *request, char const *fmt)
+{
+       char const      *p;
+       char            *out_p = *out;
+       size_t  freespace = outlen;
+
+       if (outlen <= 1) return 0;
+
+       p = fmt;
+       while (*p && (--freespace > 0)) {
+               if (isalnum(*p)) {
+                       *out_p++ = *p++;
+                       continue;
+               }
+
+               switch (*p) {
+               case '-':
+               case '_':
+               case '.':
+               case '~':
+                       *out_p++ = *p++;
+                       break;
+
+               default:
+                       if (freespace < 3)
+                               break;
+
+                       /* MUST be upper case hex to be compliant */
+                       snprintf(out_p, 4, "%%%02X", (uint8_t) *p++); /* %XX */
+
+                       /* Already decremented */
+                       freespace -= 2;
+                       out_p += 3;
+               }
+       }
+
+       *out_p = '\0';
+
+       return outlen - freespace;
+}
+
+/** URLdecode special characters
+ *
+ * Example: "%{urlunquote:http%%3A%%47%%47example.org%%47}" == "http://example.org/"
+ *
+ * Remember to escape % with %% in strings, else xlat will try to parse it.
+ */
+static ssize_t urlunquote_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                              UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                              REQUEST *request, char const *fmt)
+{
+       char const *p;
+       char *out_p = *out;
+       char *c1, *c2;
+       size_t  freespace = outlen;
+
+       if (outlen <= 1) return 0;
+
+       p = fmt;
+       while (*p && (--freespace > 0)) {
+               if (*p != '%') {
+                       *out_p++ = *p++;
+                       continue;
+               }
+               /* Is a % char */
+
+               /* Don't need \0 check, as it won't be in the hextab */
+               if (!(c1 = memchr(hextab, tolower(*++p), 16)) ||
+                   !(c2 = memchr(hextab, tolower(*++p), 16))) {
+                       REMARKER(fmt, p - fmt, "Non-hex char in % sequence");
+                       return -1;
+               }
+               p++;
+               *out_p++ = ((c1 - hextab) << 4) + (c2 - hextab);
+       }
+
+       *out_p = '\0';
+
+       return outlen - freespace;
+}
+
+
+/** Convert a string to lowercase
+ *
+ * Example: "%{tolower:Bar}" == "bar"
+ *
+ * Probably only works for ASCII
+ */
+static ssize_t tolower_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                           UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                           UNUSED REQUEST *request, char const *fmt)
+{
+       char *q;
+       char const *p;
+
+       if (outlen <= 1) return 0;
+
+       for (p = fmt, q = *out; *p != '\0'; p++, outlen--) {
+               if (outlen <= 1) break;
+
+               *(q++) = tolower((int) *p);
+       }
+
+       *q = '\0';
+
+       return strlen(*out);
+}
+
+/** Convert a string to uppercase
+ *
+ * Example: "%{toupper:Foo}" == "FOO"
+ *
+ * Probably only works for ASCII
+ */
+static ssize_t toupper_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                           UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                           UNUSED REQUEST *request, char const *fmt)
+{
+       char *q;
+       char const *p;
+
+       if (outlen <= 1) return 0;
+
+       for (p = fmt, q = *out; *p != '\0'; p++, outlen--) {
+               if (outlen <= 1) break;
+
+               *(q++) = toupper((int) *p);
+       }
+
+       *q = '\0';
+
+       return strlen(*out);
+}
+
+/** Decodes data or &Attr-Name to data
+ *
+ * This needs to die, and hopefully will die, when xlat functions accept
+ * xlat node structures.
+ *
+ * @param ctx          Talloc ctx for temporary allocations.
+ * @param out          fr_value_box_t containing a shallow copy of the attribute,
+ *                     or the fmt string.
+ * @param request      current request.
+ * @param fmt          string.
+ * @returns
+ *     - The length of the data.
+ *     - -1 on failure.
+ */
+static int fr_value_box_from_fmt(TALLOC_CTX *ctx, fr_value_box_t *out, REQUEST *request, char const *fmt)
+{
+       VALUE_PAIR *vp;
+
+       while (isspace((int) *fmt)) fmt++;
+
+       /*
+        *      Not an attribute reference?  Just use the input format.
+        */
+       if (*fmt != '&') {
+               memset(out, 0, sizeof(*out));
+               out->vb_strvalue = fmt;
+               out->datum.length = talloc_array_length(fmt) - 1;
+               out->type = FR_TYPE_STRING;
+               return 0;
+       }
+
+       /*
+        *      If it's an attribute reference, get the underlying
+        *      attribute, and then store the data in network byte
+        *      order.
+        */
+       if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) return -1;
+
+       fr_value_box_copy(ctx, out, &vp->data);
+
+       return 0;
+}
+
+static int fr_value_box_to_bin(TALLOC_CTX *ctx, REQUEST *request, uint8_t **out, size_t *outlen, fr_value_box_t const *in)
+{
+       fr_value_box_t bin;
+
+       switch (in->type) {
+       case FR_TYPE_STRING:
+       case FR_TYPE_OCTETS:
+               memcpy(out, &in->datum.ptr, sizeof(in));
+               *outlen = in->datum.length;
+               return 0;
+
+       default:
+               if (fr_value_box_cast(ctx, &bin, FR_TYPE_OCTETS, NULL, in) < 0) {
+                       RPERROR("Failed casting xlat input to 'octets'");
+                       return -1;
+               }
+               memcpy(out, &bin.datum.ptr, sizeof(in));
+               *outlen = bin.datum.length;
+               return 0;
+       }
+}
+
+#define VALUE_FROM_FMT(_tmp_ctx, _p, _len, _request, _fmt) \
+       fr_value_box_t _value; \
+       if (!_tmp_ctx) MEM(_tmp_ctx = talloc_new(_request)); \
+       if (fr_value_box_from_fmt(_tmp_ctx, &_value, _request, _fmt) < 0) { \
+               talloc_free(_tmp_ctx); \
+               return -1; \
+       } \
+       if (fr_value_box_to_bin(_tmp_ctx, _request, &_p, &_len, &_value) < 0) { \
+               talloc_free(_tmp_ctx); \
+               return -1; \
+       }
+
+
+/** Calculate the MD5 hash of a string or attribute.
+ *
+ * Example: "%{md5:foo}" == "acbd18db4cc2f85cedef654fccc4a4d8"
+ */
+static ssize_t md5_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                       UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                       REQUEST *request, char const *fmt)
+{
+       uint8_t         digest[16];
+       size_t          i, len, inlen;
+       uint8_t         *p;
+       FR_MD5_CTX      md5_ctx;
+       TALLOC_CTX      *tmp_ctx = NULL;
+
+       VALUE_FROM_FMT(tmp_ctx, p, inlen, request, fmt);
+
+       fr_md5_init(&md5_ctx);
+       fr_md5_update(&md5_ctx, p, inlen);
+       fr_md5_final(digest, &md5_ctx);
+
+       /*
+        *      Each digest octet takes two hex digits, plus one for
+        *      the terminating NUL.
+        */
+       len = (outlen / 2) - 1;
+       if (len > 16) len = 16;
+
+       for (i = 0; i < len; i++) snprintf((*out) + (i * 2), 3, "%02x", digest[i]);
+
+       talloc_free(tmp_ctx);
+
+       return strlen(*out);
+}
+
+/** Calculate the SHA1 hash of a string or attribute.
+ *
+ * Example: "%{sha1:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
+ */
+static ssize_t sha1_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                        UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                        REQUEST *request, char const *fmt)
+{
+       uint8_t         digest[20];
+       size_t          i, len, inlen;
+       uint8_t         *p;
+       fr_sha1_ctx     sha1_ctx;
+       TALLOC_CTX      *tmp_ctx = NULL;
+
+       VALUE_FROM_FMT(tmp_ctx, p, inlen, request, fmt);
+
+       fr_sha1_init(&sha1_ctx);
+       fr_sha1_update(&sha1_ctx, p, inlen);
+       fr_sha1_final(digest, &sha1_ctx);
+
+       /*
+        *      Each digest octet takes two hex digits, plus one for
+        *      the terminating NUL. SHA1 is 160 bits (20 bytes)
+        */
+       len = (outlen / 2) - 1;
+       if (len > 20) len = 20;
+
+       for (i = 0; i < len; i++) snprintf((*out) + (i * 2), 3, "%02x", digest[i]);
+
+       talloc_free(tmp_ctx);
+
+       return strlen(*out);
+}
+
+/** Calculate any digest supported by OpenSSL EVP_MD
+ *
+ * Example: "%{sha256:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
+ */
+#ifdef HAVE_OPENSSL_EVP_H
+static ssize_t evp_md_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                          UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                          REQUEST *request, char const *fmt, EVP_MD const *md)
+{
+       uint8_t         digest[EVP_MAX_MD_SIZE];
+       unsigned int    digestlen, i, len;
+       size_t          inlen;
+       uint8_t         *p;
+       EVP_MD_CTX      *md_ctx;
+       TALLOC_CTX      *tmp_ctx = NULL;
+
+       VALUE_FROM_FMT(tmp_ctx, p, inlen, request, fmt);
+
+       md_ctx = EVP_MD_CTX_create();
+       EVP_DigestInit_ex(md_ctx, md, NULL);
+       EVP_DigestUpdate(md_ctx, p, inlen);
+       EVP_DigestFinal_ex(md_ctx, digest, &digestlen);
+       EVP_MD_CTX_destroy(md_ctx);
+
+       /*
+        *      Each digest octet takes two hex digits, plus one for
+        *      the terminating NUL.
+        */
+       len = (outlen / 2) - 1;
+       if (len > digestlen) len = digestlen;
+
+       for (i = 0; i < len; i++) snprintf((*out) + (i * 2), 3, "%02x", digest[i]);
+
+       talloc_free(tmp_ctx);
+
+       return strlen(*out);
+}
+
+#  define EVP_MD_XLAT(_md) \
+static ssize_t _md##_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,\
+                         void const *mod_inst, void const *xlat_inst,\
+                         REQUEST *request, char const *fmt)\
+{\
+       return evp_md_xlat(ctx, out, outlen, mod_inst, xlat_inst, request, fmt, EVP_##_md());\
+}
+
+EVP_MD_XLAT(sha256)
+EVP_MD_XLAT(sha512)
+
+#  ifdef HAVE_EVP_SHA3_512
+EVP_MD_XLAT(sha3_256)
+EVP_MD_XLAT(sha3_512)
+#  endif
+#endif
+
+/** Generate the HMAC-MD5 of a string or attribute
+ *
+ * Example: "%{hmacmd5:foo bar}" == "Zm9v"
+ */
+static ssize_t hmac_md5_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,
+                            UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                            REQUEST *request, char const *fmt)
+{
+
+       char const      *p, *q;
+       uint8_t         digest[MD5_DIGEST_LENGTH];
+
+       char            *data_fmt;
+
+       uint8_t         *data_p, *key_p;
+       size_t          data_len, key_len;
+       TALLOC_CTX      *tmp_ctx = NULL;
+
+       if (outlen <= (sizeof(digest) * 2)) {
+               REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes",
+                       (sizeof(digest) * 2) + 1, outlen);
+               return -1;
+       }
+
+       p = fmt;
+       while (isspace(*p)) p++;
+
+       /*
+        *      Find the delimiting char
+        */
+       q = strchr(p, ' ');
+       if (!q) {
+               REDEBUG("HMAC requires exactly two arguments (&data &key)");
+               return -1;
+       }
+
+       tmp_ctx = talloc_new(ctx);
+       data_fmt = talloc_bstrndup(tmp_ctx, p, q - p);
+       p = q + 1;
+
+       {
+               VALUE_FROM_FMT(tmp_ctx, data_p, data_len, request, data_fmt);
+       }
+       {
+               VALUE_FROM_FMT(tmp_ctx, key_p, key_len, request, p);
+       }
+       fr_hmac_md5(digest, data_p, data_len, key_p, key_len);
+       talloc_free(tmp_ctx);
+
+       return fr_bin2hex(*out, digest, sizeof(digest));
+}
+
+/** Generate the HMAC-SHA1 of a string or attribute
+ *
+ * Example: "%{hmacsha1:foo bar}" == "Zm9v"
+ */
+static ssize_t hmac_sha1_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,
+                             UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                             REQUEST *request, char const *fmt)
+{
+       char const      *p, *q;
+       uint8_t         digest[SHA1_DIGEST_LENGTH];
+
+       char            *data_fmt;
+
+       uint8_t         *data_p, *key_p;
+       size_t          data_len, key_len;
+       TALLOC_CTX      *tmp_ctx = NULL;
+
+       if (outlen <= (sizeof(digest) * 2)) {
+               REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes",
+                       (sizeof(digest) * 2) + 1, outlen);
+               return -1;
+       }
+
+       p = fmt;
+       while (isspace(*p)) p++;
+
+       /*
+        *      Find the delimiting char
+        */
+       q = strchr(p, ' ');
+       if (!q) {
+               REDEBUG("HMAC requires exactly two arguments (&data &key)");
+               return -1;
+       }
+
+       tmp_ctx = talloc_new(ctx);
+       data_fmt = talloc_bstrndup(tmp_ctx, p, q - p);
+       p = q + 1;
+
+       {
+               VALUE_FROM_FMT(tmp_ctx, data_p, data_len, request, data_fmt);
+       }
+       {
+               VALUE_FROM_FMT(tmp_ctx, key_p, key_len, request, p);
+       }
+
+       fr_hmac_sha1(digest, data_p, data_len, key_p, key_len);
+
+       talloc_free(tmp_ctx);
+
+       return fr_bin2hex(*out, digest, sizeof(digest));
+}
+
+/** Encode attributes as a series of string attribute/value pairs
+ *
+ * This is intended to serialize one or more attributes as a comma
+ * delimited string.
+ *
+ * Example: "%{pairs:request:}" == "User-Name = 'foo', User-Password = 'bar'"
+ */
+static ssize_t pairs_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,
+                         UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                         REQUEST *request, char const *fmt)
+{
+       vp_tmpl_t       *vpt = NULL;
+       fr_cursor_t     cursor;
+       size_t          len, freespace = outlen;
+       char            *p = *out;
+
+       VALUE_PAIR *vp;
+
+       if (tmpl_afrom_attr_str(ctx, &vpt, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) <= 0) {
+               RPEDEBUG("Invalid input");
+               return -1;
+       }
+
+       for (vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
+            vp;
+            vp = fr_cursor_next(&cursor)) {
+               FR_TOKEN op = vp->op;
+
+               vp->op = T_OP_EQ;
+               len = fr_pair_snprint(p, freespace, vp);
+               vp->op = op;
+
+               if (is_truncated(len, freespace)) {
+               no_space:
+                       talloc_free(vpt);
+                       REDEBUG("Insufficient space to store pair string, needed %zu bytes have %zu bytes",
+                               (p - *out) + len, outlen);
+                       return -1;
+               }
+               p += len;
+               freespace -= len;
+
+               if (freespace < 2) {
+                       len = 2;
+                       goto no_space;
+               }
+
+               *p++ = ',';
+               *p++ = ' ';
+               freespace -= 2;
+       }
+
+       /* Trim the trailing ', ' */
+       if (p != *out) p -= 2;
+       *p = '\0';
+       talloc_free(vpt);
+
+       return (p - *out);
+}
+
+/** Encode string or attribute as base64
+ *
+ * Example: "%{base64:foo}" == "Zm9v"
+ */
+static ssize_t base64_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                          UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                          REQUEST *request, char const *fmt)
+{
+       size_t          inlen;
+       uint8_t         *p;
+       TALLOC_CTX      *tmp_ctx = NULL;
+       ssize_t         ret;
+
+       VALUE_FROM_FMT(tmp_ctx, p, inlen, request, fmt);
+
+       /*
+        *  We can accurately calculate the length of the output string
+        *  if it's larger than outlen, the output would be useless so abort.
+        */
+       if ((FR_BASE64_ENC_LENGTH(inlen) + 1) > outlen) {
+               REDEBUG("xlat failed");
+
+               talloc_free(tmp_ctx);
+
+               return -1;
+       }
+
+       ret = fr_base64_encode(*out, outlen, p, inlen);
+       talloc_free(tmp_ctx);
+
+       return ret;
+}
+
+/** Convert base64 to hex
+ *
+ * Example: "%{base64tohex:Zm9v}" == "666f6f"
+ */
+static ssize_t base64_to_hex_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                                 UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                                 REQUEST *request, char const *fmt)
+{
+       uint8_t decbuf[1024];
+
+       ssize_t declen;
+       ssize_t len = strlen(fmt);
+
+       declen = fr_base64_decode(decbuf, sizeof(decbuf), fmt, len);
+       if (declen < 0) {
+               REDEBUG("Base64 string invalid");
+               return -1;
+       }
+
+       if ((size_t)((declen * 2) + 1) > outlen) {
+               REDEBUG("Base64 conversion failed, output buffer exhausted, needed %zd bytes, have %zd bytes",
+                       (declen * 2) + 1, outlen);
+               return -1;
+       }
+
+       return fr_bin2hex(*out, decbuf, declen);
+}
+
+/** Split an attribute into multiple new attributes based on a delimiter
+ *
+ * @todo should support multibyte delimiter for string types.
+ *
+ * Example: "%{explode:&ref <delim>}"
+ */
+static ssize_t explode_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,
+                           UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                           REQUEST *request, char const *fmt)
+{
+       vp_tmpl_t       *vpt = NULL;
+       VALUE_PAIR      *vp;
+       fr_cursor_t     cursor, to_merge;
+       VALUE_PAIR      *head = NULL;
+       ssize_t         slen;
+       int             count = 0;
+       char const      *p = fmt;
+       char            delim;
+
+       /*
+        *  Trim whitespace
+        */
+       while (isspace(*p) && p++);
+
+       slen = tmpl_afrom_attr_substr(ctx, &vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
+       if (slen <= 0) {
+               RPEDEBUG("Invalid input");
+               return -1;
+       }
 
-               vendor = fr_dict_vendor_by_da(vp->da);
-               if (vendor) RIDEBUG2("Vendor : %i (%s)", vendor->pen, vendor->name);
-               RIDEBUG2("Type   : %s", fr_int2str(dict_attr_types, vp->vp_type, "<INVALID>"));
+       p += slen;
+
+       if (*p++ != ' ') {
+       arg_error:
+               talloc_free(vpt);
+               REDEBUG("explode needs exactly two arguments: &ref <delim>");
+               return -1;
+       }
+
+       if (*p == '\0') goto arg_error;
+
+       delim = *p;
 
+       fr_cursor_init(&to_merge, &head);
+
+       vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
+       while (vp) {
+               VALUE_PAIR *nvp;
+               char const *end;
+               char const *q;
+
+               /*
+                *      This can theoretically operate on lists too
+                *      so we need to check the type of each attribute.
+                */
                switch (vp->vp_type) {
-               case FR_TYPE_VARIABLE_SIZE:
-                       RIDEBUG2("Length : %zu", vp->vp_length);
+               case FR_TYPE_OCTETS:
+               case FR_TYPE_STRING:
                        break;
 
                default:
-                       break;
+                       goto next;
                }
 
-               if (!RDEBUG_ENABLED4) continue;
-
-               type = dict_attr_types;
-               while (type->name) {
-                       int pad;
+               p = vp->vp_ptr;
+               end = p + vp->vp_length;
+               while (p < end) {
+                       q = memchr(p, delim, end - p);
+                       if (!q) {
+                               /* Delimiter not present in attribute */
+                               if (p == vp->vp_ptr) goto next;
+                               q = end;
+                       }
 
-                       fr_value_box_t *dst = NULL;
+                       /* Skip zero length */
+                       if (q == p) {
+                               p = q + 1;
+                               continue;
+                       }
 
-                       if ((fr_type_t) type->number == vp->vp_type) goto next_type;
+                       nvp = fr_pair_afrom_da(talloc_parent(vp), vp->da);
+                       if (!nvp) {
+                               fr_pair_list_free(&head);
+                               return -1;
+                       }
+                       nvp->tag = vp->tag;
 
-                       switch (type->number) {
-                       case FR_TYPE_INVALID:           /* Not real type */
-                       case FR_TYPE_MAX:               /* Not real type */
-                       case FR_TYPE_COMBO_IP_ADDR:     /* Covered by IPv4 address IPv6 address */
-                       case FR_TYPE_COMBO_IP_PREFIX:   /* Covered by IPv4 address IPv6 address */
-                       case FR_TYPE_TIMEVAL:           /* Not a VALUE_PAIR type */
-                       case FR_TYPE_STRUCTURAL:
-                               goto next_type;
+                       switch (vp->vp_type) {
+                       case FR_TYPE_OCTETS:
+                       {
+                               uint8_t *buff;
 
-                       default:
-                               break;
+                               buff = talloc_array(nvp, uint8_t, q - p);
+                               memcpy(buff, p, q - p);
+                               fr_pair_value_memsteal(nvp, buff);
                        }
+                               break;
 
-                       dst = talloc_zero(vp, fr_value_box_t);
-                       /* We expect some to fail */
-                       if (fr_value_box_cast(dst, dst, type->number, NULL, &vp->data) < 0) {
-                               goto next_type;
-                       }
+                       case FR_TYPE_STRING:
+                       {
+                               char *buff;
 
-                       value = fr_value_box_asprint(dst, dst, '\'');
-                       if (!value) goto next_type;
+                               buff = talloc_array(nvp, char, (q - p) + 1);
+                               memcpy(buff, p, q - p);
+                               buff[q - p] = '\0';
+                               fr_pair_value_strsteal(nvp, (char *)buff);
+                       }
+                               break;
 
-                       if ((pad = (11 - strlen(type->name))) < 0) {
-                               pad = 0;
+                       default:
+                               rad_assert(0);
                        }
 
-                       RINDENT();
-                       RDEBUG2("as %s%*s: %s", type->name, pad, " ", value);
-                       REXDENT();
+                       fr_cursor_append(&to_merge, nvp);
 
-               next_type:
-                       talloc_free(dst);
-                       type++;
+                       p = q + 1;      /* next */
+
+                       count++;
                }
+
+               /*
+                *      Remove the unexploded version
+                */
+               vp = fr_cursor_remove(&cursor);
+               talloc_free(vp);
+               /*
+                *      Remove sets cursor->current to
+                *      the next iter value.
+                */
+               vp = fr_cursor_current(&cursor);
+               continue;
+
+       next:
+               vp = fr_cursor_next(&cursor);
        }
-       REXDENT();
 
+       fr_cursor_head(&to_merge);
+       fr_cursor_merge(&cursor, &to_merge);
        talloc_free(vpt);
 
-       return 0;
+       return snprintf(*out, outlen, "%i", count);
 }
 
-/** Processes fmt as a map string and applies it to the current request
+/** Calculate number of seconds until the next n hour(s), day(s), week(s), year(s).
  *
- * e.g. "%{map:&User-Name := 'foo'}"
+ * For example, if it were 16:18 %{nexttime:1h} would expand to 2520.
  *
- * Allows sets of modifications to be cached and then applied.
- * Useful for processing generic attributes from LDAP.
+ * The envisaged usage for this function is to limit sessions so that they don't
+ * cross billing periods. The output of the xlat should be combined with %{rand:} to create
+ * some jitter, unless the desired effect is every subscriber on the network
+ * re-authenticating at the same time.
  */
-static ssize_t xlat_map(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                       UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                       REQUEST *request, char const *fmt)
+static ssize_t next_time_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
+                             UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                             REQUEST *request, char const *fmt)
 {
-       vp_map_t *map = NULL;
-       int ret;
+       long            num;
 
-       if (map_afrom_attr_str(request, &map, fmt,
-                              REQUEST_CURRENT, PAIR_LIST_REQUEST,
-                              REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
-               RPEDEBUG("Failed parsing \"%s\" as map", fmt);
+       char const      *p;
+       char            *q;
+       time_t          now;
+       struct tm       *local, local_buff;
+
+       now = time(NULL);
+       local = localtime_r(&now, &local_buff);
+
+       p = fmt;
+
+       num = strtoul(p, &q, 10);
+       if (!q || *q == '\0') {
+               REDEBUG("nexttime: <int> must be followed by period specifier (h|d|w|m|y)");
                return -1;
        }
 
-       RINDENT();
-       ret = map_to_request(request, map, map_to_vp, NULL);
-       REXDENT();
-       talloc_free(map);
-       if (ret < 0) return strlcpy(*out, "0", outlen);
+       if (p == q) {
+               num = 1;
+       } else {
+               p += q - p;
+       }
 
-       return strlcpy(*out, "1", outlen);
-}
+       local->tm_sec = 0;
+       local->tm_min = 0;
 
-/** Prints the current module processing the request
- *
- */
-static ssize_t xlat_module(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                          UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                          REQUEST *request, UNUSED char const *fmt)
-{
-       strlcpy(*out, request->module, outlen);
+       switch (*p) {
+       case 'h':
+               local->tm_hour += num;
+               break;
 
-       return strlen(*out);
-}
+       case 'd':
+               local->tm_hour = 0;
+               local->tm_mday += num;
+               break;
 
-#if defined(HAVE_REGEX) && defined(HAVE_PCRE)
-static ssize_t xlat_regex(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                         UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                         REQUEST *request, char const *fmt)
-{
-       char *p;
-       size_t len;
+       case 'w':
+               local->tm_hour = 0;
+               local->tm_mday += (7 - local->tm_wday) + (7 * (num-1));
+               break;
 
-       if (regex_request_to_sub_named(request, &p, request, fmt) < 0) return 0;
+       case 'm':
+               local->tm_hour = 0;
+               local->tm_mday = 1;
+               local->tm_mon += num;
+               break;
 
-       len = talloc_array_length(p);
-       if (len > outlen) {
-               RDEBUG("Insufficient buffer space to write subcapture value, needed %zu bytes, have %zu bytes",
-                      len, outlen);
+       case 'y':
+               local->tm_hour = 0;
+               local->tm_mday = 1;
+               local->tm_mon = 0;
+               local->tm_year += num;
+               break;
+
+       default:
+               REDEBUG("nexttime: Invalid period specifier '%c', must be h|d|w|m|y", *p);
                return -1;
        }
-       strlcpy(*out, p, outlen);
 
-       return len - 1; /* - \0 */
+       return snprintf(*out, outlen, "%" PRIu64, (uint64_t)(mktime(local) - now));
 }
-#endif
 
-#ifdef WITH_UNLANG
-/** Implements the Foreach-Variable-X
+
+/** Parse the 3 arguments to lpad / rpad.
  *
- * @see modcall()
+ * Parses a fmt string with the components @verbatim <tmpl> <pad_len> <pad_char>@endverbatim
+ *
+ * @param[out] vpt_p           Template to retrieve value to pad.
+ * @param[out] pad_len_p       Length the string needs to be padded to.
+ * @param[out] pad_char_p      Char to use for padding.
+ * @param[in] request          The current request.
+ * @param[in] fmt              string to parse.
+ *
+ * @return
+ *     - <= 0 the negative offset the parse error ocurred at.
+ *     - >0 how many bytes of fmt were parsed.
  */
-static ssize_t xlat_foreach(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
-                           void const *mod_inst, UNUSED void const *xlat_inst,
-                           REQUEST *request, UNUSED char const *fmt)
+static ssize_t parse_pad(vp_tmpl_t **vpt_p, size_t *pad_len_p, char *pad_char_p, REQUEST *request, char const *fmt)
 {
-       VALUE_PAIR      **pvp;
-
-       /*
-        *      See modcall, "FOREACH" for how this works.
-        */
-       pvp = (VALUE_PAIR **) request_data_reference(request, (void *)radius_get_vp, *(int const *) mod_inst);
-       if (!pvp || !*pvp) return 0;
+       ssize_t         slen;
+       unsigned long   pad_len;
+       char const      *p;
+       char            *end;
+       vp_tmpl_t       *vpt;
 
-       *out = fr_pair_value_asprint(ctx, *pvp, '\0');
-       return  talloc_array_length(*out) - 1;
-}
-#endif
+       *pad_char_p = ' ';              /* the default */
 
-/** Print data as string, if possible.
- *
- * If attribute "Foo" is defined as "octets" it will normally
- * be printed as 0x0a0a0a. The xlat "%{string:Foo}" will instead
- * expand to "\n\n\n"
- */
-static ssize_t xlat_string(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                          UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                          REQUEST *request, char const *fmt)
-{
-       ssize_t         ret;
-       VALUE_PAIR      *vp;
-       uint8_t         buffer[64];
+       *vpt_p = NULL;
 
-       while (isspace((int) *fmt)) fmt++;
+       p = fmt;
+       while (isspace((int) *p)) p++;
 
-       if (outlen < 3) {
-       nothing:
+       if (*p != '&') {
+               RDEBUG("First argument must be an attribute reference");
                return 0;
        }
 
-       if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
+       slen = tmpl_afrom_attr_substr(request, &vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
+       if (slen <= 0) {
+               RPEDEBUG("Failed parsing input string");
+               return slen;
+       }
+
+       p = fmt + slen;
+
+       while (isspace((int) *p)) p++;
+
+       pad_len = strtoul(p, &end, 10);
+       if ((pad_len == ULONG_MAX) || (pad_len > 8192)) {
+               talloc_free(vpt);
+               RDEBUG("Invalid pad_len found at: %s", p);
+               return fmt - p;
+       }
+
+       p += (end - p);
 
        /*
-        *      These are printed specially.
+        *      The pad_char_p character is optional.
+        *
+        *      But we must have a space after the previous number,
+        *      and we must have only ONE pad_char_p character.
         */
-       switch (vp->vp_type) {
-       case FR_TYPE_OCTETS:
-               return fr_snprint(*out, outlen, (char const *) vp->vp_octets, vp->vp_length, '"');
+       if (*p) {
+               if (!isspace(*p)) {
+                       talloc_free(vpt);
+                       RDEBUG("Invalid text found at: %s", p);
+                       return fmt - p;
+               }
 
-               /*
-                *      Note that "%{string:...}" is NOT binary safe!
-                *      It is explicitly used to get rid of embedded zeros.
-                */
-       case FR_TYPE_STRING:
-               return strlcpy(*out, vp->vp_strvalue, outlen);
+               while (isspace((int) *p)) p++;
 
-       default:
-               break;
+               if (p[1] != '\0') {
+                       talloc_free(vpt);
+                       RDEBUG("Invalid text found at: %s", p);
+                       return fmt - p;
+               }
+
+               *pad_char_p = *p++;
        }
 
-       ret = fr_value_box_to_network(NULL, buffer, sizeof(buffer), &vp->data);
-       if (ret < 0) return ret;
+       *vpt_p = vpt;
+       *pad_len_p = pad_len;
 
-       return fr_snprint(*out, outlen, (char const *) buffer, ret, '\0');
+       return p - fmt;
 }
 
-/** xlat expand string attribute value
+
+/** left pad a string
  *
+ *  %{lpad:&Attribute-Name length 'x'}
  */
-static ssize_t xlat_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,
+static ssize_t lpad_xlat(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
                         UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
                         REQUEST *request, char const *fmt)
 {
-       ssize_t slen;
-       VALUE_PAIR *vp;
+       char            fill;
+       size_t          pad;
+       ssize_t         len;
+       vp_tmpl_t       *vpt;
+       char            *to_pad = NULL;
 
-       while (isspace((int) *fmt)) fmt++;
+       if (parse_pad(&vpt, &pad, &fill, request, fmt) <= 0) return 0;
 
-       if (outlen < 3) {
-       nothing:
-               return 0;
-       }
+       if (!fr_cond_assert(vpt)) return 0;
 
-       if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
+       /*
+        *      Print the attribute (left justified).  If it's too
+        *      big, we're done.
+        */
+       len = tmpl_aexpand(ctx, &to_pad, request, vpt, NULL, NULL);
+       if (len <= 0) return -1;
 
-       RDEBUG2("EXPAND %s", fmt);
-       RINDENT();
+       /*
+        *      Already big enough, no padding required...
+        */
+       if ((size_t) len >= pad) {
+               *out = to_pad;
+               return pad;
+       }
 
        /*
-        *      If it's a string, expand it again
+        *      Realloc is actually pretty cheap in most cases...
         */
-       if (vp->vp_type == FR_TYPE_STRING) {
-               slen = xlat_eval(*out, outlen, request, vp->vp_strvalue, NULL, NULL);
-               if (slen <= 0) return slen;
+       MEM(to_pad = talloc_realloc(ctx, to_pad, char, pad + 1));
+
        /*
-        *      If it's not a string, treat it as a literal
+        *      We have to shift the string to the right, and pad with
+        *      "fill" characters.
         */
-       } else {
-               *out = fr_pair_value_asprint(ctx, vp, '\0');
-               if (!*out) return -1;
-               slen = talloc_array_length(*out) - 1;
-       }
+       memmove(to_pad + (pad - len), to_pad, len + 1);
+       memset(to_pad, fill, pad - len);
 
-       REXDENT();
-       RDEBUG2("--> %s", *out);
+       *out = to_pad;
 
-       return slen;
+       return pad;
 }
 
-/** Dynamically change the debugging level for the current request
+/** right pad a string
  *
- * Example %{debug:3}
+ *  %{rpad:&Attribute-Name length 'x'}
  */
-static ssize_t xlat_debug(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                         UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                         REQUEST *request, char const *fmt)
+static ssize_t rpad_xlat(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
+                        UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+                        REQUEST *request, char const *fmt)
 {
-       int level = 0;
+       char            fill;
+       size_t          pad;
+       ssize_t         len;
+       vp_tmpl_t       *vpt;
+       char            *to_pad = NULL;
+
+       rad_assert(!*out);
+
+       if (parse_pad(&vpt, &pad, &fill, request, fmt) <= 0) return 0;
+
+       if (!fr_cond_assert(vpt)) return 0;
 
        /*
-        *  Expand to previous (or current) level
+        *      Print the attribute (left justified).  If it's too
+        *      big, we're done.
         */
-       snprintf(*out, outlen, "%d", request->log.lvl);
+       len = tmpl_aexpand(ctx, &to_pad, request, vpt, NULL, NULL);
+       if (len <= 0) return 0;
+
+       if ((size_t) len >= pad) {
+               *out = to_pad;
+               return pad;
+       }
+
+       MEM(to_pad = talloc_realloc(ctx, to_pad, char, pad + 1));
 
        /*
-        *  Assume we just want to get the current value and NOT set it to 0
+        *      We have to pad with "fill" characters.
         */
-       if (!*fmt)
-               goto done;
+       memset(to_pad + len, fill, pad - len);
+       to_pad[pad] = '\0';
 
-       level = atoi(fmt);
-       if (level == 0) {
-               request->log.lvl = RAD_REQUEST_LVL_NONE;
-       } else {
-               if (level > 4) level = 4;
-               request->log.lvl = level;
-       }
+       *out = to_pad;
 
-done:
-       return strlen(*out);
+       return pad;
 }
 
 /*
@@ -1223,6 +2381,31 @@ int xlat_init(void)
        XLAT_REGISTER(regex);
 #endif
 
+       xlat_register(NULL, "rand", rand_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "randstr", randstr_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "urlquote", urlquote_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "urlunquote", urlunquote_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "tolower", tolower_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "toupper", toupper_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "md5", md5_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "sha1", sha1_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+#ifdef HAVE_OPENSSL_EVP_H
+       xlat_register(NULL, "sha256", sha256_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "sha512", sha512_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+#endif
+       xlat_register(NULL, "hmacmd5", hmac_md5_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "hmacsha1", hmac_sha1_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "pairs", pairs_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+
+       xlat_register(NULL, "base64", base64_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "base64tohex", base64_to_hex_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+
+       xlat_register(NULL, "explode", explode_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+
+       xlat_register(NULL, "nexttime", next_time_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "lpad", lpad_xlat, NULL, NULL, 0, 0, true);
+       xlat_register(NULL, "rpad", rpad_xlat, NULL, NULL, 0, 0, true);
+
        xlat_register(&xlat_foreach_inst[0], "debug", xlat_debug, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
        c = xlat_func_find("debug");
        rad_assert(c != NULL);
index 86a3004286ef4608ed8c660d0f42343abd093014..0c0bb6ce6a83f7be5f1f2c90a03f430e09cb2f2a 100644 (file)
@@ -26,16 +26,10 @@ RCSID("$Id$")
 USES_APPLE_DEPRECATED_API
 
 #include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/md5.h>
-#include <freeradius-devel/sha1.h>
-#include <freeradius-devel/base64.h>
+
 #include <freeradius-devel/modules.h>
 #include <freeradius-devel/rad_assert.h>
 
-#ifdef HAVE_OPENSSL_EVP_H
-#  include <openssl/evp.h>
-#endif
-
 #include <ctype.h>
 
 #include "rlm_expr.h"
@@ -53,18 +47,6 @@ static const CONF_PARSER module_config[] = {
        CONF_PARSER_TERMINATOR
 };
 
-/*
- *     Lookup tables for randstr char classes
- */
-static char randstr_punc[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
-static char randstr_salt[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz/.";
-
-/*
- *     Characters humans rarely confuse. Reduces char set considerably
- *     should only be used for things such as one time passwords.
- */
-static char randstr_otp[] = "469ACGHJKLMNPQRUVWXYabdfhijkprstuvwxyz";
-
 static char const hextab[] = "0123456789abcdef";
 
 /** Calculate powers
@@ -549,266 +531,6 @@ static ssize_t expr_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
        return strlen(*out);
 }
 
-/** Generate a random integer value
- *
- */
-static ssize_t rand_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                        UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                        UNUSED REQUEST *request, char const *fmt)
-{
-       int64_t         result;
-
-       result = atoi(fmt);
-
-       /*
-        *      Too small or too big.
-        */
-       if (result <= 0) return -1;
-       if (result >= (1 << 30)) result = (1 << 30);
-
-       result *= fr_rand();    /* 0..2^32-1 */
-       result >>= 32;
-
-       snprintf(*out, outlen, "%ld", (long int) result);
-       return strlen(*out);
-}
-
-/** Generate a string of random chars
- *
- *  Build strings of random chars, useful for generating tokens and passcodes
- *  Format similar to String::Random.
- */
-static ssize_t randstr_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                           UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                           REQUEST *request, char const *fmt)
-{
-       char const      *p;
-       char            *out_p = *out;
-       unsigned int    result;
-       unsigned int    number;
-       size_t          freespace = outlen;
-
-       if (outlen <= 1) return 0;
-
-       p = fmt;
-       while (*p && (--freespace > 0)) {
-               number = 0;
-
-               /*
-                *      Modifiers are polite.
-                *
-                *      But we limit it to 100, because we don't want
-                *      utter stupidity.
-                */
-               while (isdigit((int) *p)) {
-                       if (number >= 100) {
-                               p++;
-                               continue;
-                       }
-
-                       number *= 10;
-                       number += *p - '0';
-                       p++;
-               }
-
-       redo:
-               result = fr_rand();
-
-               switch (*p) {
-               /*
-                *  Lowercase letters
-                */
-               case 'c':
-                       *out_p++ = 'a' + (result % 26);
-                       break;
-
-               /*
-                *  Uppercase letters
-                */
-               case 'C':
-                       *out_p++ = 'A' + (result % 26);
-                       break;
-
-               /*
-                *  Numbers
-                */
-               case 'n':
-                       *out_p++ = '0' + (result % 10);
-                       break;
-
-               /*
-                *  Alpha numeric
-                */
-               case 'a':
-                       *out_p++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
-                       break;
-
-               /*
-                *  Punctuation
-                */
-               case '!':
-                       *out_p++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
-                       break;
-
-               /*
-                *  Alpa numeric + punctuation
-                */
-               case '.':
-                       *out_p++ = '!' + (result % 95);
-                       break;
-
-               /*
-                *  Alpha numeric + salt chars './'
-                */
-               case 's':
-                       *out_p++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
-                       break;
-
-               /*
-                *  Chars suitable for One Time Password tokens.
-                *  Alpha numeric with easily confused char pairs removed.
-                */
-               case 'o':
-                       *out_p++ = randstr_otp[result % (sizeof(randstr_otp) - 1)];
-                       break;
-
-               /*
-                *  Binary data as hexits (we don't really support
-                *  non printable chars).
-                */
-               case 'h':
-                       if (freespace < 2) {
-                               break;
-                       }
-
-                       snprintf(out_p, 3, "%02x", result % 256);
-
-                       /* Already decremented */
-                       freespace -= 1;
-                       out_p += 2;
-                       break;
-
-               /*
-                *  Binary data with uppercase hexits
-                */
-               case 'H':
-                       if (freespace < 2) {
-                               break;
-                       }
-
-                       snprintf(out_p, 3, "%02X", result % 256);
-
-                       /* Already decremented */
-                       freespace -= 1;
-                       out_p += 2;
-                       break;
-
-               default:
-                       REDEBUG("Invalid character class '%c'", *p);
-
-                       return -1;
-               }
-
-               if (number > 0) {
-                       number--;
-                       goto redo;
-               }
-
-               p++;
-       }
-
-       *out_p++ = '\0';
-
-       return outlen - freespace;
-}
-
-/** URLencode special characters
- *
- * Example: "%{urlquote:http://example.org/}" == "http%3A%47%47example.org%47"
- */
-static ssize_t urlquote_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                            UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                            UNUSED REQUEST *request, char const *fmt)
-{
-       char const      *p;
-       char            *out_p = *out;
-       size_t  freespace = outlen;
-
-       if (outlen <= 1) return 0;
-
-       p = fmt;
-       while (*p && (--freespace > 0)) {
-               if (isalnum(*p)) {
-                       *out_p++ = *p++;
-                       continue;
-               }
-
-               switch (*p) {
-               case '-':
-               case '_':
-               case '.':
-               case '~':
-                       *out_p++ = *p++;
-                       break;
-
-               default:
-                       if (freespace < 3)
-                               break;
-
-                       /* MUST be upper case hex to be compliant */
-                       snprintf(out_p, 4, "%%%02X", (uint8_t) *p++); /* %XX */
-
-                       /* Already decremented */
-                       freespace -= 2;
-                       out_p += 3;
-               }
-       }
-
-       *out_p = '\0';
-
-       return outlen - freespace;
-}
-
-/** URLdecode special characters
- *
- * Example: "%{urlunquote:http%%3A%%47%%47example.org%%47}" == "http://example.org/"
- *
- * Remember to escape % with %% in strings, else xlat will try to parse it.
- */
-static ssize_t urlunquote_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                              UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                              REQUEST *request, char const *fmt)
-{
-       char const *p;
-       char *out_p = *out;
-       char *c1, *c2;
-       size_t  freespace = outlen;
-
-       if (outlen <= 1) return 0;
-
-       p = fmt;
-       while (*p && (--freespace > 0)) {
-               if (*p != '%') {
-                       *out_p++ = *p++;
-                       continue;
-               }
-               /* Is a % char */
-
-               /* Don't need \0 check, as it won't be in the hextab */
-               if (!(c1 = memchr(hextab, tolower(*++p), 16)) ||
-                   !(c2 = memchr(hextab, tolower(*++p), 16))) {
-                       REMARKER(fmt, p - fmt, "Non-hex char in % sequence");
-                       return -1;
-               }
-               p++;
-               *out_p++ = ((c1 - hextab) << 4) + (c2 - hextab);
-       }
-
-       *out_p = '\0';
-
-       return outlen - freespace;
-}
-
 /** Equivalent to the old safe_characters functionality in rlm_sql but with utf8 support
  *
  * @verbatim Example: "%{escape:<img>foo.jpg</img>}" == "=60img=62foo.jpg=60/img=62" @endverbatim
@@ -915,932 +637,28 @@ static ssize_t unescape_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
        return outlen - freespace;
 }
 
-/** Convert a string to lowercase
- *
- * Example: "%{tolower:Bar}" == "bar"
- *
- * Probably only works for ASCII
- */
-static ssize_t tolower_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                           UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                           UNUSED REQUEST *request, char const *fmt)
-{
-       char *q;
-       char const *p;
-
-       if (outlen <= 1) return 0;
-
-       for (p = fmt, q = *out; *p != '\0'; p++, outlen--) {
-               if (outlen <= 1) break;
-
-               *(q++) = tolower((int) *p);
-       }
-
-       *q = '\0';
-
-       return strlen(*out);
-}
-
-/** Convert a string to uppercase
- *
- * Example: "%{toupper:Foo}" == "FOO"
- *
- * Probably only works for ASCII
- */
-static ssize_t toupper_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                           UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                           UNUSED REQUEST *request, char const *fmt)
-{
-       char *q;
-       char const *p;
-
-       if (outlen <= 1) return 0;
-
-       for (p = fmt, q = *out; *p != '\0'; p++, outlen--) {
-               if (outlen <= 1) break;
-
-               *(q++) = toupper((int) *p);
-       }
-
-       *q = '\0';
-
-       return strlen(*out);
-}
-
-/** Decodes data or &Attr-Name to data
- *
- * This needs to die, and hopefully will die, when xlat functions accept
- * xlat node structures.
+/*
+ *     Do any per-module initialization that is separate to each
+ *     configured instance of the module.  e.g. set up connections
+ *     to external databases, read configuration files, set up
+ *     dictionary entries, etc.
  *
- * @param ctx          Talloc ctx for temporary allocations.
- * @param out          fr_value_box_t containing a shallow copy of the attribute,
- *                     or the fmt string.
- * @param request      current request.
- * @param fmt          string.
- * @returns
- *     - The length of the data.
- *     - -1 on failure.
+ *     If configuration information is given in the config section
+ *     that must be referenced in later calls, store a handle to it
+ *     in *instance otherwise put a null pointer there.
  */
-static int fr_value_box_from_fmt(TALLOC_CTX *ctx, fr_value_box_t *out, REQUEST *request, char const *fmt)
-{
-       VALUE_PAIR *vp;
-
-       while (isspace((int) *fmt)) fmt++;
-
-       /*
-        *      Not an attribute reference?  Just use the input format.
-        */
-       if (*fmt != '&') {
-               memset(out, 0, sizeof(*out));
-               out->vb_strvalue = fmt;
-               out->datum.length = talloc_array_length(fmt) - 1;
-               out->type = FR_TYPE_STRING;
-               return 0;
-       }
-
-       /*
-        *      If it's an attribute reference, get the underlying
-        *      attribute, and then store the data in network byte
-        *      order.
-        */
-       if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) return -1;
-
-       fr_value_box_copy(ctx, out, &vp->data);
-
-       return 0;
-}
-
-static int fr_value_box_to_bin(TALLOC_CTX *ctx, REQUEST *request, uint8_t **out, size_t *outlen, fr_value_box_t const *in)
+static int mod_bootstrap(void *instance, CONF_SECTION *conf)
 {
-       fr_value_box_t bin;
-
-       switch (in->type) {
-       case FR_TYPE_STRING:
-       case FR_TYPE_OCTETS:
-               memcpy(out, &in->datum.ptr, sizeof(in));
-               *outlen = in->datum.length;
-               return 0;
-
-       default:
-               if (fr_value_box_cast(ctx, &bin, FR_TYPE_OCTETS, NULL, in) < 0) {
-                       RPERROR("Failed casting xlat input to 'octets'");
-                       return -1;
-               }
-               memcpy(out, &bin.datum.ptr, sizeof(in));
-               *outlen = bin.datum.length;
-               return 0;
-       }
-}
+       rlm_expr_t *inst = instance;
 
-#define VALUE_FROM_FMT(_tmp_ctx, _p, _len, _request, _fmt) \
-       fr_value_box_t _value; \
-       if (!_tmp_ctx) MEM(_tmp_ctx = talloc_new(_request)); \
-       if (fr_value_box_from_fmt(_tmp_ctx, &_value, _request, _fmt) < 0) { \
-               talloc_free(_tmp_ctx); \
-               return -1; \
-       } \
-       if (fr_value_box_to_bin(_tmp_ctx, _request, &_p, &_len, &_value) < 0) { \
-               talloc_free(_tmp_ctx); \
-               return -1; \
-       }
-
-
-/** Calculate the MD5 hash of a string or attribute.
- *
- * Example: "%{md5:foo}" == "acbd18db4cc2f85cedef654fccc4a4d8"
- */
-static ssize_t md5_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                       UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                       REQUEST *request, char const *fmt)
-{
-       uint8_t         digest[16];
-       size_t          i, len, inlen;
-       uint8_t         *p;
-       FR_MD5_CTX      md5_ctx;
-       TALLOC_CTX      *tmp_ctx = NULL;
-
-       VALUE_FROM_FMT(tmp_ctx, p, inlen, request, fmt);
-
-       fr_md5_init(&md5_ctx);
-       fr_md5_update(&md5_ctx, p, inlen);
-       fr_md5_final(digest, &md5_ctx);
-
-       /*
-        *      Each digest octet takes two hex digits, plus one for
-        *      the terminating NUL.
-        */
-       len = (outlen / 2) - 1;
-       if (len > 16) len = 16;
-
-       for (i = 0; i < len; i++) snprintf((*out) + (i * 2), 3, "%02x", digest[i]);
-
-       talloc_free(tmp_ctx);
-
-       return strlen(*out);
-}
-
-/** Calculate the SHA1 hash of a string or attribute.
- *
- * Example: "%{sha1:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
- */
-static ssize_t sha1_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                        UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                        REQUEST *request, char const *fmt)
-{
-       uint8_t         digest[20];
-       size_t          i, len, inlen;
-       uint8_t         *p;
-       fr_sha1_ctx     sha1_ctx;
-       TALLOC_CTX      *tmp_ctx = NULL;
-
-       VALUE_FROM_FMT(tmp_ctx, p, inlen, request, fmt);
-
-       fr_sha1_init(&sha1_ctx);
-       fr_sha1_update(&sha1_ctx, p, inlen);
-       fr_sha1_final(digest, &sha1_ctx);
-
-       /*
-        *      Each digest octet takes two hex digits, plus one for
-        *      the terminating NUL. SHA1 is 160 bits (20 bytes)
-        */
-       len = (outlen / 2) - 1;
-       if (len > 20) len = 20;
-
-       for (i = 0; i < len; i++) snprintf((*out) + (i * 2), 3, "%02x", digest[i]);
-
-       talloc_free(tmp_ctx);
-
-       return strlen(*out);
-}
-
-/** Calculate any digest supported by OpenSSL EVP_MD
- *
- * Example: "%{sha256:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
- */
-#ifdef HAVE_OPENSSL_EVP_H
-static ssize_t evp_md_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                          UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                          REQUEST *request, char const *fmt, EVP_MD const *md)
-{
-       uint8_t         digest[EVP_MAX_MD_SIZE];
-       unsigned int    digestlen, i, len;
-       size_t          inlen;
-       uint8_t         *p;
-       EVP_MD_CTX      *md_ctx;
-       TALLOC_CTX      *tmp_ctx = NULL;
-
-       VALUE_FROM_FMT(tmp_ctx, p, inlen, request, fmt);
-
-       md_ctx = EVP_MD_CTX_create();
-       EVP_DigestInit_ex(md_ctx, md, NULL);
-       EVP_DigestUpdate(md_ctx, p, inlen);
-       EVP_DigestFinal_ex(md_ctx, digest, &digestlen);
-       EVP_MD_CTX_destroy(md_ctx);
-
-       /*
-        *      Each digest octet takes two hex digits, plus one for
-        *      the terminating NUL.
-        */
-       len = (outlen / 2) - 1;
-       if (len > digestlen) len = digestlen;
-
-       for (i = 0; i < len; i++) snprintf((*out) + (i * 2), 3, "%02x", digest[i]);
-
-       talloc_free(tmp_ctx);
-
-       return strlen(*out);
-}
-
-#  define EVP_MD_XLAT(_md) \
-static ssize_t _md##_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,\
-                         void const *mod_inst, void const *xlat_inst,\
-                         REQUEST *request, char const *fmt)\
-{\
-       return evp_md_xlat(ctx, out, outlen, mod_inst, xlat_inst, request, fmt, EVP_##_md());\
-}
-
-EVP_MD_XLAT(sha256)
-EVP_MD_XLAT(sha512)
-
-#  ifdef HAVE_EVP_SHA3_512
-EVP_MD_XLAT(sha3_256)
-EVP_MD_XLAT(sha3_512)
-#  endif
-#endif
-
-/** Generate the HMAC-MD5 of a string or attribute
- *
- * Example: "%{hmacmd5:foo bar}" == "Zm9v"
- */
-static ssize_t hmac_md5_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,
-                            UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                            REQUEST *request, char const *fmt)
-{
-
-       char const      *p, *q;
-       uint8_t         digest[MD5_DIGEST_LENGTH];
-
-       char            *data_fmt;
-
-       uint8_t         *data_p, *key_p;
-       size_t          data_len, key_len;
-       TALLOC_CTX      *tmp_ctx = NULL;
-
-       if (outlen <= (sizeof(digest) * 2)) {
-               REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes",
-                       (sizeof(digest) * 2) + 1, outlen);
-               return -1;
-       }
-
-       p = fmt;
-       while (isspace(*p)) p++;
-
-       /*
-        *      Find the delimiting char
-        */
-       q = strchr(p, ' ');
-       if (!q) {
-               REDEBUG("HMAC requires exactly two arguments (&data &key)");
-               return -1;
-       }
-
-       tmp_ctx = talloc_new(ctx);
-       data_fmt = talloc_bstrndup(tmp_ctx, p, q - p);
-       p = q + 1;
-
-       {
-               VALUE_FROM_FMT(tmp_ctx, data_p, data_len, request, data_fmt);
-       }
-       {
-               VALUE_FROM_FMT(tmp_ctx, key_p, key_len, request, p);
-       }
-       fr_hmac_md5(digest, data_p, data_len, key_p, key_len);
-       talloc_free(tmp_ctx);
-
-       return fr_bin2hex(*out, digest, sizeof(digest));
-}
-
-/** Generate the HMAC-SHA1 of a string or attribute
- *
- * Example: "%{hmacsha1:foo bar}" == "Zm9v"
- */
-static ssize_t hmac_sha1_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,
-                             UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                             REQUEST *request, char const *fmt)
-{
-       char const      *p, *q;
-       uint8_t         digest[SHA1_DIGEST_LENGTH];
-
-       char            *data_fmt;
-
-       uint8_t         *data_p, *key_p;
-       size_t          data_len, key_len;
-       TALLOC_CTX      *tmp_ctx = NULL;
-
-       if (outlen <= (sizeof(digest) * 2)) {
-               REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes",
-                       (sizeof(digest) * 2) + 1, outlen);
-               return -1;
-       }
-
-       p = fmt;
-       while (isspace(*p)) p++;
-
-       /*
-        *      Find the delimiting char
-        */
-       q = strchr(p, ' ');
-       if (!q) {
-               REDEBUG("HMAC requires exactly two arguments (&data &key)");
-               return -1;
-       }
-
-       tmp_ctx = talloc_new(ctx);
-       data_fmt = talloc_bstrndup(tmp_ctx, p, q - p);
-       p = q + 1;
-
-       {
-               VALUE_FROM_FMT(tmp_ctx, data_p, data_len, request, data_fmt);
-       }
-       {
-               VALUE_FROM_FMT(tmp_ctx, key_p, key_len, request, p);
-       }
-
-       fr_hmac_sha1(digest, data_p, data_len, key_p, key_len);
-
-       talloc_free(tmp_ctx);
-
-       return fr_bin2hex(*out, digest, sizeof(digest));
-}
-
-/** Encode attributes as a series of string attribute/value pairs
- *
- * This is intended to serialize one or more attributes as a comma
- * delimited string.
- *
- * Example: "%{pairs:request:}" == "User-Name = 'foo', User-Password = 'bar'"
- */
-static ssize_t pairs_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,
-                         UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                         REQUEST *request, char const *fmt)
-{
-       vp_tmpl_t       *vpt = NULL;
-       fr_cursor_t     cursor;
-       size_t          len, freespace = outlen;
-       char            *p = *out;
-
-       VALUE_PAIR *vp;
-
-       if (tmpl_afrom_attr_str(ctx, &vpt, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) <= 0) {
-               RPEDEBUG("Invalid input");
-               return -1;
-       }
-
-       for (vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
-            vp;
-            vp = fr_cursor_next(&cursor)) {
-               FR_TOKEN op = vp->op;
-
-               vp->op = T_OP_EQ;
-               len = fr_pair_snprint(p, freespace, vp);
-               vp->op = op;
-
-               if (is_truncated(len, freespace)) {
-               no_space:
-                       talloc_free(vpt);
-                       REDEBUG("Insufficient space to store pair string, needed %zu bytes have %zu bytes",
-                               (p - *out) + len, outlen);
-                       return -1;
-               }
-               p += len;
-               freespace -= len;
-
-               if (freespace < 2) {
-                       len = 2;
-                       goto no_space;
-               }
-
-               *p++ = ',';
-               *p++ = ' ';
-               freespace -= 2;
-       }
-
-       /* Trim the trailing ', ' */
-       if (p != *out) p -= 2;
-       *p = '\0';
-       talloc_free(vpt);
-
-       return (p - *out);
-}
-
-/** Encode string or attribute as base64
- *
- * Example: "%{base64:foo}" == "Zm9v"
- */
-static ssize_t base64_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                          UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                          REQUEST *request, char const *fmt)
-{
-       size_t          inlen;
-       uint8_t         *p;
-       TALLOC_CTX      *tmp_ctx = NULL;
-       ssize_t         ret;
-
-       VALUE_FROM_FMT(tmp_ctx, p, inlen, request, fmt);
-
-       /*
-        *  We can accurately calculate the length of the output string
-        *  if it's larger than outlen, the output would be useless so abort.
-        */
-       if ((FR_BASE64_ENC_LENGTH(inlen) + 1) > outlen) {
-               REDEBUG("xlat failed");
-
-               talloc_free(tmp_ctx);
-
-               return -1;
-       }
-
-       ret = fr_base64_encode(*out, outlen, p, inlen);
-       talloc_free(tmp_ctx);
-
-       return ret;
-}
-
-/** Convert base64 to hex
- *
- * Example: "%{base64tohex:Zm9v}" == "666f6f"
- */
-static ssize_t base64_to_hex_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                                 UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                                 REQUEST *request, char const *fmt)
-{
-       uint8_t decbuf[1024];
-
-       ssize_t declen;
-       ssize_t len = strlen(fmt);
-
-       declen = fr_base64_decode(decbuf, sizeof(decbuf), fmt, len);
-       if (declen < 0) {
-               REDEBUG("Base64 string invalid");
-               return -1;
-       }
-
-       if ((size_t)((declen * 2) + 1) > outlen) {
-               REDEBUG("Base64 conversion failed, output buffer exhausted, needed %zd bytes, have %zd bytes",
-                       (declen * 2) + 1, outlen);
-               return -1;
-       }
-
-       return fr_bin2hex(*out, decbuf, declen);
-}
-
-/** Split an attribute into multiple new attributes based on a delimiter
- *
- * @todo should support multibyte delimiter for string types.
- *
- * Example: "%{explode:&ref <delim>}"
- */
-static ssize_t explode_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,
-                           UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                           REQUEST *request, char const *fmt)
-{
-       vp_tmpl_t       *vpt = NULL;
-       VALUE_PAIR      *vp;
-       fr_cursor_t     cursor, to_merge;
-       VALUE_PAIR      *head = NULL;
-       ssize_t         slen;
-       int             count = 0;
-       char const      *p = fmt;
-       char            delim;
-
-       /*
-        *  Trim whitespace
-        */
-       while (isspace(*p) && p++);
-
-       slen = tmpl_afrom_attr_substr(ctx, &vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
-       if (slen <= 0) {
-               RPEDEBUG("Invalid input");
-               return -1;
-       }
-
-       p += slen;
-
-       if (*p++ != ' ') {
-       arg_error:
-               talloc_free(vpt);
-               REDEBUG("explode needs exactly two arguments: &ref <delim>");
-               return -1;
-       }
-
-       if (*p == '\0') goto arg_error;
-
-       delim = *p;
-
-       fr_cursor_init(&to_merge, &head);
-
-       vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
-       while (vp) {
-               VALUE_PAIR *nvp;
-               char const *end;
-               char const *q;
-
-               /*
-                *      This can theoretically operate on lists too
-                *      so we need to check the type of each attribute.
-                */
-               switch (vp->vp_type) {
-               case FR_TYPE_OCTETS:
-               case FR_TYPE_STRING:
-                       break;
-
-               default:
-                       goto next;
-               }
-
-               p = vp->vp_ptr;
-               end = p + vp->vp_length;
-               while (p < end) {
-                       q = memchr(p, delim, end - p);
-                       if (!q) {
-                               /* Delimiter not present in attribute */
-                               if (p == vp->vp_ptr) goto next;
-                               q = end;
-                       }
-
-                       /* Skip zero length */
-                       if (q == p) {
-                               p = q + 1;
-                               continue;
-                       }
-
-                       nvp = fr_pair_afrom_da(talloc_parent(vp), vp->da);
-                       if (!nvp) {
-                               fr_pair_list_free(&head);
-                               return -1;
-                       }
-                       nvp->tag = vp->tag;
-
-                       switch (vp->vp_type) {
-                       case FR_TYPE_OCTETS:
-                       {
-                               uint8_t *buff;
-
-                               buff = talloc_array(nvp, uint8_t, q - p);
-                               memcpy(buff, p, q - p);
-                               fr_pair_value_memsteal(nvp, buff);
-                       }
-                               break;
-
-                       case FR_TYPE_STRING:
-                       {
-                               char *buff;
-
-                               buff = talloc_array(nvp, char, (q - p) + 1);
-                               memcpy(buff, p, q - p);
-                               buff[q - p] = '\0';
-                               fr_pair_value_strsteal(nvp, (char *)buff);
-                       }
-                               break;
-
-                       default:
-                               rad_assert(0);
-                       }
-
-                       fr_cursor_append(&to_merge, nvp);
-
-                       p = q + 1;      /* next */
-
-                       count++;
-               }
-
-               /*
-                *      Remove the unexploded version
-                */
-               vp = fr_cursor_remove(&cursor);
-               talloc_free(vp);
-               /*
-                *      Remove sets cursor->current to
-                *      the next iter value.
-                */
-               vp = fr_cursor_current(&cursor);
-               continue;
-
-       next:
-               vp = fr_cursor_next(&cursor);
-       }
-
-       fr_cursor_head(&to_merge);
-       fr_cursor_merge(&cursor, &to_merge);
-       talloc_free(vpt);
-
-       return snprintf(*out, outlen, "%i", count);
-}
-
-/** Calculate number of seconds until the next n hour(s), day(s), week(s), year(s).
- *
- * For example, if it were 16:18 %{nexttime:1h} would expand to 2520.
- *
- * The envisaged usage for this function is to limit sessions so that they don't
- * cross billing periods. The output of the xlat should be combined with %{rand:} to create
- * some jitter, unless the desired effect is every subscriber on the network
- * re-authenticating at the same time.
- */
-static ssize_t next_time_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
-                             UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                             REQUEST *request, char const *fmt)
-{
-       long            num;
-
-       char const      *p;
-       char            *q;
-       time_t          now;
-       struct tm       *local, local_buff;
-
-       now = time(NULL);
-       local = localtime_r(&now, &local_buff);
-
-       p = fmt;
-
-       num = strtoul(p, &q, 10);
-       if (!q || *q == '\0') {
-               REDEBUG("nexttime: <int> must be followed by period specifier (h|d|w|m|y)");
-               return -1;
-       }
-
-       if (p == q) {
-               num = 1;
-       } else {
-               p += q - p;
-       }
-
-       local->tm_sec = 0;
-       local->tm_min = 0;
-
-       switch (*p) {
-       case 'h':
-               local->tm_hour += num;
-               break;
-
-       case 'd':
-               local->tm_hour = 0;
-               local->tm_mday += num;
-               break;
-
-       case 'w':
-               local->tm_hour = 0;
-               local->tm_mday += (7 - local->tm_wday) + (7 * (num-1));
-               break;
-
-       case 'm':
-               local->tm_hour = 0;
-               local->tm_mday = 1;
-               local->tm_mon += num;
-               break;
-
-       case 'y':
-               local->tm_hour = 0;
-               local->tm_mday = 1;
-               local->tm_mon = 0;
-               local->tm_year += num;
-               break;
-
-       default:
-               REDEBUG("nexttime: Invalid period specifier '%c', must be h|d|w|m|y", *p);
-               return -1;
-       }
-
-       return snprintf(*out, outlen, "%" PRIu64, (uint64_t)(mktime(local) - now));
-}
-
-
-/** Parse the 3 arguments to lpad / rpad.
- *
- * Parses a fmt string with the components @verbatim <tmpl> <pad_len> <pad_char>@endverbatim
- *
- * @param[out] vpt_p           Template to retrieve value to pad.
- * @param[out] pad_len_p       Length the string needs to be padded to.
- * @param[out] pad_char_p      Char to use for padding.
- * @param[in] request          The current request.
- * @param[in] fmt              string to parse.
- *
- * @return
- *     - <= 0 the negative offset the parse error ocurred at.
- *     - >0 how many bytes of fmt were parsed.
- */
-static ssize_t parse_pad(vp_tmpl_t **vpt_p, size_t *pad_len_p, char *pad_char_p, REQUEST *request, char const *fmt)
-{
-       ssize_t         slen;
-       unsigned long   pad_len;
-       char const      *p;
-       char            *end;
-       vp_tmpl_t       *vpt;
-
-       *pad_char_p = ' ';              /* the default */
-
-       *vpt_p = NULL;
-
-       p = fmt;
-       while (isspace((int) *p)) p++;
-
-       if (*p != '&') {
-               RDEBUG("First argument must be an attribute reference");
-               return 0;
-       }
-
-       slen = tmpl_afrom_attr_substr(request, &vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
-       if (slen <= 0) {
-               RPEDEBUG("Failed parsing input string");
-               return slen;
-       }
-
-       p = fmt + slen;
-
-       while (isspace((int) *p)) p++;
-
-       pad_len = strtoul(p, &end, 10);
-       if ((pad_len == ULONG_MAX) || (pad_len > 8192)) {
-               talloc_free(vpt);
-               RDEBUG("Invalid pad_len found at: %s", p);
-               return fmt - p;
-       }
-
-       p += (end - p);
-
-       /*
-        *      The pad_char_p character is optional.
-        *
-        *      But we must have a space after the previous number,
-        *      and we must have only ONE pad_char_p character.
-        */
-       if (*p) {
-               if (!isspace(*p)) {
-                       talloc_free(vpt);
-                       RDEBUG("Invalid text found at: %s", p);
-                       return fmt - p;
-               }
-
-               while (isspace((int) *p)) p++;
-
-               if (p[1] != '\0') {
-                       talloc_free(vpt);
-                       RDEBUG("Invalid text found at: %s", p);
-                       return fmt - p;
-               }
-
-               *pad_char_p = *p++;
-       }
-
-       *vpt_p = vpt;
-       *pad_len_p = pad_len;
-
-       return p - fmt;
-}
-
-
-/** left pad a string
- *
- *  %{lpad:&Attribute-Name length 'x'}
- */
-static ssize_t lpad_xlat(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
-                        UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                        REQUEST *request, char const *fmt)
-{
-       char            fill;
-       size_t          pad;
-       ssize_t         len;
-       vp_tmpl_t       *vpt;
-       char            *to_pad = NULL;
-
-       if (parse_pad(&vpt, &pad, &fill, request, fmt) <= 0) return 0;
-
-       if (!fr_cond_assert(vpt)) return 0;
-
-       /*
-        *      Print the attribute (left justified).  If it's too
-        *      big, we're done.
-        */
-       len = tmpl_aexpand(ctx, &to_pad, request, vpt, NULL, NULL);
-       if (len <= 0) return -1;
-
-       /*
-        *      Already big enough, no padding required...
-        */
-       if ((size_t) len >= pad) {
-               *out = to_pad;
-               return pad;
-       }
-
-       /*
-        *      Realloc is actually pretty cheap in most cases...
-        */
-       MEM(to_pad = talloc_realloc(ctx, to_pad, char, pad + 1));
-
-       /*
-        *      We have to shift the string to the right, and pad with
-        *      "fill" characters.
-        */
-       memmove(to_pad + (pad - len), to_pad, len + 1);
-       memset(to_pad, fill, pad - len);
-
-       *out = to_pad;
-
-       return pad;
-}
-
-/** right pad a string
- *
- *  %{rpad:&Attribute-Name length 'x'}
- */
-static ssize_t rpad_xlat(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
-                        UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                        REQUEST *request, char const *fmt)
-{
-       char            fill;
-       size_t          pad;
-       ssize_t         len;
-       vp_tmpl_t       *vpt;
-       char            *to_pad = NULL;
-
-       rad_assert(!*out);
-
-       if (parse_pad(&vpt, &pad, &fill, request, fmt) <= 0) return 0;
-
-       if (!fr_cond_assert(vpt)) return 0;
-
-       /*
-        *      Print the attribute (left justified).  If it's too
-        *      big, we're done.
-        */
-       len = tmpl_aexpand(ctx, &to_pad, request, vpt, NULL, NULL);
-       if (len <= 0) return 0;
-
-       if ((size_t) len >= pad) {
-               *out = to_pad;
-               return pad;
-       }
-
-       MEM(to_pad = talloc_realloc(ctx, to_pad, char, pad + 1));
-
-       /*
-        *      We have to pad with "fill" characters.
-        */
-       memset(to_pad + len, fill, pad - len);
-       to_pad[pad] = '\0';
-
-       *out = to_pad;
-
-       return pad;
-}
-
-
-/*
- *     Do any per-module initialization that is separate to each
- *     configured instance of the module.  e.g. set up connections
- *     to external databases, read configuration files, set up
- *     dictionary entries, etc.
- *
- *     If configuration information is given in the config section
- *     that must be referenced in later calls, store a handle to it
- *     in *instance otherwise put a null pointer there.
- */
-static int mod_bootstrap(void *instance, CONF_SECTION *conf)
-{
-       rlm_expr_t *inst = instance;
-
-       inst->xlat_name = cf_section_name2(conf);
-       if (!inst->xlat_name) {
-               inst->xlat_name = cf_section_name1(conf);
+       inst->xlat_name = cf_section_name2(conf);
+       if (!inst->xlat_name) {
+               inst->xlat_name = cf_section_name1(conf);
        }
 
        xlat_register(inst, inst->xlat_name, expr_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-
-       xlat_register(inst, "rand", rand_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "randstr", randstr_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "urlquote", urlquote_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "urlunquote", urlunquote_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "escape", escape_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "unescape", unescape_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "tolower", tolower_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "toupper", toupper_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "md5", md5_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "sha1", sha1_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-#ifdef HAVE_OPENSSL_EVP_H
-       xlat_register(inst, "sha256", sha256_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "sha512", sha512_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-#endif
-       xlat_register(inst, "hmacmd5", hmac_md5_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "hmacsha1", hmac_sha1_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "pairs", pairs_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-
-       xlat_register(inst, "base64", base64_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "base64tohex", base64_to_hex_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-
-       xlat_register(inst, "explode", explode_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-
-       xlat_register(inst, "nexttime", next_time_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
-       xlat_register(inst, "lpad", lpad_xlat, NULL, NULL, 0, 0, true);
-       xlat_register(inst, "rpad", rpad_xlat, NULL, NULL, 0, 0, true);
+       xlat_register(NULL, "escape", escape_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+       xlat_register(NULL, "unescape", unescape_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
 
        /*
         *      Initialize various paircompare functions