]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add base64 encoding/decoding library
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sun, 14 Oct 2012 19:59:31 +0000 (20:59 +0100)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 16 Oct 2012 09:56:24 +0000 (10:56 +0100)
Add %{base64:} expansion to encode the raw octets of an attribute

Add %{strtobase64:} expansion to encode a string to base64

Add %{base64tostr:} expansion to decode a base64 string

src/include/Makefile
src/include/base64.h [new file with mode: 0644]
src/lib/Makefile
src/lib/base64.c [new file with mode: 0644]
src/main/xlat.c
src/modules/rlm_expr/rlm_expr.c

index f30450f08f0cc1e3fa129f237f88b7a277997b18..d666f3ae5873780eaed389c5cbabe1b5cba0a22f 100644 (file)
@@ -8,7 +8,7 @@ HEADERS = autoconf.h conf.h conffile.h detail.h dhcp.h event.h hash.h heap.h \
        ident.h libradius.h md4.h md5.h missing.h modcall.h modules.h \
        packet.h rad_assert.h radius.h radiusd.h radpaths.h \
        radutmp.h realms.h sha1.h stats.h sysutmp.h token.h \
-       udpfromto.h vmps.h vqp.h
+       udpfromto.h vmps.h vqp.h base64.h
 
 include ../../Make.inc
 .PHONY: all clean distclean install
diff --git a/src/include/base64.h b/src/include/base64.h
new file mode 100644 (file)
index 0000000..de294a9
--- /dev/null
@@ -0,0 +1,46 @@
+/* base64.h -- Encode binary data using printable characters.
+   Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
+   Written by Simon Josefsson.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#ifndef _FR_BASE64_H
+# define _FR_BASE64_H
+
+/* Get size_t. */
+# include <stddef.h>
+
+/* Get bool. */
+# include <stdbool.h>
+
+/* This uses that the expression (n+(k-1))/k means the smallest
+   integer >= n/k, i.e., the ceiling of n/k.  */
+# define FR_BASE64_ENC_LENGTH(inlen) ((((inlen) + 2) / 3) * 4)
+# define FR_BASE64_DEC_LENGTH(inlen) ((3 * (inlen / 4)) + 2)
+
+extern bool fr_isbase64 (char ch);
+
+extern void fr_base64_encode (const char *in, size_t inlen,
+                             char *out, size_t outlen);
+
+extern size_t fr_base64_encode_alloc (const char *in, size_t inlen, char **out);
+
+extern bool fr_base64_decode (const char *in, size_t inlen,
+                             char *out, size_t *outlen);
+
+extern bool fr_base64_decode_alloc (const char *in, size_t inlen,
+                                   char **out, size_t *outlen);
+
+#endif /* BASE64_H */
\ No newline at end of file
index 286cc02c1ba8293e412691328f81c06e7e9af145..ee72f786b28669751a3cd7752f0de1e4c7adeb82 100644 (file)
@@ -10,7 +10,7 @@ SRCS          = dict.c filters.c hash.c hmac.c hmacsha1.c isaac.c log.c \
                  misc.c missing.c md4.c md5.c print.c radius.c rbtree.c \
                  sha1.c snprintf.c strlcat.c strlcpy.c token.c udpfromto.c \
                  valuepair.c fifo.c packet.c event.c getaddrinfo.c vqp.c \
-                 heap.c dhcp.c
+                 heap.c dhcp.c base64.c
 
 LT_OBJS                = $(SRCS:.c=.lo)
 
