/** 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; }
/** Send a fake request to a virtual server, managing the eap_session_t of the child * * If eap_session_t has a child, inject that into the fake request. * * If after the request has run, the child eap_session_t is no longer present, * we assume it has been freed, and fixup the parent eap_session_t. * * If the eap_session_t pointer changes, this is considered a fatal error. * * @param request the current (real) request. * @param eap_session representing the outer eap method. * @param fake request we're going to send. * @param virtual_server The default virtual server to send the request to. * @return the rcode of the last executed section in the virtual server. */ rlm_rcode_t eap_virtual_server(REQUEST *request, REQUEST *fake, eap_session_t *eap_session, char const *virtual_server) { eap_session_t *inner_eap; rlm_rcode_t rcode; VALUE_PAIR *vp; vp = fr_pair_find_by_num(request->config, 0, PW_VIRTUAL_SERVER, TAG_ANY); fake->server = vp ? vp->vp_strvalue : virtual_server; if (fake->server) { RDEBUG2("Sending tunneled request to %s", fake->server); } else { RDEBUG2("Sending tunnelled request"); } /* * Add a previously recorded inner eap_session_t back * to the request. This in theory allows infinite * nesting, but this is probably limited somewhere. */ if (eap_session->child) { RDEBUG4("Adding eap_session_t %p to fake request", eap_session->child); request_data_add(fake, NULL, REQUEST_DATA_EAP_SESSION, eap_session->child, false, false, false); } rcode = rad_virtual_server(fake); inner_eap = request_data_get(fake, NULL, REQUEST_DATA_EAP_SESSION); if (inner_eap) { if (!eap_session->child || (eap_session->child != inner_eap)) { RDEBUG4("Binding lifetime of child eap_session %p to parent eap_session %p", inner_eap, eap_session); fr_talloc_link_ctx(eap_session, inner_eap); eap_session->child = inner_eap; } else { RDEBUG4("Got eap_session_t %p back unmolested", eap_session->child); } /* * Assume the inner server freed the * eap_session_t and remove our reference to it. * * If it didn't actually free the child (due to error) * the call to talloc_link_ctx (above) ensures it will * be freed when the parent is. */ } else if (eap_session->child) { RDEBUG4("Inner server freed eap_session %p", eap_session->child); eap_session->child = NULL; } return rcode; }
/** 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; }
/* * The pairmove() function in src/lib/valuepair.c does all sorts of * extra magic that we don't want here. * * FIXME: integrate this with the code calling it, so that we * only paircopy() those attributes that we're really going to * use. */ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from) { int i, j, count, from_count, to_count, tailto; VALUE_PAIR *vp, *next, **last; VALUE_PAIR **from_list, **to_list; int *edited = NULL; REQUEST *fixup = NULL; /* * Set up arrays for editing, to remove some of the * O(N^2) dependencies. This also makes it easier to * insert and remove attributes. * * It also means that the operators apply ONLY to the * attributes in the original list. With the previous * implementation of pairmove(), adding two attributes * via "+=" and then "=" would mean that the second one * wasn't added, because of the existence of the first * one in the "to" list. This implementation doesn't * have that bug. * * Also, the previous implementation did NOT implement * "-=" correctly. If two of the same attributes existed * in the "to" list, and you tried to subtract something * matching the *second* value, then the pairdelete() * function was called, and the *all* attributes of that * number were deleted. With this implementation, only * the matching attributes are deleted. */ count = 0; for (vp = from; vp != NULL; vp = vp->next) count++; from_list = rad_malloc(sizeof(*from_list) * count); for (vp = *to; vp != NULL; vp = vp->next) count++; to_list = rad_malloc(sizeof(*to_list) * count); /* * Move the lists to the arrays, and break the list * chains. */ from_count = 0; for (vp = from; vp != NULL; vp = next) { next = vp->next; from_list[from_count++] = vp; vp->next = NULL; } to_count = 0; for (vp = *to; vp != NULL; vp = next) { next = vp->next; to_list[to_count++] = vp; vp->next = NULL; } tailto = to_count; edited = rad_malloc(sizeof(*edited) * to_count); memset(edited, 0, sizeof(*edited) * to_count); RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count); /* * Now that we have the lists initialized, start working * over them. */ for (i = 0; i < from_count; i++) { int found; RDEBUG4("::: Examining %s", from_list[i]->name); /* * Attribute should be appended, OR the "to" list * is empty, and we're supposed to replace or * "add if not existing". */ if (from_list[i]->operator == T_OP_ADD) goto append; found = FALSE; for (j = 0; j < to_count; j++) { if (edited[j] || !to_list[j] || !from_list[i]) continue; /* * Attributes aren't the same, skip them. */ if ((from_list[i]->attribute != to_list[j]->attribute) || (from_list[i]->vendor != to_list[j]->vendor)) { continue; } /* * We don't use a "switch" statement here * because we want to break out of the * "for" loop over 'j' in most cases. */ /* * Over-write the FIRST instance of the * matching attribute name. We free the * one in the "to" list, and move over * the one in the "from" list. */ if (from_list[i]->operator == T_OP_SET) { RDEBUG4("::: OVERWRITING %s FROM %d TO %d", to_list[j]->name, i, j); pairfree(&to_list[j]); to_list[j] = from_list[i]; from_list[i] = NULL; edited[j] = TRUE; break; } /* * Add the attribute only if it does not * exist... but it exists, so we stop * looking. */ if (from_list[i]->operator == T_OP_EQ) { found = TRUE; break; } /* * Delete every attribute, independent * of its value. */ if (from_list[i]->operator == T_OP_CMP_FALSE) { goto delete; } /* * Delete all matching attributes from * "to" */ if ((from_list[i]->operator == T_OP_SUB) || (from_list[i]->operator == T_OP_CMP_EQ) || (from_list[i]->operator == T_OP_LE) || (from_list[i]->operator == T_OP_GE)) { int rcode; int old_op = from_list[i]->operator; /* * Check for equality. */ from_list[i]->operator = T_OP_CMP_EQ; /* * If equal, delete the one in * the "to" list. */ rcode = radius_compare_vps(NULL, from_list[i], to_list[j]); /* * We may want to do more * subtractions, so we re-set the * operator back to it's original * value. */ from_list[i]->operator = old_op; switch (old_op) { case T_OP_CMP_EQ: if (rcode != 0) goto delete; break; case T_OP_SUB: if (rcode == 0) { delete: RDEBUG4("::: DELETING %s FROM %d TO %d", from_list[i]->name, i, j); pairfree(&to_list[j]); to_list[j] = NULL; } break; /* * Enforce <=. If it's * >, replace it. */ case T_OP_LE: if (rcode > 0) { RDEBUG4("::: REPLACING %s FROM %d TO %d", from_list[i]->name, i, j); pairfree(&to_list[j]); to_list[j] = from_list[i]; from_list[i] = NULL; edited[j] = TRUE; } break; case T_OP_GE: if (rcode < 0) { RDEBUG4("::: REPLACING %s FROM %d TO %d", from_list[i]->name, i, j); pairfree(&to_list[j]); to_list[j] = from_list[i]; from_list[i] = NULL; edited[j] = TRUE; } break; } continue; } rad_assert(0 == 1); /* panic! */ }
int radius_evaluate_condition(REQUEST *request, int modreturn, int depth, const char **ptr, int evaluate_it, int *presult) { int found_condition = FALSE; int result = TRUE; int invert = FALSE; int evaluate_next_condition = evaluate_it; const char *p; const char *q, *start; FR_TOKEN token, lt, rt; char left[1024], right[1024], comp[4]; const char *pleft, *pright; char xleft[1024], xright[1024]; int cflags = 0; if (!ptr || !*ptr || (depth >= 64)) { radlog(L_ERR, "Internal sanity check failed in evaluate condition"); return FALSE; } /* * Horrible parser. */ p = *ptr; while (*p) { while ((*p == ' ') || (*p == '\t')) p++; /* * ! EXPR */ if (!found_condition && (*p == '!')) { /* * Don't change the results if we're not * evaluating the condition. */ if (evaluate_next_condition) { RDEBUG4(">>> INVERT"); invert = TRUE; } p++; while ((*p == ' ') || (*p == '\t')) p++; } /* * ( EXPR ) */ if (!found_condition && (*p == '(')) { const char *end = p + 1; /* * Evaluate the condition, bailing out on * parse error. */ RDEBUG4(">>> RECURSING WITH ... %s", end); if (!radius_evaluate_condition(request, modreturn, depth + 1, &end, evaluate_next_condition, &result)) { return FALSE; } if (invert) { if (evaluate_next_condition) { RDEBUG2("%.*s Converting !%s -> %s", depth, filler, (result != FALSE) ? "TRUE" : "FALSE", (result == FALSE) ? "TRUE" : "FALSE"); result = (result == FALSE); } invert = FALSE; } /* * Start from the end of the previous * condition */ p = end; RDEBUG4(">>> AFTER RECURSION ... %s", end); while ((*p == ' ') || (*p == '\t')) p++; if (!*p) { radlog(L_ERR, "No closing brace"); return FALSE; } if (*p == ')') p++; /* eat closing brace */ found_condition = TRUE; while ((*p == ' ') || (*p == '\t')) p++; } /* * At EOL or closing brace, update && return. */ if (found_condition && (!*p || (*p == ')'))) break; /* * Now it's either: * * WORD * WORD1 op WORD2 * && EXPR * || EXPR */ if (found_condition) { /* * (A && B) means "evaluate B * only if A was true" */ if ((p[0] == '&') && (p[1] == '&')) { if (!result) evaluate_next_condition = FALSE; p += 2; found_condition = FALSE; continue; /* go back to the start */ } /* * (A || B) means "evaluate B * only if A was false" */ if ((p[0] == '|') && (p[1] == '|')) { if (result) evaluate_next_condition = FALSE; p += 2; found_condition = FALSE; continue; } /* * It must be: * * WORD * WORD1 op WORD2 */ } if (found_condition) { radlog(L_ERR, "Consecutive conditions at %s", p); return FALSE; } RDEBUG4(">>> LOOKING AT %s", p); start = p; /* * Look for common errors. */ if ((p[0] == '%') && (p[1] == '{')) { radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p); return FALSE; } /* * Look for WORD1 op WORD2 */ lt = gettoken(&p, left, sizeof(left)); if ((lt != T_BARE_WORD) && (lt != T_DOUBLE_QUOTED_STRING) && (lt != T_SINGLE_QUOTED_STRING) && (lt != T_BACK_QUOTED_STRING)) { radlog(L_ERR, "Expected string or numbers at: %s", p); return FALSE; } pleft = left; if (evaluate_next_condition) { pleft = expand_string(xleft, sizeof(xleft), request, lt, left); if (!pleft) { radlog(L_ERR, "Failed expanding string at: %s", left); return FALSE; } } /* * Peek ahead, to see if it's: * * WORD * * or something else, such as * * WORD1 op WORD2 * WORD ) * WORD && EXPR * WORD || EXPR */ q = p; while ((*q == ' ') || (*q == '\t')) q++; /* * If the next thing is: * * EOL * end of condition * && * || * * Then WORD is just a test for existence. * Remember that and skip ahead. */ if (!*q || (*q == ')') || ((q[0] == '&') && (q[1] == '&')) || ((q[0] == '|') && (q[1] == '|'))) { token = T_OP_CMP_TRUE; rt = T_OP_INVALID; pright = NULL; goto do_cmp; } /* * Otherwise, it's: * * WORD1 op WORD2 */ token = gettoken(&p, comp, sizeof(comp)); if ((token < T_OP_NE) || (token > T_OP_CMP_EQ) || (token == T_OP_CMP_TRUE)) { radlog(L_ERR, "Expected comparison at: %s", comp); return FALSE; } /* * Look for common errors. */ if ((p[0] == '%') && (p[1] == '{')) { radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p); return FALSE; } /* * Validate strings. */ #ifdef HAVE_REGEX_H if ((token == T_OP_REG_EQ) || (token == T_OP_REG_NE)) { rt = getregex(&p, right, sizeof(right), &cflags); if (rt != T_DOUBLE_QUOTED_STRING) { radlog(L_ERR, "Expected regular expression at: %s", p); return FALSE; } } else #endif rt = gettoken(&p, right, sizeof(right)); if ((rt != T_BARE_WORD) && (rt != T_DOUBLE_QUOTED_STRING) && (rt != T_SINGLE_QUOTED_STRING) && (rt != T_BACK_QUOTED_STRING)) { radlog(L_ERR, "Expected string or numbers at: %s", p); return FALSE; } pright = right; if (evaluate_next_condition) { pright = expand_string(xright, sizeof(xright), request, rt, right); if (!pright) { radlog(L_ERR, "Failed expanding string at: %s", right); return FALSE; } } RDEBUG4(">>> %d:%s %d %d:%s", lt, pleft, token, rt, pright); do_cmp: if (evaluate_next_condition) { /* * More parse errors. */ if (!radius_do_cmp(request, &result, lt, pleft, token, rt, pright, cflags, modreturn)) { return FALSE; } RDEBUG4(">>> Comparison returned %d", result); if (invert) { RDEBUG4(">>> INVERTING result"); result = (result == FALSE); } RDEBUG2("%.*s Evaluating %s(%.*s) -> %s", depth, filler, invert ? "!" : "", p - start, start, (result != FALSE) ? "TRUE" : "FALSE"); invert = FALSE; RDEBUG4(">>> GOT result %d", result); /* * Not evaluating it. We may be just * parsing it. */ } else if (request) { RDEBUG2("%.*s Skipping %s(%.*s)", depth, filler, invert ? "!" : "", p - start, start); } found_condition = TRUE; } /* loop over the input condition */ if (!found_condition) { radlog(L_ERR, "Syntax error. Expected condition at %s", p); return FALSE; } RDEBUG4(">>> AT EOL -> %d", result); *ptr = p; if (evaluate_it) *presult = result; return TRUE; }
/** Print out attribute info * * Prints out all instances of a current attribute, or all attributes in a list. * * At higher debugging levels, also prints out alternative decodings of the same * value. This is helpful to determine types for unknown attributes of long * passed vendors, or just crazy/broken NAS. * * It's also useful for exposing issues in the packet decoding functions, as in * some cases they get fed random garbage data. * * This expands to a zero length string. */ static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, UNUSED size_t outlen) { VALUE_PAIR *vp, **vps; REQUEST *current; value_pair_tmpl_t vpt; vp_cursor_t cursor; char buffer[1024]; if (!RDEBUG_ENABLED2) { *out = '\0'; return -1; } while (isspace((int) *fmt)) fmt++; if (*fmt == '&') fmt++; if (radius_parse_attr(fmt, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { return -1; } current = request; if (radius_request(¤t, vpt.request) < 0) return -2; vps = radius_list(current, vpt.list); if (!vps) { return -2; } RIDEBUG("Attributes matching \"%s\"", fmt); vp = fr_cursor_init(&cursor, vps); if (vpt.da) { vp = fr_cursor_next_by_num(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY); } while (vp) { DICT_ATTR *dac = NULL; DICT_VENDOR *dv; VALUE_PAIR *vpc = NULL; FR_NAME_NUMBER const *type; vp_prints_value(buffer, sizeof(buffer), vp, '\''); if (vp->da->flags.has_tag) { RIDEBUG2("\t%s:%s:%i %s %s", fr_int2str(pair_lists, vpt.list, "<INVALID>"), vp->da->name, vp->tag, fr_int2str(fr_tokens, vp->op, "<INVALID>"), buffer); } else { RIDEBUG2("\t%s:%s %s %s", fr_int2str(pair_lists, vpt.list, "<INVALID>"), vp->da->name, fr_int2str(fr_tokens, vp->op, "<INVALID>"), buffer); } if (!RDEBUG_ENABLED3) { goto next_vp; } if (vp->da->vendor) { dv = dict_vendorbyvalue(vp->da->vendor); RDEBUG3("\t\tvendor : %i (%s)", vp->da->vendor, dv ? dv->name : "unknown"); } RDEBUG3("\t\ttype : %s", fr_int2str(dict_attr_types, vp->da->type, "<INVALID>")); RDEBUG3("\t\tlength : %zu", vp->length); dac = talloc_memdup(request, vp->da, sizeof(DICT_ATTR)); if (!dac) { return -1; } dac->flags.vp_free = 0; if (!RDEBUG_ENABLED4) { goto next_vp; } type = dict_attr_types; while (type->name) { int pad; ssize_t len; uint8_t const *data = NULL; vpc = NULL; if ((PW_TYPE) type->number == vp->da->type) { goto next_type; } switch (type->number) { case PW_TYPE_INVALID: /* Not real type */ case PW_TYPE_MAX: /* Not real type */ case PW_TYPE_EXTENDED: /* Not safe/appropriate */ case PW_TYPE_LONG_EXTENDED: /* Not safe/appropriate */ case PW_TYPE_TLV: /* Not safe/appropriate */ case PW_TYPE_VSA: /* @fixme We need special behaviour for these */ goto next_type; default: break; } dac->type = type->number; len = rad_vp2data(&data, vp); if (len < 0) { goto next_type; } if (data2vp(NULL, NULL, NULL, dac, data, len, len, &vpc) < 0) { goto next_type; } /* * data2vp has knowledge of expected format lengths, if the length * from rad_vp2data doesn't match, it encodes the attribute * as raw octets. This results in many useless debug lines with * the same hex string. */ if ((type->number != PW_TYPE_OCTETS) && (vpc->da->type == PW_TYPE_OCTETS)) { goto next_type; } if (!vp_prints_value(buffer, sizeof(buffer), vpc, '\'')) { goto next_type; } if ((pad = (11 - strlen(type->name))) < 0) { pad = 0; } /* * @fixme: if the value happens to decode as a VSA * (someone put a VSA into a VSA?), we probably to print * extended info for that/reparse */ RDEBUG4("\t\tas %s%*s: %s", type->name, pad, " ", buffer); next_type: talloc_free(vpc); type++; } next_vp: talloc_free(dac); if (vpt.da) { vp = fr_cursor_next_by_num(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY); } else { vp = fr_cursor_next(&cursor); } } *out = '\0'; return 0; }
static unlang_action_t unlang_module(REQUEST *request, rlm_rcode_t *presult, int *priority) { unlang_module_t *sp; unlang_stack_t *stack = request->stack; unlang_stack_frame_t *frame = &stack->frame[stack->depth]; unlang_t *instruction = frame->instruction; unlang_frame_state_module_t *ms; int stack_depth = stack->depth; char const *caller; #ifndef NDEBUG int unlang_indent = request->log.unlang_indent; #endif /* * Process a stand-alone child, and fall through * to dealing with it's parent. */ sp = unlang_generic_to_module(instruction); rad_assert(sp); RDEBUG4("[%i] %s - %s (%s)", stack->depth, __FUNCTION__, sp->module_instance->name, sp->module_instance->module->name); /* * Return administratively configured return code */ if (sp->module_instance->force) { *presult = request->rcode = sp->module_instance->code; goto done; } frame->state = ms = talloc_zero(stack, unlang_frame_state_module_t); /* * Grab the thread/module specific data if any exists. */ ms->thread = module_thread_instance_find(sp->module_instance); rad_assert(ms->thread != NULL); /* * For logging unresponsive children. */ ms->thread->total_calls++; caller = request->module; request->module = sp->module_instance->name; safe_lock(sp->module_instance); /* Noop unless instance->mutex set */ *presult = sp->method(sp->module_instance->dl_inst->data, ms->thread->data, request); safe_unlock(sp->module_instance); request->module = caller; /* * Is now marked as "stop" when it wasn't before, we must have been blocked. */ if (request->master_state == REQUEST_STOP_PROCESSING) { RWARN("Module %s became unblocked", sp->module_instance->module->name); return UNLANG_ACTION_STOP_PROCESSING; } if (*presult == RLM_MODULE_YIELD) { ms->thread->active_callers++; goto done; } /* * Module execution finished, ident should be the same. */ rad_assert(unlang_indent == request->log.unlang_indent); rad_assert(*presult >= RLM_MODULE_REJECT); rad_assert(*presult < RLM_MODULE_NUMCODES); *priority = instruction->actions[*presult]; request->rcode = *presult; done: /* * Must be left at RDEBUG() level otherwise RDEBUG becomes pointless */ RDEBUG("%s (%s)", instruction->name ? instruction->name : "", fr_int2str(mod_rcode_table, *presult, "<invalid>")); switch (*presult) { case RLM_MODULE_YIELD: if (stack_depth < stack->depth) return UNLANG_ACTION_PUSHED_CHILD; rad_assert(stack_depth == stack->depth); return UNLANG_ACTION_YIELD; default: return UNLANG_ACTION_CALCULATE_RESULT; } }
/* * *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[1024]; #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 && 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); return FALSE; } #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: RDEBUG4(">>> NOT IMPLEMENTED %d", token); result = FALSE; break; } *presult = result; return TRUE; }