RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/md5.h>
#include <freeradius-devel/rad_assert.h>
#include <ctype.h>
#if REQUEST_MAX_REGEX > 8
#error Please fix the following line
#endif
-static const int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; /* up to 8 for regex */
+static int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; /* up to 8 for regex */
/*
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:
*/
da = dict_attrbyname(fmt);
if (!da) {
+ int do_number = FALSE;
size_t count;
- const char *p = strchr(fmt, '[');
+ const char *p;
char buffer[256];
- if (!p) return 0;
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);
+ 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);
}
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;
*/
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
{
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 {
/*
+ * 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.
*/
static int xlat_cmp(const void *a, const void *b)
((const xlat_t *)a)->length);
}
-
/*
* find the appropriate registered xlat function.
*/
* Look for dictionary attributes first.
*/
if ((dict_attrbyname(module) != NULL) ||
- (strchr(module, '[') != NULL)) {
+ (strchr(module, '[') != NULL) ||
+ (strchr(module, '#') != NULL)) {
module = "request";
}
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;
}
/*
/*
* Decode an attribute name into a string.
*/
-static void decode_attribute(const char **from, char **to, int freespace,
- int *open_p, 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_p;
+ char *xlat_name, *xlat_string;
+ char *p, *q, *l, *next = NULL;
+ int retlen=0;
const xlat_t *c;
- int spaces = FALSE;
+ 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;
* Did I mention that this parser is garbage?
*/
if ((p[0] == '%') && (p[1] == '{')) {
- /*
- * This is really bad, but it works.
- */
int len1, len2;
- size_t mylen = strlen(p);
- char *first = rad_malloc(mylen);
- char *second = rad_malloc(mylen);
int expand2 = FALSE;
- len1 = rad_copy_variable(first, p);
+ /*
+ * 'p' is after the start of 'buffer', so we can
+ * safely do this.
+ */
+ len1 = rad_copy_variable(buffer, p);
if (len1 < 0) {
- DEBUG2("Badly formatted variable: %s", p);
- goto free_and_done;
+ RDEBUG2("Badly formatted variable: %s", p);
+ return -1;
}
+ /*
+ * They did %{%{foo}}, which is stupid, but allowed.
+ */
+ if (!p[len1]) {
+ RDEBUG2("Improperly nested variable; %%{%s}", p);
+ return -1;
+ }
+
+ /*
+ * It SHOULD be %{%{foo}:-%{bar}}. If not, it's
+ * an error.
+ */
if ((p[len1] != ':') || (p[len1 + 1] != '-')) {
- DEBUG2("No trailing :- after variable at %s", p);
- goto free_and_done;
+ RDEBUG2("No trailing :- after variable at %s", p);
+ return -1;
}
+ /*
+ * 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(second, p);
+ len2 = rad_copy_variable(l, p);
- expand2 = TRUE;
if (len2 < 0) {
- DEBUG2("Invalid text after :- at %s", p);
- goto free_and_done;
+ RDEBUG2("Invalid text after :- at %s", p);
+ return -1;
}
p += len2;
+ expand2 = TRUE;
} else if ((p[0] == '"') || p[0] == '\'') {
- getstring(&p, second, mylen);
+ getstring((const char **) &p, l, strlen(l));
} else {
- char *s = second;
-
- while (*p && (*p != '}')) {
- *(s++) = *(p++);
- }
- *s = '\0';
+ l = p;
}
- if (*p != '}') {
- DEBUG2("Failed to find trailing '}' in string");
- goto free_and_done;
- }
-
- mylen = radius_xlat(q, freespace, first, request, func);
- if (mylen) {
- q += mylen;
- goto free_and_done;
+ /*
+ * Expand the first one. If we did, exit the
+ * conditional.
+ */
+ retlen = radius_xlat(q, freespace, buffer, request, func);
+ if (retlen) {
+ q += retlen;
+ goto done;
}
- if (!expand2) {
- strlcpy(q, second, freespace);
- q += strlen(q);
- } else {
- mylen = radius_xlat(q, freespace, second,
+ RDEBUG2("\t... expanding second conditional");
+ /*
+ * Expand / copy the second string if required.
+ */
+ if (expand2) {
+ retlen = radius_xlat(q, freespace, l,
request, func);
- if (mylen) {
- q += mylen;
- goto free_and_done;
+ if (retlen) {
+ q += retlen;
}
+ } else {
+ strlcpy(q, l, freespace);
+ q += strlen(q);
}
/*
* Else the output is an empty string.
*/
- free_and_done:
- free(first);
- free(second);
goto done;
}
-
/*
- * First, copy the xlat key name to one buffer
+ * See if we're supposed to expand a module name.
*/
- while (*p && (*p != '}') && (*p != ':')) {
- *pa++ = *p++;
-
- 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;
+ xlat_name = NULL;
+ for (l = p; *l != '\0'; l++) {
+ if (*l == '\\') {
+ l++;
+ continue;
}
- }
- *pa = '\0';
- if (!*p) {
- DEBUG("xlat: Invalid syntax in %s", *from);
+ if (*l == ':') {
+ xlat_name = p; /* start of name */
+ *l = '\0';
+ p = l + 1;
+ break;
+ }
/*
- * %{name} is a simple attribute reference,
- * or regex reference.
+ * Module names can't have spaces.
*/
- } else if (*p == '}') {
- openbraces--;
- rad_assert(openbraces == *open_p);
+ if ((*l == ' ') || (*l == '\t')) break;
+ }
- p++;
- xlat_string = xlat_name;
+ /*
+ * %{name} is a simple attribute reference,
+ * or regex reference.
+ */
+ if (!xlat_name) {
+ xlat_name = xlat_string = p;
goto do_xlat;
+ }
+
+ /*
+ * Maybe it's the old-style %{foo:-bar}
+ */
+ if (*p == '-') {
+ RDEBUG2("WARNING: Deprecated conditional expansion \":-\". See \"man unlang\" for details");
+ p++;
- } else if ((p[0] == ':') && (p[1] == '-')) { /* handle ':- */
- DEBUG2("WARNING: Deprecated conditional expansion \":-\". See \"man unlang\" for details");
- p += 2;
xlat_string = xlat_name;
+ next = p;
goto do_xlat;
-
- } else { /* module name, followed by per-module string */
- int stop = 0;
- int delimitbrace = *open_p;
-
- rad_assert(*p == ':');
- p++; /* skip the ':' */
-
- /*
- * If there's a brace immediately following the colon,
- * then we've chosen to delimite the per-module string,
- * so keep track of that.
- */
- if (*p == '{') {
- delimitbrace = openbraces;
- openbraces++;
- p++;
- }
-
- 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.
- */
- while (*p && !stop) {
- switch(*p) {
-
- /*
- * What the heck is this supposed
- * to be doing?
- */
- case '\\':
- p++; /* skip it */
- *pa++ = *p++;
- break;
-
- case ':':
- if (!spaces && p[1] == '-') {
- p += 2;
- stop = 1;
- break;
- }
- *pa++ = *p++;
- break;
-
- /*
- * This is pretty hokey... we
- * should use the functions in
- * util.c
- */
- case '{':
- openbraces++;
- *pa++ = *p++;
- break;
-
- case '}':
- openbraces--;
- if (openbraces == delimitbrace) {
- p++;
- stop=1;
- } else {
- *pa++ = *p++;
- }
- break;
-
- case ' ':
- case '\t':
- spaces = TRUE;
- /* FALL-THROUGH */
-
- default:
- *pa++ = *p++;
- break;
- }
- }
-
- *pa = '\0';
-
- /*
- * Now check to see if we're at the end of the string
- * we were sent. If we're not, check for :-
- */
- if (openbraces == delimitbrace) {
- if (p[0] == ':' && p[1] == '-') {
- p += 2;
- }
- }
-
- /*
- * Look up almost everything in the new tree of xlat
- * functions. This makes it a little quicker...
- */
- do_xlat:
- if ((c = xlat_find(xlat_name)) != NULL) {
- if (!c->internal) DEBUG3("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
- } else {
- /*
- * No attribute by that name, return an error.
- */
- DEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", xlat_name, *from);
-#endif
- }
}
/*
- * Skip to last '}' if attr is found
- * The rest of the stuff within the braces is
- * useless if we found what we need
+ * FIXME: For backwards "WTF" compatibility, check for
+ * {...}, (after the :), and copy that, too.
*/
- if (found) {
- if (do_length) {
- snprintf(q, freespace, "%d", retlen);
- retlen = strlen(q);
- }
- q += retlen;
+ /* 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);
+ }
- while((*p != '\0') && (openbraces > *open_p)) {
+ } else if (next) {
/*
- * Handle escapes outside of the loop.
+ * Expand the second bit.
*/
- if (*p == '\\') {
- p++;
- if (!*p) break;
- p++; /* get & ignore next character */
- continue;
- }
-
- switch (*p) {
- default:
- break;
-
- /*
- * Bare brace
- */
- case '{':
- openbraces++;
- break;
-
- case '}':
- openbraces--;
- break;
- }
- p++; /* skip the character */
+ RDEBUG2("\t... expanding second conditional");
+ retlen = radius_xlat(q, freespace, next, request, func);
}
- }
+ q += retlen;
- done:
- if (free_xlat_string) free(xlat_string);
+ } else {
+ /*
+ * No attribute by that name, return an error.
+ */
+ RDEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", xlat_name, *from);
+ return -1;
+ }
- *open_p = openbraces;
- *from = p;
+done:
*to = q;
+ return 0;
}
/*
} 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 */
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->received.tv_sec);
+ (unsigned long) request->timestamp);
strlcpy(q,tmpdt,freespace);
q += strlen(q);
p++;
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 */
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 */
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 */
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 */
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("\texpand: %s -> %s", fmt, out);
+ RDEBUG2("\texpand: %s -> %s", fmt, out);
return strlen(out);
}