diff --git a/src/lib/base64.c b/src/lib/base64.c
new file mode 100644 (file)
index 0000000..69796a0
--- /dev/null
@@ -0,0 +1,425 @@
+/* base64.c -- Encode binary data using printable characters.
+   Copyright (C) 1999, 2000, 2001, 2004, 2005, 2006 Free Software
+   Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Written by Simon Josefsson.  Partially adapted from GNU MailUtils
+ * (mailbox/filter_trans.c, as of 2004-11-28).  Improved by review
+ * from Paul Eggert, Bruno Haible, and Stepan Kasal.
+ *
+ * See also RFC 3548 <http://www.ietf.org/rfc/rfc3548.txt>.
+ *
+ * Be careful with error checking.  Here is how you would typically
+ * use these functions:
+ *
+ * bool ok = fr_base64_decode_alloc (in, inlen, &out, &outlen);
+ * if (!ok)
+ *   FAIL: input was not valid base64
+ * if (out == NULL)
+ *   FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN
+ *
+ * size_t outlen = fr_base64_encode_alloc (in, inlen, &out);
+ * if (out == NULL && outlen == 0 && inlen != 0)
+ *   FAIL: input too long
+ * if (out == NULL)
+ *   FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN.
+ *
+ */
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+/* Get prototype. */
+#include <freeradius-devel/base64.h>
+
+/* Get malloc. */
+#include <stdlib.h>
+
+/* Get UCHAR_MAX. */
+#include <limits.h>
+
+/* C89 compliant way to cast 'char' to 'unsigned char'. */
+static inline unsigned char
+to_uchar (char ch)
+{
+  return ch;
+}
+
+/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN.
+   If OUTLEN is less than FR_BASE64_ENC_LENGTH(INLEN), write as many bytes as
+   possible.  If OUTLEN is larger than FR_BASE64_ENC_LENGTH(INLEN), also zero
+   terminate the output buffer. */
+void
+fr_base64_encode (const char *in, size_t inlen,
+                 char *out, size_t outlen)
+{
+  static const char b64str[64] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+  while (inlen && outlen)
+    {
+      *out++ = b64str[(to_uchar (in[0]) >> 2) & 0x3f];
+      if (!--outlen)
+       break;
+      *out++ = b64str[((to_uchar (in[0]) << 4)
+                      + (--inlen ? to_uchar (in[1]) >> 4 : 0))
+                     & 0x3f];
+      if (!--outlen)
+       break;
+      *out++ =
+       (inlen
+        ? b64str[((to_uchar (in[1]) << 2)
+                  + (--inlen ? to_uchar (in[2]) >> 6 : 0))
+                 & 0x3f]
+        : '=');
+      if (!--outlen)
+       break;
+      *out++ = inlen ? b64str[to_uchar (in[2]) & 0x3f] : '=';
+      if (!--outlen)
+       break;
+      if (inlen)
+       inlen--;
+      if (inlen)
+       in += 3;
+    }
+
+  if (outlen)
+    *out = '\0';
+}
+
+/* Allocate a buffer and store zero terminated base64 encoded data
+   from array IN of size INLEN, returning FR_BASE64_ENC_LENGTH(INLEN), i.e.,
+   the length of the encoded data, excluding the terminating zero.  On
+   return, the OUT variable will hold a pointer to newly allocated
+   memory that must be deallocated by the caller.  If output string
+   length would overflow, 0 is returned and OUT is set to NULL.  If
+   memory allocation failed, OUT is set to NULL, and the return value
+   indicates length of the requested memory block, i.e.,
+   FR_BASE64_ENC_LENGTH(inlen) + 1. */
+size_t
+fr_base64_encode_alloc (const char *in, size_t inlen, char **out)
+{
+  size_t outlen = 1 + FR_BASE64_ENC_LENGTH (inlen);
+
+  /* Check for overflow in outlen computation.
+   *
+   * If there is no overflow, outlen >= inlen.
+   *
+   * If the operation (inlen + 2) overflows then it yields at most +1, so
+   * outlen is 0.
+   *
+   * If the multiplication overflows, we lose at least half of the
+   * correct value, so the result is < ((inlen + 2) / 3) * 2, which is
+   * less than (inlen + 2) * 0.66667, which is less than inlen as soon as
+   * (inlen > 4).
+   */
+  if (inlen > outlen)
+    {
+      *out = NULL;
+      return 0;
+    }
+
+  *out = malloc (outlen);
+  if (!*out)
+    return outlen;
+
+  fr_base64_encode (in, inlen, *out, outlen);
+
+  return outlen - 1;
+}
+
+/* With this approach this file works independent of the charset used
+   (think EBCDIC).  However, it does assume that the characters in the
+   Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255.  POSIX
+   1003.1-2001 require that char and unsigned char are 8-bit
+   quantities, though, taking care of that problem.  But this may be a
+   potential problem on non-POSIX C99 platforms.
+
+   IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_"
+   as the formal parameter rather than "x".  */
+#define B64(_)                                 \
+  ((_) == 'A' ? 0                              \
+   : (_) == 'B' ? 1                            \
+   : (_) == 'C' ? 2                            \
+   : (_) == 'D' ? 3                            \
+   : (_) == 'E' ? 4                            \
+   : (_) == 'F' ? 5                            \
+   : (_) == 'G' ? 6                            \
+   : (_) == 'H' ? 7                            \
+   : (_) == 'I' ? 8                            \
+   : (_) == 'J' ? 9                            \
+   : (_) == 'K' ? 10                           \
+   : (_) == 'L' ? 11                           \
+   : (_) == 'M' ? 12                           \
+   : (_) == 'N' ? 13                           \
+   : (_) == 'O' ? 14                           \
+   : (_) == 'P' ? 15                           \
+   : (_) == 'Q' ? 16                           \
+   : (_) == 'R' ? 17                           \
+   : (_) == 'S' ? 18                           \
+   : (_) == 'T' ? 19                           \
+   : (_) == 'U' ? 20                           \
+   : (_) == 'V' ? 21                           \
+   : (_) == 'W' ? 22                           \
+   : (_) == 'X' ? 23                           \
+   : (_) == 'Y' ? 24                           \
+   : (_) == 'Z' ? 25                           \
+   : (_) == 'a' ? 26                           \
+   : (_) == 'b' ? 27                           \
+   : (_) == 'c' ? 28                           \
+   : (_) == 'd' ? 29                           \
+   : (_) == 'e' ? 30                           \
+   : (_) == 'f' ? 31                           \
+   : (_) == 'g' ? 32                           \
+   : (_) == 'h' ? 33                           \
+   : (_) == 'i' ? 34                           \
+   : (_) == 'j' ? 35                           \
+   : (_) == 'k' ? 36                           \
+   : (_) == 'l' ? 37                           \
+   : (_) == 'm' ? 38                           \
+   : (_) == 'n' ? 39                           \
+   : (_) == 'o' ? 40                           \
+   : (_) == 'p' ? 41                           \
+   : (_) == 'q' ? 42                           \
+   : (_) == 'r' ? 43                           \
+   : (_) == 's' ? 44                           \
+   : (_) == 't' ? 45                           \
+   : (_) == 'u' ? 46                           \
+   : (_) == 'v' ? 47                           \
+   : (_) == 'w' ? 48                           \
+   : (_) == 'x' ? 49                           \
+   : (_) == 'y' ? 50                           \
+   : (_) == 'z' ? 51                           \
+   : (_) == '0' ? 52                           \
+   : (_) == '1' ? 53                           \
+   : (_) == '2' ? 54                           \
+   : (_) == '3' ? 55                           \
+   : (_) == '4' ? 56                           \
+   : (_) == '5' ? 57                           \
+   : (_) == '6' ? 58                           \
+   : (_) == '7' ? 59                           \
+   : (_) == '8' ? 60                           \
+   : (_) == '9' ? 61                           \
+   : (_) == '+' ? 62                           \
+   : (_) == '/' ? 63                           \
+   : -1)
+
+static const signed char b64[0x100] = {
+  B64 (0), B64 (1), B64 (2), B64 (3),
+  B64 (4), B64 (5), B64 (6), B64 (7),
+  B64 (8), B64 (9), B64 (10), B64 (11),
+  B64 (12), B64 (13), B64 (14), B64 (15),
+  B64 (16), B64 (17), B64 (18), B64 (19),
+  B64 (20), B64 (21), B64 (22), B64 (23),
+  B64 (24), B64 (25), B64 (26), B64 (27),
+  B64 (28), B64 (29), B64 (30), B64 (31),
+  B64 (32), B64 (33), B64 (34), B64 (35),
+  B64 (36), B64 (37), B64 (38), B64 (39),
+  B64 (40), B64 (41), B64 (42), B64 (43),
+  B64 (44), B64 (45), B64 (46), B64 (47),
+  B64 (48), B64 (49), B64 (50), B64 (51),
+  B64 (52), B64 (53), B64 (54), B64 (55),
+  B64 (56), B64 (57), B64 (58), B64 (59),
+  B64 (60), B64 (61), B64 (62), B64 (63),
+  B64 (64), B64 (65), B64 (66), B64 (67),
+  B64 (68), B64 (69), B64 (70), B64 (71),
+  B64 (72), B64 (73), B64 (74), B64 (75),
+  B64 (76), B64 (77), B64 (78), B64 (79),
+  B64 (80), B64 (81), B64 (82), B64 (83),
+  B64 (84), B64 (85), B64 (86), B64 (87),
+  B64 (88), B64 (89), B64 (90), B64 (91),
+  B64 (92), B64 (93), B64 (94), B64 (95),
+  B64 (96), B64 (97), B64 (98), B64 (99),
+  B64 (100), B64 (101), B64 (102), B64 (103),
+  B64 (104), B64 (105), B64 (106), B64 (107),
+  B64 (108), B64 (109), B64 (110), B64 (111),
+  B64 (112), B64 (113), B64 (114), B64 (115),
+  B64 (116), B64 (117), B64 (118), B64 (119),
+  B64 (120), B64 (121), B64 (122), B64 (123),
+  B64 (124), B64 (125), B64 (126), B64 (127),
+  B64 (128), B64 (129), B64 (130), B64 (131),
+  B64 (132), B64 (133), B64 (134), B64 (135),
+  B64 (136), B64 (137), B64 (138), B64 (139),
+  B64 (140), B64 (141), B64 (142), B64 (143),
+  B64 (144), B64 (145), B64 (146), B64 (147),
+  B64 (148), B64 (149), B64 (150), B64 (151),
+  B64 (152), B64 (153), B64 (154), B64 (155),
+  B64 (156), B64 (157), B64 (158), B64 (159),
+  B64 (160), B64 (161), B64 (162), B64 (163),
+  B64 (164), B64 (165), B64 (166), B64 (167),
+  B64 (168), B64 (169), B64 (170), B64 (171),
+  B64 (172), B64 (173), B64 (174), B64 (175),
+  B64 (176), B64 (177), B64 (178), B64 (179),
+  B64 (180), B64 (181), B64 (182), B64 (183),
+  B64 (184), B64 (185), B64 (186), B64 (187),
+  B64 (188), B64 (189), B64 (190), B64 (191),
+  B64 (192), B64 (193), B64 (194), B64 (195),
+  B64 (196), B64 (197), B64 (198), B64 (199),
+  B64 (200), B64 (201), B64 (202), B64 (203),
+  B64 (204), B64 (205), B64 (206), B64 (207),
+  B64 (208), B64 (209), B64 (210), B64 (211),
+  B64 (212), B64 (213), B64 (214), B64 (215),
+  B64 (216), B64 (217), B64 (218), B64 (219),
+  B64 (220), B64 (221), B64 (222), B64 (223),
+  B64 (224), B64 (225), B64 (226), B64 (227),
+  B64 (228), B64 (229), B64 (230), B64 (231),
+  B64 (232), B64 (233), B64 (234), B64 (235),
+  B64 (236), B64 (237), B64 (238), B64 (239),
+  B64 (240), B64 (241), B64 (242), B64 (243),
+  B64 (244), B64 (245), B64 (246), B64 (247),
+  B64 (248), B64 (249), B64 (250), B64 (251),
+  B64 (252), B64 (253), B64 (254), B64 (255)
+};
+
+#if UCHAR_MAX == 255
+# define uchar_in_range(c) true
+#else
+# define uchar_in_range(c) ((c) <= 255)
+#endif
+
+/* Return true if CH is a character from the Base64 alphabet, and
+   false otherwise.  Note that '=' is padding and not considered to be
+   part of the alphabet.  */
+bool
+fr_isbase64 (char ch)
+{
+  return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)];
+}
+
+/* Decode base64 encoded input array IN of length INLEN to output
+   array OUT that can hold *OUTLEN bytes.  Return true if decoding was
+   successful, i.e. if the input was valid base64 data, false
+   otherwise.  If *OUTLEN is too small, as many bytes as possible will
+   be written to OUT.  On return, *OUTLEN holds the length of decoded
+   bytes in OUT.  Note that as soon as any non-alphabet characters are
+   encountered, decoding is stopped and false is returned.  This means
+   that, when applicable, you must remove any line terminators that is
+   part of the data stream before calling this function.  */
+bool
+fr_base64_decode (const char *in, size_t inlen,
+                 char *out, size_t *outlen)
+{
+  size_t outleft = *outlen;
+
+  while (inlen >= 2)
+    {
+      if (!fr_isbase64 (in[0]) || !fr_isbase64 (in[1]))
+       break;
+
+      if (outleft)
+       {
+         *out++ = ((b64[to_uchar (in[0])] << 2)
+                   | (b64[to_uchar (in[1])] >> 4));
+         outleft--;
+       }
+
+      if (inlen == 2)
+       break;
+
+      if (in[2] == '=')
+       {
+         if (inlen != 4)
+           break;
+
+         if (in[3] != '=')
+           break;
+
+       }
+      else
+       {
+         if (!fr_isbase64 (in[2]))
+           break;
+
+         if (outleft)
+           {
+             *out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0)
+                       | (b64[to_uchar (in[2])] >> 2));
+             outleft--;
+           }
+
+         if (inlen == 3)
+           break;
+
+         if (in[3] == '=')
+           {
+             if (inlen != 4)
+               break;
+           }
+         else
+           {
+             if (!fr_isbase64 (in[3]))
+               break;
+
+             if (outleft)
+               {
+                 *out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0)
+                           | b64[to_uchar (in[3])]);
+                 outleft--;
+               }
+           }
+       }
+
+      in += 4;
+      inlen -= 4;
+    }
+
+  *outlen -= outleft;
+
+  if (inlen != 0)
+    return false;
+
+  return true;
+}
+
+/* Allocate an output buffer in *OUT, and decode the base64 encoded
+   data stored in IN of size INLEN to the *OUT buffer.  On return, the
+   size of the decoded data is stored in *OUTLEN.  OUTLEN may be NULL,
+   if the caller is not interested in the decoded length.  *OUT may be
+   NULL to indicate an out of memory error, in which case *OUTLEN
+   contains the size of the memory block needed.  The function returns
+   true on successful decoding and memory allocation errors.  (Use the
+   *OUT and *OUTLEN parameters to differentiate between successful
+   decoding and memory error.)  The function returns false if the
+   input was invalid, in which case *OUT is NULL and *OUTLEN is
+   undefined. */
+bool
+fr_base64_decode_alloc (const char *in, size_t inlen, char **out,
+                       size_t *outlen)
+{
+  /* This may allocate a few bytes too much, depending on input,
+     but it's not worth the extra CPU time to compute the exact amount.
+     The exact amount is 3 * inlen / 4, minus 1 if the input ends
+     with "=" and minus another 1 if the input ends with "==".
+     Dividing before multiplying avoids the possibility of overflow.  */
+  size_t needlen = FR_BASE64_DEC_LENGTH(inlen);
+
+  *out = malloc (needlen);
+  if (!*out)
+    return true;
+
+  if (!fr_base64_decode (in, inlen, *out, &needlen))
+    {
+      free (*out);
+      *out = NULL;
+      return false;
+    }
+
+  if (outlen)
+    *outlen = needlen;
+
+  return true;
+}
\ No newline at end of file
index 6e9dddb2456b46a5c6d1e00cbf5b1742fa2be390..42c4fdffd611a048d6039941fba773ff82a321c6 100644 (file)
@@ -27,6 +27,7 @@ RCSID("$Id$")
 
 #include       <freeradius-devel/radiusd.h>
 #include       <freeradius-devel/rad_assert.h>
