/** Extract a named subcapture value from the request * * @note This is the PCRE variant of the function. * * @param[in] ctx To allocate subcapture buffer in. * @param[out] out Where to write the subcapture string. * @param[in] request to extract. * @param[in] name of subcapture. * @return * - 0 on success. * - -1 on notfound. */ int regex_request_to_sub_named(TALLOC_CTX *ctx, char **out, REQUEST *request, char const *name) { fr_regcapture_t *rc; char const *p; int ret; rc = request_data_reference(request, request, REQUEST_DATA_REGEX); if (!rc) { RDEBUG4("No subcapture data found"); *out = NULL; return 1; } ret = pcre_get_named_substring(rc->preg->compiled, rc->regmatch->subject, (int *)rc->regmatch->match_data, (int)rc->regmatch->used, name, &p); switch (ret) { case PCRE_ERROR_NOMEMORY: MEM(NULL); /* * We can't really fall through, but GCC 7.3 is * too stupid to realise that we can never get * here despite _fr_exit_now being marked as * NEVER_RETURNS. * * If we did anything else, compilers and static * analysis tools would probably complain about * code that could never be executed *sigh*. */ /* FALL-THROUGH */ /* * Not finding a substring is fine */ case PCRE_ERROR_NOSUBSTRING: RDEBUG4("No named capture group \"%s\"", name); *out = NULL; return -1; default: if (ret < 0) { *out = NULL; return -1; } /* * Check libpcre really is using our overloaded * memory allocation and freeing talloc wrappers. */ talloc_set_type(p, char); talloc_steal(ctx, p); RDEBUG4("Found \"%s\": %pV (%zu)", name, fr_box_strvalue_buffer(p), talloc_array_length(p) - 1); memcpy(out, &p, sizeof(*out)); break; } return 0; }
/** Extract a subcapture value from the request * * @note This is the PCRE variant of the function. * * @param[in] ctx To allocate subcapture buffer in. * @param[out] out Where to write the subcapture string. * @param[in] request to extract. * @param[in] num Subcapture index (0 for entire match). * @return * - 0 on success. * - -1 on notfound. */ int regex_request_to_sub(TALLOC_CTX *ctx, char **out, REQUEST *request, uint32_t num) { fr_regcapture_t *rc; char *buff; size_t len; int ret; pcre2_match_data *match_data; rc = request_data_reference(request, request, REQUEST_DATA_REGEX); if (!rc) { RDEBUG4("No subcapture data found"); *out = NULL; return 1; } match_data = talloc_get_type_abort(rc->regmatch->match_data, pcre2_match_data); ret = pcre2_substring_length_bynumber(match_data, num, &len); switch (ret) { case PCRE2_ERROR_NOMEMORY: MEM(NULL); /* * We can't really fall through, but GCC 7.3 is * too stupid to realise that we can never get * here despite _fr_exit_now being marked as * NEVER_RETURNS. * * If we did anything else, compilers and static * analysis tools would probably complain about * code that could never be executed *sigh*. */ /* FALL-THROUGH */ /* * Not finding a substring is fine */ case PCRE2_ERROR_NOSUBSTRING: RDEBUG4("%i/%zu Not found", num + 1, rc->regmatch->used); *out = NULL; return -1; default: if (ret < 0) { *out = NULL; return -1; } MEM(buff = talloc_array(ctx, char, ++len)); /* +1 for \0, it'll get reset by pcre2_substring */ pcre2_substring_copy_bynumber(match_data, num, (PCRE2_UCHAR *)buff, &len); /* can't error */ RDEBUG4("%i/%zu Found: %pV (%zu)", num + 1, rc->regmatch->used, fr_box_strvalue_buffer(buff), talloc_array_length(buff) - 1); *out = buff; break; } return 0; }
/** 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); }
/** Extract a subcapture value from the request * * @note This is the POSIX variant of the function. * * @param[in] ctx To allocate subcapture buffer in. * @param[out] out Where to write the subcapture string. * @param[in] request to extract. * @param[in] num Subcapture index (0 for entire match). * @return * - 0 on success. * - -1 on notfound. */ int regex_request_to_sub(TALLOC_CTX *ctx, char **out, REQUEST *request, uint32_t num) { fr_regcapture_t *rc; char *buff; char const *start; size_t len; regmatch_t *match_data; rc = request_data_reference(request, request, REQUEST_DATA_REGEX); if (!rc) { RDEBUG4("No subcapture data found"); *out = NULL; return -1; } match_data = rc->regmatch->match_data; /* * Greater than our capture array * * -1 means no value in this capture group. */ if ((num >= rc->regmatch->used) || (match_data[num].rm_eo == -1) || (match_data[num].rm_so == -1)) { RDEBUG4("%i/%zu Not found", num + 1, rc->regmatch->used); *out = NULL; return -1; } /* * Sanity checks on the offsets */ rad_assert(match_data[num].rm_eo <= (regoff_t)talloc_array_length(rc->regmatch->subject)); rad_assert(match_data[num].rm_so <= (regoff_t)talloc_array_length(rc->regmatch->subject)); start = rc->regmatch->subject + match_data[num].rm_so; len = match_data[num].rm_eo - match_data[num].rm_so; MEM(buff = talloc_bstrndup(ctx, start, len)); RDEBUG4("%i/%zu Found: %pV (%zu)", num + 1, rc->regmatch->used, fr_box_strvalue_buffer(buff), len); *out = buff; return 0; }
/* * @brief Expand regexp matches %{0} to %{8} */ static size_t xlat_regex(void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { char *regex; /* * We cheat: fmt is "0" to "8", but those numbers * are already in the "instance". */ fmt = fmt; /* -Wunused */ regex = request_data_reference(request, request, REQUEST_DATA_REGEX | *(int *)instance); if (!regex) return 0; /* * Copy UP TO "freespace" bytes, including * a zero byte. */ strlcpy(out, regex, outlen); return strlen(out); }
static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node, RADIUS_ESCAPE_STRING escape, void *escape_ctx, int lvl) { ssize_t rcode; char *str = NULL, *child; REQUEST *ref; XLAT_DEBUG("%.*sxlat aprint %d", lvl, xlat_spaces, node->type); switch (node->type) { /* * Don't escape this. */ case XLAT_LITERAL: XLAT_DEBUG("xlat_aprint LITERAL"); return talloc_strdup(ctx, node->fmt); /* * Do a one-character expansion. */ case XLAT_PERCENT: { char const *p; char *nl; size_t freespace = 256; struct tm ts; time_t when; XLAT_DEBUG("xlat_aprint PERCENT"); str = talloc_array(ctx, char, freespace); /* @todo do better allocation */ p = node->fmt; when = request->timestamp; if (request->packet) { when = request->packet->timestamp.tv_sec; } switch (*p) { case '%': str[0] = '%'; str[1] = '\0'; break; case 'd': /* request day */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%d", &ts); break; case 'l': /* request timestamp */ snprintf(str, freespace, "%lu", (unsigned long) when); break; case 'm': /* request month */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%m", &ts); break; case 't': /* request timestamp */ CTIME_R(&when, str, freespace); nl = strchr(str, '\n'); if (nl) *nl = '\0'; break; case 'D': /* request date */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%Y%m%d", &ts); break; case 'G': /* request minute */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%M", &ts); break; case 'H': /* request hour */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%H", &ts); break; case 'I': /* Request ID */ if (request->packet) { snprintf(str, freespace, "%i", request->packet->id); } break; case 'S': /* request timestamp in SQL format*/ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%Y-%m-%d %H:%M:%S", &ts); break; case 'T': /* request timestamp */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%Y-%m-%d-%H.%M.%S.000000", &ts); break; case 'Y': /* request year */ if (!localtime_r(&when, &ts)) { error: REDEBUG("Failed converting packet timestamp to localtime: %s", fr_syserror(errno)); talloc_free(str); return NULL; } strftime(str, freespace, "%Y", &ts); break; default: rad_assert(0 == 1); break; } } break; case XLAT_ATTRIBUTE: XLAT_DEBUG("xlat_aprint ATTRIBUTE"); ref = request; if (radius_request(&ref, node->ref) < 0) { return NULL; } /* * Some attributes are virtual <sigh> */ str = xlat_getvp(ctx, ref, node->list, node->da, node->tag, node->num, true); if (str) { XLAT_DEBUG("expand attr %s --> '%s'", node->da->name, str); } break; case XLAT_VIRTUAL: XLAT_DEBUG("xlat_aprint VIRTUAL"); str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */ rcode = node->xlat->func(node->xlat->instance, request, NULL, str, 1024); if (rcode < 0) { talloc_free(str); return NULL; } break; case XLAT_MODULE: XLAT_DEBUG("xlat_aprint MODULE"); if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) { return NULL; } XLAT_DEBUG("%.*sexpand mod %s --> '%s'", lvl, xlat_spaces, node->fmt, child); str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */ *str = '\0'; /* Be sure the string is NULL terminated, we now only free on error */ rcode = node->xlat->func(node->xlat->instance, request, child, str, 1024); talloc_free(child); if (rcode < 0) { talloc_free(str); return NULL; } break; #ifdef HAVE_REGEX_H case XLAT_REGEX: XLAT_DEBUG("xlat_aprint REGEX"); child = request_data_reference(request, request, REQUEST_DATA_REGEX | node->num); if (!child) return NULL; str = talloc_strdup(ctx, child); break; #endif case XLAT_ALTERNATE: XLAT_DEBUG("xlat_aprint ALTERNATE"); rad_assert(node->child != NULL); rad_assert(node->alternate != NULL); str = xlat_aprint(ctx, request, node->child, escape, escape_ctx, lvl); if (str) break; str = xlat_aprint(ctx, request, node->alternate, escape, escape_ctx, lvl); break; } /* * Escape the non-literals we found above. */ if (str && escape) { char *escaped; escaped = talloc_array(ctx, char, 1024); /* FIXME: do something intelligent */ escape(request, escaped, 1024, str, escape_ctx); talloc_free(str); str = escaped; }
static unlang_action_t unlang_foreach(REQUEST *request, rlm_rcode_t *presult, int *priority) { VALUE_PAIR *vp; unlang_stack_t *stack = request->stack; unlang_stack_frame_t *frame = &stack->frame[stack->depth]; unlang_t *instruction = frame->instruction; unlang_frame_state_foreach_t *foreach = NULL; unlang_group_t *g; g = unlang_generic_to_group(instruction); if (!frame->repeat) { int i, foreach_depth = -1; VALUE_PAIR *vps; if (stack->depth >= UNLANG_STACK_MAX) { ERROR("Internal sanity check failed: module stack is too deep"); fr_exit(EXIT_FAILURE); } /* * Figure out how deep we are in nesting by looking at request_data * stored previously. * * FIXME: figure this out by walking up the modcall stack instead. */ for (i = 0; i < 8; i++) { if (!request_data_reference(request, (void *)xlat_fmt_get_vp, i)) { foreach_depth = i; break; } } if (foreach_depth < 0) { REDEBUG("foreach Nesting too deep!"); *presult = RLM_MODULE_FAIL; *priority = 0; return UNLANG_ACTION_CALCULATE_RESULT; } /* * Copy the VPs from the original request, this ensures deterministic * behaviour if someone decides to add or remove VPs in the set we're * iterating over. */ if (tmpl_copy_vps(stack, &vps, request, g->vpt) < 0) { /* nothing to loop over */ *presult = RLM_MODULE_NOOP; *priority = instruction->actions[RLM_MODULE_NOOP]; return UNLANG_ACTION_CALCULATE_RESULT; } MEM(frame->state = foreach = talloc_zero(stack, unlang_frame_state_foreach_t)); rad_assert(vps != NULL); foreach->depth = foreach_depth; foreach->vps = vps; fr_cursor_talloc_init(&foreach->cursor, &foreach->vps, VALUE_PAIR); #ifndef NDEBUG foreach->indent = request->log.unlang_indent; #endif vp = fr_cursor_head(&foreach->cursor); } else { foreach = talloc_get_type_abort(frame->state, unlang_frame_state_foreach_t); vp = fr_cursor_next(&foreach->cursor); /* * We've been asked to unwind to the * enclosing "foreach". We're here, so * we can stop unwinding. */ if (frame->unwind == UNLANG_TYPE_BREAK) { frame->unwind = UNLANG_TYPE_NULL; vp = NULL; } /* * Unwind all the way. */ if (frame->unwind == UNLANG_TYPE_RETURN) { vp = NULL; } if (!vp) { /* * Free the copied vps and the request data * If we don't remove the request data, something could call * the xlat outside of a foreach loop and trigger a segv. */ fr_pair_list_free(&foreach->vps); request_data_get(request, (void *)xlat_fmt_get_vp, foreach->depth); *presult = frame->result; if (*presult != RLM_MODULE_UNKNOWN) *priority = instruction->actions[*presult]; #ifndef NDEBUG rad_assert(foreach->indent == request->log.unlang_indent); #endif return UNLANG_ACTION_CALCULATE_RESULT; } } #ifndef NDEBUG RDEBUG2(""); RDEBUG2("# looping with: Foreach-Variable-%d = %pV", foreach->depth, &vp->data); #endif rad_assert(vp); /* * Add the vp to the request, so that * xlat.c, xlat_foreach() can find it. */ foreach->variable = vp; request_data_add(request, (void *)xlat_fmt_get_vp, foreach->depth, &foreach->variable, false, false, false); /* * Push the child, and yield for a later return. */ unlang_push(stack, g->children, frame->result, UNLANG_NEXT_SIBLING, UNLANG_SUB_FRAME); frame->repeat = true; return UNLANG_ACTION_PUSHED_CHILD; }