*
* 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 <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;
"reply",
"proxy-request",
"proxy-reply",
+ "outer.request",
+ "outer.reply",
NULL};
#if REQUEST_MAX_REGEX > 8
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 :
- strNcpy(out,":?:",outlen);
+ 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;
}
*/
da = dict_attrbyname(fmt);
if (!da) {
- int count;
- const char *p = strchr(fmt, '[');
+ int do_number = FALSE;
+ size_t count;
+ const char *p;
char buffer[256];
- if (!p) return 0;
if (strlen(fmt) > sizeof(buffer)) return 0;
- strNcpy(buffer, fmt, p - fmt + 1);
+ 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);
+ for (vp = pairfind(vps, da->attr, da->vendor);
vp != NULL;
- vp = pairfind(vp->next, da->attr)) {
+ vp = pairfind(vp->next, da->attr, da->vendor)) {
count++;
}
- snprintf(out, outlen, "%d", 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);
+ for (vp = pairfind(vps, da->attr, da->vendor);
vp != NULL;
- vp = pairfind(vp->next, da->attr)) {
+ 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);
/*
*/
p += 1 + strspn(p + 1, "0123456789");
if (*p != ']') {
- DEBUG2("xlat: Invalid array reference in string at %s %s",
+ 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);
+ for (vp = pairfind(vps, da->attr, da->vendor);
vp != NULL;
- vp = pairfind(vp->next, da->attr)) {
+ vp = pairfind(vp->next, da->attr, da->vendor)) {
if (count == 0) break;
count--;
}
* Non-existent array reference.
*/
if (!vp) return 0;
-
+ just_print:
return valuepair2str(out, outlen, vp, da->type, func);
}
- vp = pairfind(vps, da->attr);
+ vp = pairfind(vps, da->attr, da->vendor);
if (!vp) {
/*
* Some "magic" handlers, which are never in VP's, but
if (packet) {
VALUE_PAIR localvp;
- localvp.strvalue[0] = 0;
+ 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 {
}
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.lvalue = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
+ 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.lvalue = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
+ localvp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
break;
-
+
case PW_PACKET_SRC_PORT:
localvp.attribute = da->attr;
- localvp.lvalue = packet->src_port;
+ localvp.vp_integer = packet->src_port;
break;
-
+
case PW_PACKET_DST_PORT:
localvp.attribute = da->attr;
- localvp.lvalue = packet->dst_port;
+ localvp.vp_integer = packet->dst_port;
break;
case PW_PACKET_AUTHENTICATION_VECTOR:
localvp.attribute = da->attr;
- memcpy(localvp.strvalue, packet->vector,
+ memcpy(localvp.vp_strvalue, packet->vector,
sizeof(packet->vector));
localvp.length = sizeof(packet->vector);
break;
*/
case PW_REQUEST_PROCESSING_STAGE:
if (request->component) {
- strNcpy(out, request->component, outlen);
+ strlcpy(out, request->component, outlen);
} else {
- strNcpy(out, "server_core", outlen);
+ 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.strvalue,
- &packet->src_ipaddr.ipaddr.ip4addr.s_addr,
- sizeof(packet->src_ipaddr.ipaddr.ip4addr.s_addr));
+ 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.strvalue,
- &packet->dst_ipaddr.ipaddr.ip4addr.s_addr,
- sizeof(packet->dst_ipaddr.ipaddr.ip4addr.s_addr));
+ memcpy(localvp.vp_strvalue,
+ &packet->dst_ipaddr.ipaddr.ip6addr,
+ sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
break;
-
- case PW_SERVER_IDENTITY:
- if (!request->listener->identity) return 0;
- snprintf(out, outlen, "%s", request->listener->identity);
+ 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;
/*
* 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 (!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);
+}
+
+
+
/*
* Compare two xlat_t structs, based ONLY on the module name.
*/
((const xlat_t *)a)->length);
}
-
/*
* find the appropriate registered xlat function.
*/
-static const xlat_t *xlat_find(const char *module)
+static xlat_t *xlat_find(const char *module)
{
xlat_t my_xlat;
* Look for dictionary attributes first.
*/
if ((dict_attrbyname(module) != NULL) ||
- (strchr(module, '[') != NULL)) {
- static const xlat_t dict_xlat = {
- "request",
- 7,
- &xlat_inst[1],
- xlat_packet,
- TRUE
- };
-
- return &dict_xlat;
+ (strchr(module, '[') != NULL) ||
+ (strchr(module, '#') != NULL)) {
+ module = "request";
}
- 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);
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;
}
/*
* 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);
/*
* 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)
{
int do_length = 0;
- char xlat_name[128];
- char *xlat_string = NULL; /* can be large */
- int free_xlat_string = FALSE;
- const char *p;
- char *q, *pa;
- int found=0, retlen=0;
- int openbraces = *open;
+ 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 = &xlat_name[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;
+ /*
+ * Kill the %{} around the data we are looking for.
+ */
+ p = buffer;
+ p[varlen - 1] = '\0'; /* */
+ p += 2;
if (*p == '#') {
p++;
do_length = 1;
}
/*
- * First, copy the xlat key name to one buffer
+ * Handle %{%{foo}:-%{bar}}, which is useful, too.
+ *
+ * Did I mention that this parser is garbage?
*/
- while (*p && (*p != '}') && (*p != ':')) {
- *pa++ = *p++;
+ if ((p[0] == '%') && (p[1] == '{')) {
+ int len1, len2;
+ int expand2 = FALSE;
- if (pa >= (xlat_name + sizeof(xlat_name) - 1)) {
- /*
- * Skip to the end of the input
- */
- p += strlen(p);
- DEBUG("xlat: Module name is too long in string %%%s",
- *from);
- goto done;
+ /*
+ * '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;
}
- }
- *pa = '\0';
-
- if (!*p) {
- DEBUG("xlat: Invalid syntax in %s", *from);
/*
- * %{name} is a simple attribute reference,
- * or regex reference.
+ * They did %{%{foo}}, which is stupid, but allowed.
*/
- } else if (*p == '}') {
- openbraces--;
- rad_assert(openbraces == *open);
-
- p++;
- xlat_string = xlat_name;
- goto do_xlat;
-
- } else if (p[1] == '-') { /* handle ':- */
- p += 2;
- xlat_string = xlat_name;
- goto do_xlat;
-
- } else { /* module name, followed by per-module string */
- int stop = 0;
- int delimitbrace = *open;
-
- rad_assert(*p == ':');
- p++; /* skip the ':' */
+ if (!p[len1]) {
+ RDEBUG2("Improperly nested variable; %%{%s}", p);
+ return -1;
+ }
/*
- * If there's a brace immediately following the colon,
- * then we've chosen to delimite the per-module string,
- * so keep track of that.
+ * It SHOULD be %{%{foo}:-%{bar}}. If not, it's
+ * an error.
*/
- if (*p == '{') {
- delimitbrace = openbraces;
- openbraces++;
- p++;
+ if ((p[len1] != ':') || (p[len1 + 1] != '-')) {
+ RDEBUG2("No trailing :- after variable at %s", p);
+ return -1;
}
-
- xlat_string = rad_malloc(strlen(p) + 1); /* always returns */
- free_xlat_string = TRUE;
- pa = xlat_string;
-
+
/*
- * Copy over the rest of the string, which is per-module
- * data.
+ * 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
*/
- while (*p && !stop) {
- switch(*p) {
- /*
- * What the heck is this supposed
- * to be doing?
- */
- case '\\':
- p++; /* skip it */
- *pa++ = *p++;
- break;
+ p += len1 + 2;
+ l = buffer + len1 + 1;
- /*
- * This is pretty hokey... we
- * should use the functions in
- * util.c
- */
- case '{':
- openbraces++;
- *pa++ = *p++;
- break;
+ if ((p[0] == '%') && (p[1] == '{')) {
+ len2 = rad_copy_variable(l, p);
- case '}':
- openbraces--;
- if (openbraces == delimitbrace) {
- p++;
- stop=1;
- } else {
- *pa++ = *p++;
- }
- break;
-
- default:
- *pa++ = *p++;
- break;
+ if (len2 < 0) {
+ RDEBUG2("Invalid text after :- at %s", p);
+ return -1;
}
- }
+ p += len2;
+ expand2 = TRUE;
- *pa = '\0';
+ } else if ((p[0] == '"') || p[0] == '\'') {
+ getstring(&p, l, strlen(l));
+
+ } else {
+ l = p;
+ }
/*
- * Now check to see if we're at the end of the string
- * we were sent. If we're not, check for :-
+ * Expand the first one. If we did, exit the
+ * conditional.
*/
- if (openbraces == delimitbrace) {
- if (p[0] == ':' && p[1] == '-') {
- p += 2;
- }
+ retlen = radius_xlat(q, freespace, buffer, request, func);
+ if (retlen) {
+ q += retlen;
+ goto done;
}
-
+
+ RDEBUG2("\t... expanding second conditional");
/*
- * Look up almost everything in the new tree of xlat
- * functions. This makes it a little quicker...
+ * Expand / copy the second string if required.
*/
- do_xlat:
- if ((c = xlat_find(xlat_name)) != NULL) {
- if (!c->internal) DEBUG("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 is 0, treat it as not found */
- if (retlen > 0) found = 1;
-#ifndef NDEBUG
+ if (expand2) {
+ retlen = radius_xlat(q, freespace, l,
+ request, func);
+ if (retlen) {
+ q += retlen;
+ }
} else {
- /*
- * No attribute by that name, return an error.
- */
- DEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", xlat_name, *from);
-#endif
+ strlcpy(q, l, freespace);
+ q += strlen(q);
}
+
+ /*
+ * Else the output is an empty string.
+ */
+ goto done;
}
/*
- * Skip to last '}' if attr is found
- * The rest of the stuff within the braces is
- * useless if we found what we need
+ * See if we're supposed to expand a module name.
*/
- if (found) {
- if (do_length) {
- snprintf(q, freespace, "%d", retlen);
- retlen = strlen(q);
+ xlat_name = NULL;
+ for (l = p; *l != '\0'; l++) {
+ if (*l == '\\') {
+ l++;
+ continue;
}
- q += retlen;
+ if (*l == ':') {
+ xlat_name = p; /* start of name */
+ *l = '\0';
+ p = l + 1;
+ break;
+ }
- while((*p != '\0') && (openbraces > 0)) {
- /*
- * Handle escapes outside of the loop.
- */
- if (*p == '\\') {
- p++;
- if (!*p) break;
- p++; /* get & ignore next character */
- continue;
- }
+ /*
+ * Module names can't have spaces.
+ */
+ if ((*l == ' ') || (*l == '\t')) break;
+ }
- switch (*p) {
- default:
- break;
+ /*
+ * %{name} is a simple attribute reference,
+ * or regex reference.
+ */
+ if (!xlat_name) {
+ xlat_name = xlat_string = p;
+ goto do_xlat;
+ }
- /*
- * Bare brace
- */
- case '{':
- openbraces++;
- break;
+ /*
+ * Maybe it's the old-style %{foo:-bar}
+ */
+ if (*p == '-') {
+ RDEBUG2("WARNING: Deprecated conditional expansion \":-\". See \"man unlang\" for details");
+ p++;
- case '}':
- openbraces--;
- break;
+ xlat_string = xlat_name;
+ next = p;
+ goto do_xlat;
+ }
+
+ /*
+ * 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;
}
-
- done:
- if (free_xlat_string) free(xlat_string);
- *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 freespace = outlen;
- rad_assert(outlen >= 0);
+ rad_assert(outlen > 0);
while ((*in) && (freespace > 1)) {
/*
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 */
}
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);
- q = strchr(q, '\n');
- if (q) {
- *q = '\0';
- } else {
- q += strlen(q);
- }
+ 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_old(&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);
}