+#include       <freeradius-devel/base64.h>
 
 #include       <ctype.h>
 
@@ -567,6 +568,37 @@ static size_t xlat_hex(UNUSED void *instance, REQUEST *request,
        return vp->length * 2;
 }
 
+/**
+ * @brief Print data as base64, not as VALUE
+ */
+static size_t xlat_base64(UNUSED void *instance, REQUEST *request,
+                         const char *fmt, char *out, size_t outlen)
+{
+       VALUE_PAIR *vp;
+       size_t len;
+       
+       while (isspace((int) *fmt)) fmt++;
+
+       if (!radius_get_vp(request, fmt, &vp) || !vp) {
+               *out = '\0';
+               return 0;
+       }
+       
+       len = FR_BASE64_ENC_LENGTH(vp->length);
+       
+       /*
+        *      Don't truncate the data.
+        */
+       if (outlen < (len + 1)) {
+               *out = 0;
+               return 0;
+       }
+       
+       fr_base64_encode((char *) vp->vp_octets, vp->length, out, outlen);
+
+       return len;
+}
+
 #ifdef HAVE_REGEX_H
 /*
  *     Pull %{0} to %{8} out of the packet.
@@ -722,6 +754,11 @@ int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance)
                c = xlat_find("integer");
                rad_assert(c != NULL);
                c->internal = TRUE;
+               
+               xlat_register("base64", xlat_hex, "");
+               c = xlat_find("base64");
+               rad_assert(c != NULL);
+               c->internal = TRUE;
 
                xlat_register("string", xlat_string, "");
                c = xlat_find("string");
index 10411d042a40ad7897b1174bd1e4960193aaafbd..05a00f184a41b8eb63c5e690177a3783f1df8fe5 100644 (file)
@@ -26,6 +26,7 @@ RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/md5.h>
+#include <freeradius-devel/base64.h>
 #include <freeradius-devel/modules.h>
 
 #include <ctype.h>
@@ -302,7 +303,7 @@ static size_t rand_xlat(void *instance, REQUEST *request, char *fmt,
  *  @brief Generate a string of random chars
  *
  *  Build strings of random chars, useful for generating tokens and passcodes
- *  Format identical to String::Random.
+ *  Format similar to String::Random.
  */
 static size_t randstr_xlat(UNUSED void *instance, REQUEST *request,
                           const char *fmt, char *out, size_t outlen,
@@ -564,6 +565,90 @@ static size_t md5_xlat(UNUSED void *instance, REQUEST *request,
        return strlen(out);
 }
 
+/**
+ * @brief Encode string as base64
+ *
+ * Example: "%{strtobase64:foo}" == "Zm9v"
+ */
+static size_t base64_encode_xlat(UNUSED void *instance, REQUEST *request,
+                                const char *fmt, char *out, size_t outlen)
+{
+       size_t len;
+       char buffer[1024];
+
+       len = radius_xlat(buffer, sizeof(buffer), fmt, request, NULL, NULL);
+       
+       /* 
+        *  We can accurately calculate the length of the output string
+        *  if it's larger than outlen, the output would be useless so abort.
+        */
+       if (!len || ((FR_BASE64_ENC_LENGTH(len) + 1) > outlen)) {
+               radlog(L_ERR, "rlm_expr: xlat failed.");
+               *out = '\0';
+               return 0;
+       }
+       
+       fr_base64_encode(buffer, len, out, outlen);
+
+       return strlen(out);
+}
+
+/**
+ * @brief Decode base64 string
+ *
+ * Example: "%{base64tostr:Zm9v}" == "foo"
+ */
+static size_t base64_decode_xlat(UNUSED void *instance, REQUEST *request,
+                                const char *fmt, char *out, size_t outlen)
+{      
+       char *p;
+       
+       char buffer[1024];
+       char decbuf[1024];
+       
+       size_t declen = sizeof(decbuf);
+       size_t freespace = outlen;
+       size_t len;
+
+       len = radius_xlat(buffer, sizeof(buffer), fmt, request, NULL, NULL);
+       
+       if (!len) {
+               radlog(L_ERR, "rlm_expr: xlat failed.");
+               *out = '\0';
+               return 0;
+       }
+       
+       if (!fr_base64_decode(buffer, len, decbuf, &declen)) {
+               radlog(L_ERR, "rlm_expr: base64 string invalid");
+               *out = '\0';
+               return 0;
+       }
+       
+       p = decbuf;
+       while ((declen-- > 0) && (--freespace > 0)) {
+               /*
+                *      Non-printable characters get replaced with their
+                *      mime-encoded equivalents.
+                */
+               if (*p > 31) {
+                       *out++ = *p++;
+                       continue;
+               }
+               
+               if (freespace < 3)
+                       break;
+
+               snprintf(out, 4, "=%02X", *p++);
+               
+               /* Already decremented */
+               freespace -= 2;
+               out += 3;
+       }
+
+       return outlen - freespace;
+}
+
+
 /*
  *     Do any per-module initialization that is separate to each
  *     configured instance of the module.  e.g. set up connections
@@ -602,6 +687,8 @@ static int expr_instantiate(CONF_SECTION *conf, void **instance)
        xlat_register("tolower", lc_xlat, inst);
        xlat_register("toupper", uc_xlat, inst);
        xlat_register("md5", md5_xlat, inst);
+       xlat_register("strtobase64", base64_encode_xlat, inst);
+       xlat_register("base64tostr", base64_decode_xlat, inst);
 
        /*
         * Initialize various paircompare functions