/** Implements the Foreach-Variable-X * * @see modcall() */ static ssize_t xlat_foreach(void *instance, REQUEST *request, UNUSED char const *fmt, char *out, size_t outlen) { VALUE_PAIR **pvp; /* * See modcall, "FOREACH" for how this works. */ pvp = (VALUE_PAIR **) request_data_reference(request, radius_get_vp, *(int*) instance); if (!pvp || !*pvp) { *out = '\0'; return 0; } return valuepair2str(out, outlen, (*pvp), (*pvp)->da->type); }
/** * @brief Replace %whatever in a string. * * See 'doc/variables.txt' for more information. * * @param out output buffer * @param outlen size of output buffer * @param fmt string to expand * @param request current request * @param func function to escape final value e.g. SQL quoting * @return length of string written @bug should really have -1 for failure */ int radius_xlat(char *out, int outlen, const char *fmt, REQUEST *request, RADIUS_ESCAPE_STRING func) { 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 */ int openbraces=0; /* * Catch bad modules. */ if (!fmt || !out || !request) return 0; /* * Ensure that we always have an escaping function. */ if (func == NULL) { func = xlat_copy; } q = out; p = fmt; while (*p) { /* Calculate freespace in output */ freespace = outlen - (q - out); if (freespace <= 1) break; c = *p; if ((c != '%') && (c != '$') && (c != '\\')) { /* * We check if we're inside an open brace. If we are * then we assume this brace is NOT literal, but is * a closing brace and apply it */ if ((c == '}') && openbraces) { openbraces--; p++; /* skip it */ continue; } *q++ = *p++; continue; } /* * There's nothing after this character, copy * the last '%' or "$' or '\\' over to the output * buffer, and exit. */ if (*++p == '\0') { *q++ = c; break; } if (c == '\\') { switch(*p) { case '\\': *q++ = *p; break; case 't': *q++ = '\t'; break; case 'n': *q++ = '\n'; break; default: *q++ = c; *q++ = *p; break; } p++; } else if (c == '%') switch(*p) { case '{': 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, 0),PW_TYPE_INTEGER, func); p++; break; case 'c': /* Callback-Number */ 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); 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, 0),PW_TYPE_IPADDR, func); p++; break; case 'i': /* Calling station ID */ 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); strlcpy(q,tmpdt,freespace); q += strlen(q); p++; break; case 'm': /* request month */ TM = localtime_r(&request->timestamp, &s_TM); 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, 0),PW_TYPE_IPADDR, func); p++; break; case 'p': /* Port number */ 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, 0),PW_TYPE_STRING, func); p++; break; case 't': /* request timestamp */ 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, 0),PW_TYPE_STRING, func); p++; break; case 'A': /* radacct_dir */ strlcpy(q,radacct_dir,freespace); q += strlen(q); p++; break; case 'C': /* ClientName */ strlcpy(q,request->client->shortname,freespace); q += strlen(q); p++; break; case 'D': /* request date */ TM = localtime_r(&request->timestamp, &s_TM); 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); len = strftime(tmpdt, sizeof(tmpdt), "%H", TM); if (len > 0) { strlcpy(q, tmpdt, freespace); q += strlen(q); } p++; break; case 'L': /* radlog_dir */ strlcpy(q,radlog_dir,freespace); q += strlen(q); p++; break; case 'M': /* MTU */ q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_MTU, 0),PW_TYPE_INTEGER, func); p++; break; case 'R': /* radius_dir */ strlcpy(q,radius_dir,freespace); q += strlen(q); p++; break; case 'S': /* request timestamp in SQL format*/ TM = localtime_r(&request->timestamp, &s_TM); 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); 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, 0),PW_TYPE_STRING, func); p++; break; case 'V': /* Request-Authenticator */ strlcpy(q,"Verified",freespace); q += strlen(q); p++; break; case 'Y': /* request year */ TM = localtime_r(&request->timestamp, &s_TM); 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_USER_PASSWORD) { *q++ = '\t'; len = vp_prints(q, freespace - 2, tmp); q += len; freespace -= (len + 2); *q++ = '\n'; } tmp = tmp->next; } p++; break; default: RDEBUG2("WARNING: Unknown variable '%%%c': See 'doc/variables.txt'", *p); if (freespace > 2) { *q++ = '%'; *q++ = *p++; } break; } } *q = '\0'; RDEBUG2("\texpand: %s -> %s", fmt, out); return strlen(out); }
/* * Dynamically translate for check:, request:, reply:, etc. */ static size_t xlat_packet(void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { DICT_ATTR *da; VALUE_PAIR *vp; VALUE_PAIR *vps = NULL; RADIUS_PACKET *packet = NULL; switch (*(int*) instance) { case 0: vps = request->config_items; break; case 1: vps = request->packet->vps; packet = request->packet; break; case 2: vps = request->reply->vps; packet = request->reply; 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; case 7: if (request->parent) { vps = request->parent->config_items; } break; default: /* WTF? */ return 0; } /* * The "format" string is the attribute name. */ da = dict_attrbyname(fmt); if (!da) { int do_number = FALSE; int do_array = FALSE; int do_count = FALSE; int do_all = FALSE; int tag = 0; size_t count = 0, total; char *p; char buffer[256]; if (strlen(fmt) > sizeof(buffer)) return 0; strlcpy(buffer, fmt, sizeof(buffer)); /* * %{Attribute-name#} - print integer version of it. */ p = buffer + strlen(buffer) - 1; if (*p == '#') { *p = '\0'; do_number = TRUE; } /* * %{Attribute-Name:tag} - get the name with the specified * value of the tag. */ p = strchr(buffer, ':'); if (p) { tag = atoi(p + 1); *p = '\0'; p++; } else { /* * Allow %{Attribute-Name:tag[...]} */ p = buffer; } /* * %{Attribute-Name[...] does more stuff */ p = strchr(p, '['); if (p) { *p = '\0'; do_array = TRUE; if (p[1] == '#') { do_count = TRUE; } else if (p[1] == '*') { do_all = TRUE; } else { count = atoi(p + 1); p += 1 + strspn(p + 1, "0123456789"); if (*p != ']') { RDEBUG2("xlat: Invalid array reference in string at %s %s", fmt, p); return 0; } } } /* * We COULD argue about %{Attribute-Name[#]#} etc. * But that looks like more work than it's worth. */ da = dict_attrbyname(buffer); if (!da) return 0; /* * No array, print the tagged attribute. */ if (!do_array) { vp = pairfind_tag(vps, da, tag); goto just_print; } total = 0; /* * Array[#] - return the total */ if (do_count) { for (vp = pairfind_tag(vps, da, tag); vp != NULL; vp = pairfind_tag(vp->next, da, tag)) { total++; } snprintf(out, outlen, "%d", (int) total); return strlen(out); } /* * %{Attribute-Name[*]} returns ALL of the * the attributes, separated by a newline. */ if (do_all) { for (vp = pairfind_tag(vps, da, tag); vp != NULL; vp = pairfind_tag(vp->next, da, tag)) { count = valuepair2str(out, outlen - 1, vp, da->type); rad_assert(count <= outlen); total += count + 1; outlen -= (count + 1); out += count; *(out++) = '\n'; if (outlen <= 1) break; } *out = '\0'; return total; } /* * Find the N'th value. */ for (vp = pairfind_tag(vps, da, tag); vp != NULL; vp = pairfind_tag(vp->next, da, tag)) { if (total == count) break; total++; if (total > count) { vp = NULL; break; } } /* * Non-existent array reference. */ just_print: if (!vp) return 0; if (do_number) { if ((vp->type != PW_TYPE_IPADDR) && (vp->type != PW_TYPE_INTEGER) && (vp->type != PW_TYPE_SHORT) && (vp->type != PW_TYPE_BYTE) && (vp->type != PW_TYPE_DATE)) { *out = '\0'; return 0; } return snprintf(out, outlen, "%u", vp->vp_integer); } return valuepair2str(out, outlen, vp, da->type); } vp = pairfind(vps, da->attr, da->vendor); if (!vp) { /* * Some "magic" handlers, which are never in VP's, but * which are in the packet. * * @bug 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, da->vendor, packet->code); if (dval) { snprintf(out, outlen, "%s", dval->name); } else { snprintf(out, outlen, "%d", packet->code); } 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); } /* * Not found, die. */ return 0; } if (!vps) return 0; /* silently fail */ /* * Convert the VP to a string, and return it. */ return valuepair2str(out, outlen, vp, da->type); }
/** * @brief Dynamically translate for check:, request:, reply:, etc. */ 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; VALUE_PAIR *vps = NULL; RADIUS_PACKET *packet = NULL; switch (*(int*) instance) { case 0: vps = request->config_items; break; case 1: vps = request->packet->vps; packet = request->packet; break; case 2: vps = request->reply->vps; packet = request->reply; 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; case 7: if (request->parent) { vps = request->parent->config_items; } break; default: /* WTF? */ return 0; } /* * The "format" string is the attribute name. */ da = dict_attrbyname(fmt); 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 <= 1) break; } *out = '\0'; 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--; } /* * 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. * * @bug 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, da->vendor, packet->code); if (dval) { snprintf(out, outlen, "%s", dval->name); } else { snprintf(out, outlen, "%d", packet->code); } 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); } /* * Not found, die. */ return 0; } if (!vps) return 0; /* silently fail */ /* * Convert the VP to a string, and return it. */ return valuepair2str(out, outlen, vp, da->type, func); }