*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * Copyright 2000 The FreeRADIUS server project
+ * Copyright 2000,2006 The FreeRADIUS server project
* Copyright 2000 Alan DeKok <aland@ox.org>
*/
-static const char rcsid[] =
-"$Id$";
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
-#include "autoconf.h"
-#include "libradius.h"
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/md5.h>
+#include <freeradius-devel/rad_assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <ctype.h>
-#include "radiusd.h"
-
-#include "rad_assert.h"
-
typedef struct xlat_t {
char module[MAX_STRING_LEN];
int length;
/*
* Define all xlat's in the structure.
*/
-static const char *internal_xlat[] = {"check",
- "request",
- "reply",
- "proxy-request",
- "proxy-reply",
- NULL};
+static const char * const internal_xlat[] = {"check",
+ "request",
+ "reply",
+ "proxy-request",
+ "proxy-reply",
+ "outer.request",
+ "outer.reply",
+ NULL};
#if REQUEST_MAX_REGEX > 8
#error Please fix the following line
char buffer[MAX_STRING_LEN * 4];
if (pair != NULL) {
- vp_prints_value(buffer, sizeof(buffer), pair, 0);
+ vp_prints_value(buffer, sizeof(buffer), pair, -1);
return func(out, outlen, buffer);
}
switch (type) {
case PW_TYPE_STRING :
- strNcpy(out,"_",outlen);
+ strlcpy(out,"_",outlen);
break;
case PW_TYPE_INTEGER :
- strNcpy(out,"0",outlen);
+ strlcpy(out,"0",outlen);
break;
case PW_TYPE_IPADDR :
- strNcpy(out,"?.?.?.?",outlen);
+ strlcpy(out,"?.?.?.?",outlen);
+ break;
+ case PW_TYPE_IPV6ADDR :
+ strlcpy(out,":?:",outlen);
break;
case PW_TYPE_DATE :
- strNcpy(out,"0",outlen);
+ strlcpy(out,"0",outlen);
break;
default :
- strNcpy(out,"unknown_type",outlen);
+ strlcpy(out,"unknown_type",outlen);
}
return strlen(out);
}
/*
* Dynamically translate for check:, request:, reply:, etc.
*/
-static int xlat_packet(void *instance, REQUEST *request,
- char *fmt, char *out, size_t outlen,
- RADIUS_ESCAPE_STRING func)
+static size_t xlat_packet(void *instance, REQUEST *request,
+ char *fmt, char *out, size_t outlen,
+ RADIUS_ESCAPE_STRING func)
{
DICT_ATTR *da;
VALUE_PAIR *vp;
break;
case 3:
+#ifdef WITH_PROXY
if (request->proxy) vps = request->proxy->vps;
packet = request->proxy;
+#endif
break;
case 4:
+#ifdef WITH_PROXY
if (request->proxy_reply) vps = request->proxy_reply->vps;
packet = request->proxy_reply;
+#endif
break;
+ case 5:
+ if (request->parent) {
+ vps = request->parent->packet->vps;
+ packet = request->parent->packet;
+ }
+ break;
+
+ case 6:
+ if (request->parent && request->parent->reply) {
+ vps = request->parent->reply->vps;
+ packet = request->parent->reply;
+ }
+ break;
+
default: /* WTF? */
return 0;
}
* The "format" string is the attribute name.
*/
da = dict_attrbyname(fmt);
- if (!da) return 0;
+ if (!da) {
+ int do_number = FALSE;
+ size_t count;
+ const char *p;
+ char buffer[256];
+
+ if (strlen(fmt) > sizeof(buffer)) return 0;
+
+ p = strchr(fmt, '[');
+ if (!p) {
+ p = strchr(fmt, '#');
+ if (!p) return 0;
+ do_number = TRUE;
+ }
+
+ strlcpy(buffer, fmt, p - fmt + 1);
+
+ da = dict_attrbyname(buffer);
+ if (!da) return 0;
+
+ if (do_number) {
+ vp = pairfind(vps, da->attr, 0);
+ if (!vp) return 0;
+
+ switch (da->type) {
+ default:
+ break;
+
+ case PW_TYPE_INTEGER:
+ case PW_TYPE_DATE:
+ case PW_TYPE_SHORT:
+ case PW_TYPE_BYTE:
+ snprintf(out, outlen, "%u", vp->lvalue);
+ return strlen(out);
+ }
+
+ goto just_print;
+ }
+
+ /*
+ * %{Attribute-Name[#]} returns the count of
+ * attributes of that name in the list.
+ */
+ if ((p[1] == '#') && (p[2] == ']')) {
+ count = 0;
+
+ for (vp = pairfind(vps, da->attr, da->vendor);
+ vp != NULL;
+ vp = pairfind(vp->next, da->attr, da->vendor)) {
+ count++;
+ }
+ snprintf(out, outlen, "%d", (int) count);
+ return strlen(out);
+ }
+
+ /*
+ * %{Attribute-Name[*]} returns ALL of the
+ * the attributes, separated by a newline.
+ */
+ if ((p[1] == '*') && (p[2] == ']')) {
+ int total = 0;
+
+ for (vp = pairfind(vps, da->attr, da->vendor);
+ vp != NULL;
+ vp = pairfind(vp->next, da->attr, da->vendor)) {
+ count = valuepair2str(out, outlen - 1, vp, da->type, func);
+ rad_assert(count <= outlen);
+ total += count + 1;
+ outlen -= (count + 1);
+ out += count;
+
+ *(out++) = '\n';
+
+ if (outlen == 0) break;
+ }
+
+ return total;
+ }
+
+ count = atoi(p + 1);
+
+ /*
+ * Skip the numbers.
+ */
+ p += 1 + strspn(p + 1, "0123456789");
+ if (*p != ']') {
+ RDEBUG2("xlat: Invalid array reference in string at %s %s",
+ fmt, p);
+ return 0;
+ }
+
+ /*
+ * Find the N'th value.
+ */
+ for (vp = pairfind(vps, da->attr, da->vendor);
+ vp != NULL;
+ vp = pairfind(vp->next, da->attr, da->vendor)) {
+ if (count == 0) break;
+ count--;
+ }
- vp = pairfind(vps, da->attr);
+ /*
+ * Non-existent array reference.
+ */
+ if (!vp) return 0;
+ just_print:
+ return valuepair2str(out, outlen, vp, da->type, func);
+ }
+
+ vp = pairfind(vps, da->attr, da->vendor);
if (!vp) {
/*
* Some "magic" handlers, which are never in VP's, but
* which are in the packet.
*
- * FIXME: Add SRC/DST IP address!
+ * FIXME: We should really do this in a more
+ * intelligent way...
*/
if (packet) {
+ VALUE_PAIR localvp;
+
+ memset(&localvp, 0, sizeof(localvp));
+
switch (da->attr) {
case PW_PACKET_TYPE:
{
DICT_VALUE *dval;
-
- dval = dict_valbyattr(da->attr, packet->code);
+
+ dval = dict_valbyattr(da->attr, da->vendor, packet->code);
if (dval) {
snprintf(out, outlen, "%s", dval->name);
} else {
return strlen(out);
}
break;
-
+
+ case PW_CLIENT_SHORTNAME:
+ if (request->client && request->client->shortname) {
+ strlcpy(out, request->client->shortname, outlen);
+ } else {
+ strlcpy(out, "<UNKNOWN-CLIENT>", outlen);
+ }
+ return strlen(out);
+
+ case PW_CLIENT_IP_ADDRESS: /* the same as below */
+ case PW_PACKET_SRC_IP_ADDRESS:
+ if (packet->src_ipaddr.af != AF_INET) {
+ return 0;
+ }
+ localvp.attribute = da->attr;
+ localvp.vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
+ break;
+
+ case PW_PACKET_DST_IP_ADDRESS:
+ if (packet->dst_ipaddr.af != AF_INET) {
+ return 0;
+ }
+ localvp.attribute = da->attr;
+ localvp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
+ break;
+
+ case PW_PACKET_SRC_PORT:
+ localvp.attribute = da->attr;
+ localvp.vp_integer = packet->src_port;
+ break;
+
+ case PW_PACKET_DST_PORT:
+ localvp.attribute = da->attr;
+ localvp.vp_integer = packet->dst_port;
+ break;
+
+ case PW_PACKET_AUTHENTICATION_VECTOR:
+ localvp.attribute = da->attr;
+ memcpy(localvp.vp_strvalue, packet->vector,
+ sizeof(packet->vector));
+ localvp.length = sizeof(packet->vector);
+ break;
+
+ /*
+ * Authorization, accounting, etc.
+ */
+ case PW_REQUEST_PROCESSING_STAGE:
+ if (request->component) {
+ strlcpy(out, request->component, outlen);
+ } else {
+ strlcpy(out, "server_core", outlen);
+ }
+ return strlen(out);
+
+ case PW_PACKET_SRC_IPV6_ADDRESS:
+ if (packet->src_ipaddr.af != AF_INET6) {
+ return 0;
+ }
+ localvp.attribute = da->attr;
+ memcpy(localvp.vp_strvalue,
+ &packet->src_ipaddr.ipaddr.ip6addr,
+ sizeof(packet->src_ipaddr.ipaddr.ip6addr));
+ break;
+
+ case PW_PACKET_DST_IPV6_ADDRESS:
+ if (packet->dst_ipaddr.af != AF_INET6) {
+ return 0;
+ }
+ localvp.attribute = da->attr;
+ memcpy(localvp.vp_strvalue,
+ &packet->dst_ipaddr.ipaddr.ip6addr,
+ sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
+ break;
+
+ case PW_VIRTUAL_SERVER:
+ if (!request->server) return 0;
+
+ snprintf(out, outlen, "%s", request->server);
+ return strlen(out);
+ break;
+
+ case PW_MODULE_RETURN_CODE:
+ localvp.attribute = da->attr;
+
+ /*
+ * See modcall.c for a bit of a hack.
+ */
+ localvp.vp_integer = request->simul_max;
+ break;
+
default:
+ return 0; /* not found */
break;
}
+
+ localvp.type = da->type;
+ return valuepair2str(out, outlen, &localvp,
+ da->type, func);
}
/*
/*
* Pull %{0} to %{8} out of the packet.
*/
-static int xlat_regex(void *instance, REQUEST *request,
- char *fmt, char *out, size_t outlen,
- RADIUS_ESCAPE_STRING func)
+static size_t xlat_regex(void *instance, REQUEST *request,
+ char *fmt, char *out, size_t outlen,
+ RADIUS_ESCAPE_STRING func)
{
char *regex;
*/
fmt = fmt; /* -Wunused */
func = func; /* -Wunused FIXME: do escaping? */
-
- regex = request_data_get(request, request,
+
+ regex = request_data_reference(request, request,
REQUEST_DATA_REGEX | *(int *)instance);
if (!regex) return 0;
* Copy UP TO "freespace" bytes, including
* a zero byte.
*/
- strNcpy(out, regex, outlen);
- free(regex); /* was strdup'd */
+ strlcpy(out, regex, outlen);
return strlen(out);
}
#endif /* HAVE_REGEX_H */
+
+/*
+ * Change the debugging level.
+ */
+static size_t xlat_debug(UNUSED void *instance, REQUEST *request,
+ char *fmt, char *out, size_t outlen,
+ UNUSED RADIUS_ESCAPE_STRING func)
+{
+ int level = 0;
+
+ if (*fmt) level = atoi(fmt);
+
+ if (level == 0) {
+ request->options = RAD_REQUEST_OPTION_NONE;
+ request->radlog = NULL;
+ } else {
+ if (level > 4) level = 4;
+
+ request->options = level;
+ request->radlog = radlog_request;
+ }
+
+ snprintf(out, outlen, "%d", level);
+ return strlen(out);
+}
+
+
+/*
+ * Calculate the MD5 hash of a string.
+ */
+static size_t xlat_md5(UNUSED void *instance, REQUEST *request,
+ char *fmt, char *out, size_t outlen,
+ UNUSED RADIUS_ESCAPE_STRING func)
+{
+ int i;
+ uint8_t digest[16];
+ FR_MD5_CTX ctx;
+ char buffer[1024];
+
+ if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
+ *out = '\0';
+ return 0;
+ }
+
+ fr_MD5Init(&ctx);
+ fr_MD5Update(&ctx, (void *) buffer, strlen(buffer));
+ fr_MD5Final(digest, &ctx);
+
+ if (outlen < 33) {
+ snprintf(out, outlen, "md5_overflow");
+ return strlen(out);
+ }
+
+ for (i = 0; i < 16; i++) {
+ snprintf(out + i * 2, 3, "%02x", digest[i]);
+ }
+
+ return strlen(out);
+}
+
+
+/*
+ * Convert a string to lowercase
+ */
+static size_t xlat_lc(UNUSED void *instance, REQUEST *request,
+ char *fmt, char *out, size_t outlen,
+ UNUSED RADIUS_ESCAPE_STRING func)
+{
+ char *p, *q;
+ char buffer[1024];
+
+ if (outlen <= 1) return 0;
+
+ if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
+ *out = '\0';
+ return 0;
+ }
+
+ for (p = buffer, q = out; *p != '\0'; p++, outlen--) {
+ if (outlen <= 1) break;
+
+ *(q++) = tolower((int) *p);
+ }
+
+ *q = '\0';
+
+ return strlen(out);
+}
+
+
+/*
+ * Convert a string to uppercase
+ */
+static size_t xlat_uc(UNUSED void *instance, REQUEST *request,
+ char *fmt, char *out, size_t outlen,
+ UNUSED RADIUS_ESCAPE_STRING func)
+{
+ char *p, *q;
+ char buffer[1024];
+
+ if (outlen <= 1) return 0;
+
+ if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
+ *out = '\0';
+ return 0;
+ }
+
+ for (p = buffer, q = out; *p != '\0'; p++, outlen--) {
+ if (outlen <= 1) break;
+
+ *(q++) = toupper((int) *p);
+ }
+
+ *q = '\0';
+
+ return strlen(out);
+}
+
+
/*
* Compare two xlat_t structs, based ONLY on the module name.
*/
((const xlat_t *)a)->length);
}
-
/*
* find the appropriate registered xlat function.
*/
static xlat_t *xlat_find(const char *module)
{
- char *p;
xlat_t my_xlat;
- strNcpy(my_xlat.module, module, sizeof(my_xlat.module));
-
/*
- * We get passed the WHOLE string, and all we want here
- * is the first piece.
+ * Look for dictionary attributes first.
*/
- p = strchr(my_xlat.module, ':');
- if (p) *p = '\0';
+ if ((dict_attrbyname(module) != NULL) ||
+ (strchr(module, '[') != NULL) ||
+ (strchr(module, '#') != NULL)) {
+ module = "request";
+ }
+ strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
my_xlat.length = strlen(my_xlat.module);
return rbtree_finddata(xlat_root, &my_xlat);
c->internal = TRUE;
}
+ /*
+ * New name: "control"
+ */
+ xlat_register("control", xlat_packet, &xlat_inst[0]);
+ c = xlat_find("control");
+ rad_assert(c != NULL);
+ c->internal = TRUE;
+
#ifdef HAVE_REGEX_H
/*
* Register xlat's for regexes.
c->internal = TRUE;
}
#endif /* HAVE_REGEX_H */
+
+
+ xlat_register("debug", xlat_debug, &xlat_inst[0]);
+ c = xlat_find("debug");
+ rad_assert(c != NULL);
+ c->internal = TRUE;
+
+ xlat_register("md5", xlat_md5, &xlat_inst[0]);
+ c = xlat_find("md5");
+ rad_assert(c != NULL);
+ c->internal = TRUE;
+
+ xlat_register("tolower", xlat_lc, &xlat_inst[0]);
+ c = xlat_find("tolower");
+ rad_assert(c != NULL);
+ c->internal = TRUE;
+
+ xlat_register("toupper", xlat_uc, &xlat_inst[0]);
+ c = xlat_find("toupper");
+ rad_assert(c != NULL);
+ c->internal = TRUE;
}
/*
* If it already exists, replace the instance.
*/
- strNcpy(my_xlat.module, module, sizeof(my_xlat.module));
+ strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
my_xlat.length = strlen(my_xlat.module);
c = rbtree_finddata(xlat_root, &my_xlat);
if (c) {
/*
* Doesn't exist. Create it.
*/
- c = rad_malloc(sizeof(xlat_t));
+ c = rad_malloc(sizeof(*c));
memset(c, 0, sizeof(*c));
c->do_xlat = func;
- strNcpy(c->module, module, sizeof(c->module));
+ strlcpy(c->module, module, sizeof(c->module));
c->length = strlen(c->module);
c->instance = instance;
func = func; /* -Wunused */
- strNcpy(my_xlat.module, module, sizeof(my_xlat.module));
+ if (!module) return;
+
+ strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
my_xlat.length = strlen(my_xlat.module);
node = rbtree_find(xlat_root, &my_xlat);
rbtree_delete(xlat_root, node);
}
+/*
+ * De-register all xlat functions,
+ * used mainly for debugging.
+ */
+void xlat_free(void)
+{
+ rbtree_free(xlat_root);
+}
+
/*
* Decode an attribute name into a string.
*/
-static void decode_attribute(const char **from, char **to, int freespace,
- int *open, REQUEST *request,
+static int decode_attribute(const char **from, char **to, int freespace,
+ REQUEST *request,
RADIUS_ESCAPE_STRING func)
{
- char attrname[256];
- const char *p;
- char *q, *pa;
- int stop=0, found=0, retlen=0;
- int openbraces = *open;
- xlat_t *c;
+ int do_length = 0;
+ char *xlat_name, *xlat_string;
+ char *p, *q, *l, *next = NULL;
+ int retlen=0;
+ const xlat_t *c;
+ int varlen;
+ char buffer[8192];
- p = *from;
q = *to;
- pa = &attrname[0];
*q = '\0';
/*
- * Skip the '{' at the front of 'p'
- * Increment open braces
+ * Copy the input string to an intermediate buffer where
+ * we can mangle it.
*/
- p++;
- openbraces++;
+ varlen = rad_copy_variable(buffer, *from);
+ if (varlen < 0) {
+ RDEBUG2("Badly formatted variable: %s", *from);
+ return -1;
+ }
+ *from += varlen;
/*
- * Copy over the rest of the string.
+ * Kill the %{} around the data we are looking for.
*/
- while ((*p) && (!stop)) {
- switch(*p) {
- /*
- * Allow braces inside things, too.
- */
- case '\\':
- p++; /* skip it */
- *pa++ = *p++;
- break;
+ p = buffer;
+ p[varlen - 1] = '\0'; /* */
+ p += 2;
+ if (*p == '#') {
+ p++;
+ do_length = 1;
+ }
- case '{':
- openbraces++;
- *pa++ = *p++;
- break;
+ /*
+ * Handle %{%{foo}:-%{bar}}, which is useful, too.
+ *
+ * Did I mention that this parser is garbage?
+ */
+ if ((p[0] == '%') && (p[1] == '{')) {
+ int len1, len2;
+ int expand2 = FALSE;
- case '}':
- openbraces--;
- if (openbraces == *open) {
- p++;
- stop=1;
- } else {
- *pa++ = *p++;
- }
- break;
+ /*
+ * 'p' is after the start of 'buffer', so we can
+ * safely do this.
+ */
+ len1 = rad_copy_variable(buffer, p);
+ if (len1 < 0) {
+ RDEBUG2("Badly formatted variable: %s", p);
+ return -1;
+ }
- /*
- * Attr-Name1:-Attr-Name2
- *
- * Use Attr-Name1, and if not found,
- * use Attr-Name2.
- */
- case ':':
- if (p[1] == '-') {
- p += 2;
- stop = 1;
- break;
- }
- /* else FALL-THROUGH */
+ /*
+ * They did %{%{foo}}, which is stupid, but allowed.
+ */
+ if (!p[len1]) {
+ RDEBUG2("Improperly nested variable; %%{%s}", p);
+ return -1;
+ }
- default:
- *pa++ = *p++;
- break;
+ /*
+ * It SHOULD be %{%{foo}:-%{bar}}. If not, it's
+ * an error.
+ */
+ if ((p[len1] != ':') || (p[len1 + 1] != '-')) {
+ RDEBUG2("No trailing :- after variable at %s", p);
+ return -1;
}
- }
- *pa = '\0';
- /*
- * Look up almost everything in the new tree of xlat
- * functions. this makes it a little quicker...
- */
- if ((c = xlat_find(attrname)) != NULL) {
- if (!c->internal) DEBUG("radius_xlat: Running registered xlat function of module %s for string \'%s\'",
- c->module, attrname+ c->length + 1);
- retlen = c->do_xlat(c->instance, request, attrname+(c->length+1), q, freespace, func);
- /* If retlen is 0, treat it as not found */
- if (retlen == 0) {
- found = 0;
+ /*
+ * Parse the second bit. The second bit can be
+ * either %{foo}, or a string "foo", or a string
+ * 'foo', or just a bare word: foo
+ */
+ p += len1 + 2;
+ l = buffer + len1 + 1;
+
+ if ((p[0] == '%') && (p[1] == '{')) {
+ len2 = rad_copy_variable(l, p);
+
+ if (len2 < 0) {
+ RDEBUG2("Invalid text after :- at %s", p);
+ return -1;
+ }
+ p += len2;
+ expand2 = TRUE;
+
+ } else if ((p[0] == '"') || p[0] == '\'') {
+ getstring((const char **) &p, l, strlen(l));
+
} else {
- found = 1;
+ l = p;
+ }
+
+ /*
+ * Expand the first one. If we did, exit the
+ * conditional.
+ */
+ retlen = radius_xlat(q, freespace, buffer, request, func);
+ if (retlen) {
q += retlen;
+ goto done;
}
+ RDEBUG2("\t... expanding second conditional");
/*
- * Not in the default xlat database. Must be
- * a bare attribute number.
+ * Expand / copy the second string if required.
*/
- } else if ((retlen = xlat_packet(&xlat_inst[1], request, attrname,
- q, freespace, func)) > 0) {
- found = 1;
- q += retlen;
+ if (expand2) {
+ retlen = radius_xlat(q, freespace, l,
+ request, func);
+ if (retlen) {
+ q += retlen;
+ }
+ } else {
+ strlcpy(q, l, freespace);
+ q += strlen(q);
+ }
/*
- * Look up the name, in order to get the correct
- * debug message.
+ * Else the output is an empty string.
*/
-#ifndef NDEBUG
- } else if (dict_attrbyname(attrname) == NULL) {
+ goto done;
+ }
+
+ /*
+ * See if we're supposed to expand a module name.
+ */
+ xlat_name = NULL;
+ for (l = p; *l != '\0'; l++) {
+ if (*l == '\\') {
+ l++;
+ continue;
+ }
+
+ if (*l == ':') {
+ xlat_name = p; /* start of name */
+ *l = '\0';
+ p = l + 1;
+ break;
+ }
+
/*
- * No attribute by that name, return an error.
+ * Module names can't have spaces.
*/
- DEBUG2("WARNING: Attempt to use unknown xlat function, or non-existent attribute in string %%{%s}", attrname);
-#endif
- } /* else the attribute is known, but not in the request */
+ if ((*l == ' ') || (*l == '\t')) break;
+ }
/*
- * Skip to last '}' if attr is found
- * The rest of the stuff within the braces is
- * useless if we found what we need
+ * %{name} is a simple attribute reference,
+ * or regex reference.
*/
- if (found) {
- while((*p != '\0') && (openbraces > 0)) {
- /*
- * Handle escapes outside of the loop.
- */
- if (*p == '\\') {
- p++;
- if (!*p) break;
- p++; /* get & ignore next character */
- continue;
- }
+ if (!xlat_name) {
+ xlat_name = xlat_string = p;
+ goto do_xlat;
+ }
- switch (*p) {
- default:
- break;
+ /*
+ * Maybe it's the old-style %{foo:-bar}
+ */
+ if (*p == '-') {
+ RDEBUG2("WARNING: Deprecated conditional expansion \":-\". See \"man unlang\" for details");
+ p++;
- /*
- * Bare brace
- */
- case '{':
- openbraces++;
- break;
+ xlat_string = xlat_name;
+ next = p;
+ goto do_xlat;
+ }
- case '}':
- openbraces--;
- break;
+ /*
+ * FIXME: For backwards "WTF" compatibility, check for
+ * {...}, (after the :), and copy that, too.
+ */
+
+ /* module name, followed by (possibly) per-module string */
+ xlat_string = p;
+
+do_xlat:
+ if ((c = xlat_find(xlat_name)) != NULL) {
+ if (!c->internal) RDEBUG3("radius_xlat: Running registered xlat function of module %s for string \'%s\'",
+ c->module, xlat_string);
+ retlen = c->do_xlat(c->instance, request, xlat_string,
+ q, freespace, func);
+ if (retlen > 0) {
+ if (do_length) {
+ snprintf(q, freespace, "%d", retlen);
+ retlen = strlen(q);
}
- p++; /* skip the character */
+
+ } else if (next) {
+ /*
+ * Expand the second bit.
+ */
+ RDEBUG2("\t... expanding second conditional");
+ retlen = radius_xlat(q, freespace, next, request, func);
}
+ q += retlen;
+
+ } else {
+ /*
+ * No attribute by that name, return an error.
+ */
+ RDEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", xlat_name, *from);
+ return -1;
}
- *open = openbraces;
- *from = p;
+done:
*to = q;
+ return 0;
}
/*
* we use this one. It simplifies the coding, as the check for
* func == NULL only happens once.
*/
-static int xlat_copy(char *out, int outlen, const char *in)
+static size_t xlat_copy(char *out, size_t outlen, const char *in)
{
- int len = 0;
+ int freespace = outlen;
- while (*in) {
- /*
- * Truncate, if too much.
- */
- if (len >= outlen) {
- break;
- }
+ rad_assert(outlen > 0);
+ while ((*in) && (freespace > 1)) {
/*
* Copy data.
*
* FIXME: Do escaping of bad stuff!
*/
- *out = *in;
+ *(out++) = *(in++);
- out++;
- in++;
- len++;
+ freespace--;
}
-
*out = '\0';
- return len;
+
+ return (outlen - freespace); /* count does not include NUL */
}
/*
int radius_xlat(char *out, int outlen, const char *fmt,
REQUEST *request, RADIUS_ESCAPE_STRING func)
{
- int i, c,freespace;
+ int c, len, freespace;
const char *p;
char *q;
+ char *nl;
VALUE_PAIR *tmp;
struct tm *TM, s_TM;
char tmpdt[40]; /* For temporary storing of dates */
func = xlat_copy;
}
- q = out;
+ q = out;
p = fmt;
while (*p) {
/* Calculate freespace in output */
}
p++;
- /*
- * Hmmm... ${User-Name} is a synonym for
- * %{User-Name}.
- *
- * Why, exactly?
- */
- } else if (c == '$') switch(*p) {
- case '{': /* Attribute by Name */
- decode_attribute(&p, &q, freespace, &openbraces, request, func);
- break;
- default:
- *q++ = c;
- *q++ = *p++;
- break;
-
} else if (c == '%') switch(*p) {
case '{':
- decode_attribute(&p, &q, freespace, &openbraces, request, func);
+ p--;
+ if (decode_attribute(&p, &q, freespace, request, func) < 0) return 0;
break;
case '%':
*q++ = *p++;
break;
case 'a': /* Protocol: */
- q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_PROTOCOL),PW_TYPE_INTEGER, func);
+ q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_PROTOCOL, 0),PW_TYPE_INTEGER, func);
p++;
break;
case 'c': /* Callback-Number */
- q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_CALLBACK_NUMBER),PW_TYPE_STRING, func);
+ q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_CALLBACK_NUMBER, 0),PW_TYPE_STRING, func);
p++;
break;
case 'd': /* request day */
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%d",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%d", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'f': /* Framed IP address */
- q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_IP_ADDRESS),PW_TYPE_IPADDR, func);
+ q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_IP_ADDRESS, 0),PW_TYPE_IPADDR, func);
p++;
break;
case 'i': /* Calling station ID */
- q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CALLING_STATION_ID),PW_TYPE_STRING, func);
+ q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CALLING_STATION_ID, 0),PW_TYPE_STRING, func);
p++;
break;
case 'l': /* request timestamp */
snprintf(tmpdt, sizeof(tmpdt), "%lu",
(unsigned long) request->timestamp);
- strNcpy(q,tmpdt,freespace);
+ strlcpy(q,tmpdt,freespace);
q += strlen(q);
p++;
break;
case 'm': /* request month */
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%m",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%m", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'n': /* NAS IP address */
- q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_IP_ADDRESS),PW_TYPE_IPADDR, func);
+ q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_IP_ADDRESS, 0),PW_TYPE_IPADDR, func);
p++;
break;
case 'p': /* Port number */
- q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_PORT),PW_TYPE_INTEGER, func);
+ q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_PORT, 0),PW_TYPE_INTEGER, func);
p++;
break;
case 's': /* Speed */
- q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CONNECT_INFO),PW_TYPE_STRING, func);
+ q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CONNECT_INFO, 0),PW_TYPE_STRING, func);
p++;
break;
case 't': /* request timestamp */
- CTIME_R(&request->timestamp, q, freespace);
+ CTIME_R(&request->timestamp, tmpdt, sizeof(tmpdt));
+ nl = strchr(tmpdt, '\n');
+ if (nl) *nl = '\0';
+ strlcpy(q, tmpdt, freespace);
q += strlen(q);
p++;
break;
case 'u': /* User name */
- q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_USER_NAME),PW_TYPE_STRING, func);
+ q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_USER_NAME, 0),PW_TYPE_STRING, func);
p++;
break;
case 'A': /* radacct_dir */
- strNcpy(q,radacct_dir,freespace-1);
+ strlcpy(q,radacct_dir,freespace);
q += strlen(q);
p++;
break;
case 'C': /* ClientName */
- strNcpy(q,client_name(request->packet->src_ipaddr),freespace-1);
+ strlcpy(q,request->client->shortname,freespace);
q += strlen(q);
p++;
break;
case 'D': /* request date */
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%Y%m%d",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%Y%m%d", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'H': /* request hour */
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%H",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%H", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'L': /* radlog_dir */
- strNcpy(q,radlog_dir,freespace-1);
+ strlcpy(q,radlog_dir,freespace);
q += strlen(q);
p++;
break;
case 'M': /* MTU */
- q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_MTU),PW_TYPE_INTEGER, func);
+ q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_MTU, 0),PW_TYPE_INTEGER, func);
p++;
break;
case 'R': /* radius_dir */
- strNcpy(q,radius_dir,freespace-1);
+ strlcpy(q,radius_dir,freespace);
q += strlen(q);
p++;
break;
case 'S': /* request timestamp in SQL format*/
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%Y-%m-%d %H:%M:%S",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%Y-%m-%d %H:%M:%S", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'T': /* request timestamp */
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%Y-%m-%d-%H.%M.%S.000000",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%Y-%m-%d-%H.%M.%S.000000", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'U': /* Stripped User name */
- q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_STRIPPED_USER_NAME),PW_TYPE_STRING, func);
+ q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_STRIPPED_USER_NAME, 0),PW_TYPE_STRING, func);
p++;
break;
case 'V': /* Request-Authenticator */
- if (request->packet->verified)
- strNcpy(q,"Verified",freespace-1);
- else
- strNcpy(q,"None",freespace-1);
+ strlcpy(q,"Verified",freespace);
q += strlen(q);
p++;
break;
case 'Y': /* request year */
TM = localtime_r(&request->timestamp, &s_TM);
- strftime(tmpdt,sizeof(tmpdt),"%Y",TM);
- strNcpy(q,tmpdt,freespace);
- q += strlen(q);
+ len = strftime(tmpdt, sizeof(tmpdt), "%Y", TM);
+ if (len > 0) {
+ strlcpy(q, tmpdt, freespace);
+ q += strlen(q);
+ }
p++;
break;
case 'Z': /* Full request pairs except password */
tmp = request->packet->vps;
while (tmp && (freespace > 3)) {
- if (tmp->attribute != PW_PASSWORD) {
+ if (tmp->attribute != PW_USER_PASSWORD) {
*q++ = '\t';
- i = vp_prints(q,freespace-2,tmp);
- q += i;
- freespace -= (i+2);
+ len = vp_prints(q, freespace - 2, tmp);
+ q += len;
+ freespace -= (len + 2);
*q++ = '\n';
}
tmp = tmp->next;
p++;
break;
default:
- DEBUG2("WARNING: Unknown variable '%%%c': See 'doc/variables.txt'", *p);
+ RDEBUG2("WARNING: Unknown variable '%%%c': See 'doc/variables.txt'", *p);
if (freespace > 2) {
*q++ = '%';
*q++ = *p++;
}
*q = '\0';
- DEBUG2("radius_xlat: '%s'", out);
+ RDEBUG2("\texpand: %s -> %s", fmt, out);
return strlen(out);
}