/** * @brief Print data as integer, not as VALUE. */ static size_t xlat_integer(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { VALUE_PAIR *vp; while (isspace((int) *fmt)) fmt++; if (!radius_get_vp(request, fmt, &vp) || !vp) { *out = '\0'; return 0; } if ((vp->type != PW_TYPE_IPADDR) && (vp->type != PW_TYPE_INTEGER) && (vp->type != PW_TYPE_INTEGER64) && (vp->type != PW_TYPE_SHORT) && (vp->type != PW_TYPE_BYTE) && (vp->type != PW_TYPE_DATE)) { *out = '\0'; return 0; } if (vp->type == PW_TYPE_INTEGER64) { return snprintf(out, outlen, "%llu", vp->vp_integer64); } return snprintf(out, outlen, "%u", vp->vp_integer); }
/** * @brief Print data as hex, not as VALUE. */ static size_t xlat_hex(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { size_t i; VALUE_PAIR *vp; uint8_t buffer[MAX_STRING_LEN]; ssize_t ret; size_t len; while (isspace((int) *fmt)) fmt++; if (!radius_get_vp(request, fmt, &vp) || !vp) { *out = '\0'; return 0; } ret = rad_vp2data(vp, buffer, sizeof(buffer)); len = (size_t) ret; /* * Don't truncate the data. */ if ((ret < 0 ) || (outlen < (len * 2))) { *out = 0; return 0; } for (i = 0; i < len; i++) { snprintf(out + 2*i, 3, "%02x", buffer[i]); } return len * 2; }
/** * @brief Print data as string, if possible. * * If attribute "Foo" is defined as "octets" it will normally * be printed as 0x0a0a0a. The xlat "%{string:Foo}" will instead * expand to "\n\n\n" */ static size_t xlat_string(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { int len; VALUE_PAIR *vp; while (isspace((int) *fmt)) fmt++; if (outlen < 3) { nothing: *out = '\0'; return 0; } if (!radius_get_vp(request, fmt, &vp)) goto nothing; if (!vp) goto nothing; if (vp->type != PW_TYPE_OCTETS) goto nothing; len = fr_print_string(vp->vp_strvalue, vp->length, out, outlen); out[len] = '\0'; return len; }
/** * @brief Print data as hex, not as VALUE. */ static size_t xlat_hex(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { size_t i; uint8_t *p; VALUE_PAIR *vp; while (isspace((int) *fmt)) fmt++; if (!radius_get_vp(request, fmt, &vp) || !vp) { *out = '\0'; return 0; } /* * Don't truncate the data. */ if (outlen < (vp->length * 2)) { *out = 0; return 0; } p = &vp->vp_octets[0]; for (i = 0; i < vp->length; i++) { snprintf(out + 2*i, 3, "%02x", p[i]); } return vp->length * 2; }
/** Print the size of the attribute in bytes. * */ static ssize_t xlat_length(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt, char *out, size_t outlen) { VALUE_PAIR *vp; while (isspace((int) *fmt)) fmt++; if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) { *out = '\0'; return 0; } snprintf(out, outlen, "%zu", vp->length); return strlen(out); }
/** xlat expand string attribute value * */ static size_t xlat_xlat(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { VALUE_PAIR *vp; while (isspace((int) *fmt)) fmt++; if (outlen < 3) { nothing: *out = '\0'; return 0; } if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) goto nothing; return radius_xlat(out, outlen, vp->vp_strvalue, request, NULL, NULL); }
/** Crappy temporary function to add attribute ref support to xlats * * This needs to die, and hopefully will die, when xlat functions accept * xlat node structures. * * Provides either a pointer to a buffer which contains the value of the reference VALUE_PAIR * in an architecture independent format. Or a pointer to the start of the fmt string. * * The pointer is only guaranteed to be valid between calls to xlat_fmt_to_ref, * and so long as the source VALUE_PAIR is not freed. * * @param out where to write a pointer to the buffer to the data the xlat function needs to work on. * @param request current request. * @param fmt string. * @returns the length of the data or -1 on error. */ ssize_t xlat_fmt_to_ref(uint8_t const **out, REQUEST *request, char const *fmt) { VALUE_PAIR *vp; while (isspace((int) *fmt)) fmt++; if (fmt[0] == '&') { if ((radius_get_vp(&vp, request, fmt + 1) < 0) || !vp) { *out = NULL; return -1; } return rad_vp2data(out, vp); } *out = (uint8_t const *)fmt; return strlen(fmt); }
/** Print data as integer, not as VALUE. * */ static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { VALUE_PAIR *vp; uint64_t integer; while (isspace((int) *fmt)) fmt++; if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) { *out = '\0'; return 0; } switch (vp->da->type) { case PW_TYPE_OCTETS: case PW_TYPE_STRING: if (vp->length > 8) { break; } memcpy(&integer, &(vp->vp_octets), vp->length); return snprintf(out, outlen, "%" PRIu64, ntohll(integer)); case PW_TYPE_INTEGER64: return snprintf(out, outlen, "%" PRIu64, vp->vp_integer64); case PW_TYPE_IPADDR: case PW_TYPE_INTEGER: case PW_TYPE_SHORT: case PW_TYPE_BYTE: case PW_TYPE_DATE: return snprintf(out, outlen, "%u", vp->vp_integer); default: break; } REDEBUG("Type \"%s\" cannot be converted to integer", fr_int2str(dict_attr_types, vp->da->type, PW_TYPE_INVALID)); *out = '\0'; return -1; }
/** Print data as integer, not as VALUE. * */ static size_t xlat_integer(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { VALUE_PAIR *vp; uint64_t integer; while (isspace((int) *fmt)) fmt++; if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) { *out = '\0'; return 0; } switch (vp->da->type) { case PW_TYPE_OCTETS: case PW_TYPE_STRING: if (vp->length > 8) { break; } memcpy(&integer, &(vp->vp_octets), vp->length); return snprintf(out, outlen, "%llu", ntohll(integer)); case PW_TYPE_INTEGER64: return snprintf(out, outlen, "%llu", vp->vp_integer64); case PW_TYPE_IPADDR: case PW_TYPE_INTEGER: case PW_TYPE_SHORT: case PW_TYPE_BYTE: case PW_TYPE_DATE: return snprintf(out, outlen, "%u", vp->vp_integer); default: break; } *out = '\0'; return 0; }
/* * Allow single attribute values to be retrieved from the dhcp. */ static size_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace) { vp_cursor_t cursor; VALUE_PAIR *vp, *head = NULL; int decoded = 0; while (isspace((int) *fmt)) fmt++; if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) { *out = '\0'; return 0; } if ((fr_dhcp_decode_options(request->packet, vp->vp_octets, vp->length, &head) < 0) || (!head)) { RWDEBUG("DHCP option decoding failed"); goto fail; } for (vp = paircursor(&cursor, &head); vp; vp = pairnext(&cursor)) { decoded++; } pairmove(request->packet, &(request->packet->vps), &head); /* Free any unmoved pairs */ pairfree(&head); fail: snprintf(out, freespace, "%i", decoded); return strlen(out); }
/** Print data as string, if possible. * * If attribute "Foo" is defined as "octets" it will normally * be printed as 0x0a0a0a. The xlat "%{string:Foo}" will instead * expand to "\n\n\n" */ static ssize_t xlat_string(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { size_t len; ssize_t ret; VALUE_PAIR *vp; uint8_t const *p; while (isspace((int) *fmt)) fmt++; if (*fmt == '&') fmt++; if (outlen < 3) { nothing: *out = '\0'; return 0; } if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing; ret = rad_vp2data(&p, vp); if (ret < 0) { return ret; } switch (vp->da->type) { case PW_TYPE_OCTETS: len = fr_print_string((char const *) p, vp->length, out, outlen); break; case PW_TYPE_STRING: len = strlcpy(out, vp->vp_strvalue, outlen); break; default: len = fr_print_string((char const *) p, ret, out, outlen); break; } return len; }
/* * Allow single attribute values to be retrieved from the dhcp. */ static size_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t freespace) { VALUE_PAIR *vp, *head = NULL, *next; int decoded = 0; while (isspace((int) *fmt)) fmt++; if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) { *out = '\0'; return 0; } if ((fr_dhcp_decode_options(vp->vp_octets, vp->length, &head) < 0) || (head == NULL)) { RDEBUG("WARNING: DHCP option decoding failed"); goto fail; } next = head; do { next = next->next; decoded++; } while (next); pairmove(&(request->packet->vps), &head); /* Free any unmoved pairs */ pairfree(&head); fail: snprintf(out, freespace, "%i", decoded); return strlen(out); }
/** Print data as base64, not as VALUE * */ static size_t xlat_base64(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { VALUE_PAIR *vp; uint8_t buffer[MAX_STRING_LEN]; ssize_t ret; while (isspace((int) *fmt)) fmt++; if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) { *out = '\0'; return 0; } ret = rad_vp2data(vp, buffer, sizeof(buffer)); if (ret < 0) { *out = 0; return 0; } return fr_base64_encode(buffer, (size_t) ret, out, outlen); }
/** * @brief Print data as base64, not as VALUE */ static size_t xlat_base64(UNUSED void *instance, REQUEST *request, char *fmt, char *out, size_t outlen, UNUSED RADIUS_ESCAPE_STRING func) { VALUE_PAIR *vp; uint8_t buffer[MAX_STRING_LEN]; ssize_t ret; size_t len; size_t enc; while (isspace((int) *fmt)) fmt++; if (!radius_get_vp(request, fmt, &vp) || !vp) { *out = '\0'; return 0; } ret = rad_vp2data(vp, buffer, sizeof(buffer)); if (ret < 0) { *out = 0; return 0; } len = (size_t) ret; enc = FR_BASE64_ENC_LENGTH(len); /* * Don't truncate the data. */ if (outlen < (enc + 1)) { *out = 0; return 0; } fr_base64_encode(buffer, len, out, outlen); return enc; }
/** Print data as hex, not as VALUE. * */ static ssize_t xlat_hex(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { size_t i; VALUE_PAIR *vp; uint8_t const *p; ssize_t ret; size_t len; while (isspace((int) *fmt)) fmt++; if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) { *out = '\0'; return -1; } ret = rad_vp2data(&p, vp); if (ret < 0) { return ret; } len = (size_t) ret; /* * Don't truncate the data. */ if ((ret < 0 ) || (outlen < (len * 2))) { *out = 0; return 0; } for (i = 0; i < len; i++) { snprintf(out + 2*i, 3, "%02x", p[i]); } return len * 2; }
/* * *presult is "did comparison match or not" */ static int radius_do_cmp(REQUEST *request, int *presult, FR_TOKEN lt, const char *pleft, FR_TOKEN token, FR_TOKEN rt, const char *pright, int cflags, int modreturn) { int result; uint32_t lint, rint; VALUE_PAIR *vp = NULL; #ifdef HAVE_REGEX_H char buffer[8192]; #else cflags = cflags; /* -Wunused */ #endif rt = rt; /* -Wunused */ if (lt == T_BARE_WORD) { /* * Maybe check the last return code. */ if (token == T_OP_CMP_TRUE) { int isreturn; /* * Looks like a return code, treat is as such. */ isreturn = fr_str2int(modreturn_table, pleft, -1); if (isreturn != -1) { *presult = (modreturn == isreturn); return TRUE; } } /* * Bare words on the left can be attribute names. */ if (radius_get_vp(request, pleft, &vp)) { VALUE_PAIR myvp; /* * VP exists, and that's all we're looking for. */ if (token == T_OP_CMP_TRUE) { *presult = (vp != NULL); return TRUE; } if (!vp) { DICT_ATTR *da; /* * The attribute on the LHS may * have been a dynamically * registered callback. i.e. it * doesn't exist as a VALUE_PAIR. * If so, try looking for it. */ da = dict_attrbyname(pleft); if (da && (da->vendor == 0) && radius_find_compare(da->attr)) { VALUE_PAIR *check = pairmake(pleft, pright, token); *presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0); RDEBUG3(" Callback returns %d", *presult); pairfree(&check); return TRUE; } RDEBUG2(" (Attribute %s was not found)", pleft); *presult = 0; return TRUE; } #ifdef HAVE_REGEX_H /* * Regex comparisons treat everything as * strings. */ if ((token == T_OP_REG_EQ) || (token == T_OP_REG_NE)) { vp_prints_value(buffer, sizeof(buffer), vp, 0); pleft = buffer; goto do_checks; } #endif memcpy(&myvp, vp, sizeof(myvp)); if (!pairparsevalue(&myvp, pright)) { RDEBUG2("Failed parsing \"%s\": %s", pright, fr_strerror()); return FALSE; } myvp.operator = token; *presult = paircmp(&myvp, vp); RDEBUG3(" paircmp -> %d", *presult); return TRUE; } /* else it's not a VP in a list */ } #ifdef HAVE_REGEX_H do_checks: #endif switch (token) { case T_OP_GE: case T_OP_GT: case T_OP_LE: case T_OP_LT: if (!all_digits(pright)) { RDEBUG2(" (Right field is not a number at: %s)", pright); return FALSE; } rint = strtoul(pright, NULL, 0); if (!all_digits(pleft)) { RDEBUG2(" (Left field is not a number at: %s)", pleft); return FALSE; } lint = strtoul(pleft, NULL, 0); break; default: lint = rint = 0; /* quiet the compiler */ break; } switch (token) { case T_OP_CMP_TRUE: /* * Check for truth or falsehood. */ if (all_digits(pleft)) { lint = strtoul(pleft, NULL, 0); result = (lint != 0); } else { result = (*pleft != '\0'); } break; case T_OP_CMP_EQ: result = (strcmp(pleft, pright) == 0); break; case T_OP_NE: result = (strcmp(pleft, pright) != 0); break; case T_OP_GE: result = (lint >= rint); break; case T_OP_GT: result = (lint > rint); break; case T_OP_LE: result = (lint <= rint); break; case T_OP_LT: result = (lint < rint); break; #ifdef HAVE_REGEX_H case T_OP_REG_EQ: { int i, compare; regex_t reg; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* * Include substring matches. */ compare = regcomp(®, pright, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); DEBUG("ERROR: Failed compiling regular expression: %s", errbuf); } return FALSE; } compare = regexec(®, pleft, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); /* * Add new %{0}, %{1}, etc. */ if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) { char *r; free(request_data_get(request, request, REQUEST_DATA_REGEX | i)); /* * No %{i}, skip it. * We MAY have %{2} without %{1}. */ if (rxmatch[i].rm_so == -1) continue; /* * Copy substring into allocated buffer */ r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1); memcpy(r, pleft + rxmatch[i].rm_so, rxmatch[i].rm_eo - rxmatch[i].rm_so); r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0'; request_data_add(request, request, REQUEST_DATA_REGEX | i, r, free); } result = (compare == 0); } break; case T_OP_REG_NE: { int compare; regex_t reg; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* * Include substring matches. */ compare = regcomp(®, pright, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); DEBUG("ERROR: Failed compiling regular expression: %s", errbuf); } return FALSE; } compare = regexec(®, pleft, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); result = (compare != 0); } break; #endif default: DEBUG("ERROR: Comparison operator %s is not supported", fr_token_name(token)); result = FALSE; break; } *presult = result; return TRUE; }
/** Print data as integer, not as VALUE. * */ static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { VALUE_PAIR *vp; uint64_t int64 = 0; /* Needs to be initialised to zero */ uint32_t int32 = 0; /* Needs to be initialised to zero */ while (isspace((int) *fmt)) fmt++; if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) { *out = '\0'; return 0; } switch (vp->da->type) { case PW_TYPE_OCTETS: case PW_TYPE_STRING: if (vp->length > 8) { break; } if (vp->length > 4) { memcpy(&int64, vp->vp_octets, vp->length); return snprintf(out, outlen, "%" PRIu64, htonll(int64)); } memcpy(&int32, vp->vp_octets, vp->length); return snprintf(out, outlen, "%i", htonl(int32)); case PW_TYPE_INTEGER64: return snprintf(out, outlen, "%" PRIu64, vp->vp_integer64); /* * IP addresses are treated specially, as parsing functions assume the value * is bigendian and will convert it for us. */ case PW_TYPE_IPADDR: return snprintf(out, outlen, "%u", htonl(vp->vp_ipaddr)); case PW_TYPE_INTEGER: case PW_TYPE_DATE: case PW_TYPE_BYTE: case PW_TYPE_SHORT: return snprintf(out, outlen, "%u", vp->vp_integer); /* * Ethernet is weird... It's network related, so we assume to it should be * bigendian. */ case PW_TYPE_ETHERNET: memcpy(&int64, &vp->vp_ether, vp->length); return snprintf(out, outlen, "%" PRIu64, htonll(int64)); case PW_TYPE_SIGNED: return snprintf(out, outlen, "%i", vp->vp_signed); default: break; } REDEBUG("Type '%s' of length %zu cannot be converted to integer", fr_int2str(dict_attr_types, vp->da->type, PW_TYPE_INVALID), vp->length); *out = '\0'; return -1; }
/** Unpack data * * Example: %{unpack:&Class 0 integer} * * Expands Class, treating octet at offset 0 (bytes 0-3) as an "integer". */ static ssize_t unpack_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { char *data_name, *data_size, *data_type; char *p; size_t len, input_len; int offset; PW_TYPE type; DICT_ATTR const *da; VALUE_PAIR *vp, *cast; uint8_t const *input; char buffer[256]; uint8_t blob[256]; /* * FIXME: copy only the fields here, as we parse them. */ strlcpy(buffer, fmt, sizeof(buffer)); p = buffer; while (isspace((int) *p)) p++; /* skip leading spaces */ data_name = p; while (*p && !isspace((int) *p)) p++; if (!*p) { error: REDEBUG("Format string should be '<data> <offset> <type>' e.g. '&Class 1 integer'"); nothing: *out = '\0'; return -1; } while (isspace((int) *p)) *(p++) = '\0'; if (!*p) GOTO_ERROR; data_size = p; while (*p && !isspace((int) *p)) p++; if (!*p) GOTO_ERROR; while (isspace((int) *p)) *(p++) = '\0'; if (!*p) GOTO_ERROR; data_type = p; while (*p && !isspace((int) *p)) p++; if (*p) GOTO_ERROR; /* anything after the type is an error */ /* * Attribute reference */ if (*data_name == '&') { if (radius_get_vp(&vp, request, data_name) < 0) goto nothing; if ((vp->da->type != PW_TYPE_OCTETS) && (vp->da->type != PW_TYPE_STRING)) { REDEBUG("unpack requires the input attribute to be 'string' or 'octets'"); goto nothing; } input = vp->vp_octets; input_len = vp->vp_length; } else if ((data_name[0] == '0') && (data_name[1] == 'x')) { /* * Hex data. */ len = strlen(data_name + 2); if ((len & 0x01) != 0) { RDEBUG("Invalid hex string in '%s'", data_name); goto nothing; } input = blob; input_len = fr_hex2bin(blob, sizeof(blob), data_name + 2, len); } else { GOTO_ERROR; } offset = (int) strtoul(data_size, &p, 10); if (*p) { REDEBUG("unpack requires a decimal number, not '%s'", data_size); goto nothing; } type = fr_str2int(dict_attr_types, data_type, PW_TYPE_INVALID); if (type == PW_TYPE_INVALID) { REDEBUG("Invalid data type '%s'", data_type); goto nothing; } /* * Output must be a non-zero limited size. */ if ((dict_attr_sizes[type][0] == 0) || (dict_attr_sizes[type][0] != dict_attr_sizes[type][1])) { REDEBUG("unpack requires fixed-size output type, not '%s'", data_type); goto nothing; } if (input_len < (offset + dict_attr_sizes[type][0])) { REDEBUG("Insufficient data to unpack '%s' from '%s'", data_type, data_name); goto nothing; } da = dict_attrbyvalue(PW_CAST_BASE + type, 0); if (!da) { REDEBUG("Cannot decode type '%s'", data_type); goto nothing; } cast = pairalloc(request, da); if (!cast) goto nothing; memcpy(&(cast->data), input + offset, dict_attr_sizes[type][0]); cast->vp_length = dict_attr_sizes[type][0]; /* * Hacks */ switch (type) { case PW_TYPE_SIGNED: case PW_TYPE_INTEGER: case PW_TYPE_DATE: cast->vp_integer = ntohl(cast->vp_integer); break; case PW_TYPE_SHORT: cast->vp_short = ((input[offset] << 8) | input[offset + 1]); break; case PW_TYPE_INTEGER64: cast->vp_integer64 = ntohll(cast->vp_integer64); break; default: break; } len = vp_prints_value(out, outlen, cast, 0); talloc_free(cast); if (is_truncated(len, outlen)) { REDEBUG("Insufficient buffer space to unpack data"); goto nothing; } return len; }