25RCSID(
"$Id: 655b5ce8590b4197f0573f18cc91aa6978faa8d7 $")
27#include <freeradius-devel/server/base.h>
28#include <freeradius-devel/server/module_rlm.h>
29#include <freeradius-devel/server/pairmove.h>
30#include <freeradius-devel/util/debug.h>
83 ret = strcmp(a->
name1, b->name1);
84 if (ret != 0)
return CMP(ret, 0);
85 if (!a->
name2 && !b->name2)
return 0;
86 if (!a->
name2 || !b->name2)
return a->
name2 ? 1 : -1;
87 ret = strcmp(a->
name2, b->name2);
96 mrb_get_args(mrb,
"iz", &level, &
msg);
99 return mrb_nil_value();
104 int indent_section = (lvl + 1) * 4;
105 int indent_item = (lvl + 2) * 4;
116 mrb_value sub_hash, mrubyKey;
120 mrubyKey = mrb_str_new_cstr(mrb, key);
122 if (!mrb_nil_p(mrb_hash_get(mrb,
hash, mrubyKey))) {
123 WARN(
"rlm_mruby: Ignoring duplicate config section '%s'", key);
127 sub_hash = mrb_hash_new(mrb);
128 mrb_hash_set(mrb,
hash, mrubyKey, sub_hash);
135 mrb_value mrubyKey, mrubyValue;
137 if (!key || !
value)
continue;
139 mrubyKey = mrb_str_new_cstr(mrb, key);
140 mrubyValue = mrb_str_new_cstr(mrb,
value);
142 if (!mrb_nil_p(mrb_hash_get(mrb,
hash, mrubyKey))) {
143 WARN(
"rlm_mruby: Ignoring duplicate config item '%s'", key);
147 mrb_hash_set(mrb,
hash, mrubyKey, mrubyValue);
149 DEBUG(
"%*s%s = %s", indent_item,
" ", key,
value);
153 DEBUG(
"%*s}", indent_section,
" ");
170 mrb =
inst->mrb = mrb_open();
172 ERROR(
"mruby initialization failed");
177 DEBUG(
"Creating module %s",
inst->module_name);
178 inst->mruby_module = mrb_define_module(mrb,
inst->module_name);
179 if (!
inst->mruby_module) {
180 ERROR(
"Creating module %s failed",
inst->module_name);
185 mrb_define_class_method(mrb,
inst->mruby_module,
"log",
mruby_log, MRB_ARGS_REQ(2));
187#define A(x) mrb_define_const(mrb, inst->mruby_module, #x, mrb_fixnum_value(x));
213 inst->mrubyconf_hash = mrb_hash_new(mrb);
220 DEBUG(
"Loading file %s...",
inst->filename);
221 f = fopen(
inst->filename,
"r");
223 ERROR(
"Opening file failed");
227 status = mrb_load_file(mrb, f);
229 if (mrb_undef_p(status)) {
230 ERROR(
"Parsing file failed");
234 status = mrb_funcall(mrb, mrb_obj_value(
inst->mruby_module),
"instantiate", 0);
235 if (mrb_undef_p(status)) {
236 ERROR(
"Running instantiate failed");
248 res = mrb_ary_new(mrb);
250 mrb_value tmp, key, val, to_cast;
252 tmp = mrb_ary_new_capa(mrb, 2);
253 key = mrb_str_new(mrb,
vp->
da->name, strlen(
vp->
da->name));
260 switch (
vp->vp_type) {
263 to_cast = mrb_str_new(mrb,
vp->vp_ptr,
vp->vp_length);
268 to_cast = mrb_nil_value();
278 to_cast = mrb_str_new(mrb,
in, len);
284 switch (
vp->vp_type) {
299 val =
vp->vp_bool ? mrb_obj_value(mrb->true_class) : mrb_obj_value(mrb->false_class);
313 val = mrb_convert_type(mrb, to_cast, MRB_TT_FIXNUM,
"Fixnum",
"to_int");
318 val = mrb_convert_type(mrb, to_cast, MRB_TT_FLOAT,
"Float",
"to_f");
326 mrb_ary_push(mrb, tmp, key);
327 mrb_ary_push(mrb, tmp, val);
328 mrb_ary_push(mrb, res, tmp);
343 for (i = 0; i < RARRAY_LEN(
value); i++) {
344 mrb_value tuple = mrb_ary_entry(
value, i);
346 char const *ckey, *cval;
352 if (mrb_type(tuple) != MRB_TT_ARRAY) {
353 REDEBUG(
"add_vp_tuple, %s: non-array passed at index %i", function_name, i);
357 if (RARRAY_LEN(tuple) != 2 && RARRAY_LEN(tuple) != 3) {
358 REDEBUG(
"add_vp_tuple, %s: array with incorrect length passed at index "
359 "%i, expected 2 or 3, got %"PRId64, function_name, i, RARRAY_LEN(tuple));
363 key = mrb_ary_entry(tuple, 0);
364 val = mrb_ary_entry(tuple, -1);
365 if (mrb_type(key) != MRB_TT_STRING) {
366 REDEBUG(
"add_vp_tuple, %s: tuple element %i must have a string as first element", function_name, i);
370 ckey = mrb_str_to_cstr(mrb, key);
371 cval = mrb_str_to_cstr(mrb, mrb_obj_as_string(mrb, val));
372 if (ckey == NULL || cval == NULL) {
373 REDEBUG(
"%s: string conv failed", function_name);
378 if (RARRAY_LEN(tuple) == 3) {
379 if (mrb_type(mrb_ary_entry(tuple, 1)) != MRB_TT_STRING) {
380 REDEBUG(
"Invalid type for operator, expected string, falling back to =");
382 char const *cop = mrb_str_to_cstr(mrb, mrb_ary_entry(tuple, 1));
384 REDEBUG(
"Invalid operator: %s, falling back to =", cop);
394 .dict_def = request->proto_dict,
398 ERROR(
"Failed to find attribute %s", ckey);
403 ERROR(
"Attribute name %s refers to outer request but not in a tunnel, skipping...", ckey);
412 REDEBUG(
"%s: %s = %s failed", function_name, ckey, cval);
414 DEBUG(
"%s: %s = %s OK", function_name, ckey, cval);
427 memset(&res, 0,
sizeof(res));
431 mrb_iv_set(mrb, mruby_request, mrb_intern_cstr(mrb, list_name), res);
440 mrb_state *mrb =
inst->mrb;
441 mrb_value mruby_request, mruby_result;
443 mruby_request = mrb_obj_new(mrb,
inst->mruby_request, 0, NULL);
444 mrb_iv_set(mrb, mruby_request, mrb_intern_cstr(mrb,
"@frconfig"),
inst->mrubyconf_hash);
445 mruby_set_vps(request, mrb, mruby_request,
"@request", &request->request_pairs);
446 mruby_set_vps(request, mrb, mruby_request,
"@reply", &request->reply_pairs);
447 mruby_set_vps(request, mrb, mruby_request,
"@control", &request->control_pairs);
448 mruby_set_vps(request, mrb, mruby_request,
"@session_state", &request->session_state_pairs);
452 mruby_result = mrb_funcall(mrb, mrb_obj_value(
inst->mruby_module), func->
func->
function_name, 1, mruby_request);
464 switch (mrb_type(mruby_result)) {
471 if (RARRAY_LEN(mruby_result) != 3) {
472 ERROR(
"Expected array to have exactly three values, got %" PRId64
" instead", RARRAY_LEN(mruby_result));
477 if (mrb_type(mrb_ary_entry(mruby_result, 0)) != MRB_TT_FIXNUM) {
478 ERROR(
"Expected first array element to be a Fixnum, got %s instead", RSTRING_PTR(mrb_obj_as_string(mrb, mrb_ary_entry(mruby_result, 0))));
483 if (mrb_type(mrb_ary_entry(mruby_result, 1)) != MRB_TT_ARRAY) {
484 ERROR(
"Expected second array element to be an Array, got %s instead", RSTRING_PTR(mrb_obj_as_string(mrb, mrb_ary_entry(mruby_result, 1))));
486 }
else if (mrb_type(mrb_ary_entry(mruby_result, 2)) != MRB_TT_ARRAY) {
487 ERROR(
"Expected third array element to be an Array, got %s instead", RSTRING_PTR(mrb_obj_as_string(mrb, mrb_ary_entry(mruby_result, 2))));
497 ERROR(
"Expected return to be a Fixnum or an Array, got %s instead", RSTRING_PTR(mrb_obj_as_string(mrb, mruby_result)));
510 mrb_close(
inst->mrb);
524 for (i = 0; i < talloc_array_length(
name); i++) {
526 if (!strchr(
"abcdefghijklmnopqrstuvwxyz1234567890", *p)) *p =
'_';
539 if (!
inst->funcs_init) {
541 inst->funcs_init =
true;
550 .offset = rule->pair.offset,
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
#define DIAG_UNKNOWN_PRAGMAS
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
call_env_parsed_t * call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule)
Allocate a new call_env_parsed_t structure and add it to the list of parsed call envs.
void call_env_parsed_set_data(call_env_parsed_t *parsed, void const *data)
Assign data to a call_env_parsed_t.
#define CALL_ENV_TERMINATOR
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
call_env_parser_t const * env
Parsing rules for call method env.
section_name_t const * asked
The actual name1/name2 that resolved to a module_method_binding_t.
@ CALL_ENV_FLAG_PARSE_ONLY
The result of parsing will not be evaluated at runtime.
@ CALL_ENV_FLAG_PARSE_MISSING
If this subsection is missing, still parse it.
@ CALL_ENV_PARSE_TYPE_VOID
Output of the parsing phase is undefined (a custom structure).
module_instance_t const * mi
Module instance that the callenv is registered to.
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _name2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
#define CONF_PARSER_TERMINATOR
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
@ CONF_FLAG_FILE_INPUT
File matching value must exist, and must be readable.
Defines a CONF_PAIR to C data type mapping.
Common header for all CONF_* types.
Configuration AVP similar to a fr_pair_t.
A section grouping multiple CONF_PAIR.
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
#define cf_item_next(_parent, _curr)
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
void fr_log(fr_log_t const *log, fr_log_type_t type, char const *file, int line, char const *fmt,...)
Send a server log message to its destination.
@ L_DBG_WARN_REQ
Less severe warning only displayed when debugging is enabled.
@ L_DBG_ERR
Error only displayed when debugging is enabled.
@ L_DBG_ERR_REQ
Less severe error only displayed when debugging is enabled.
@ L_DBG_WARN
Warning only displayed when debugging is enabled.
@ L_INFO
Informational message.
@ L_DBG
Only displayed when debugging is enabled.
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_FLOAT32
Single precision floating point.
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_INT8
8 Bit signed integer.
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
@ FR_TYPE_IPV6_PREFIX
IPv6 Prefix.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_INT64
64 Bit signed integer.
@ FR_TYPE_INT16
16 Bit signed integer.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_COMBO_IP_PREFIX
IPv4 or IPv6 address prefix depending on length.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
@ FR_TYPE_COMBO_IP_ADDR
IPv4 or IPv6 address depending on length.
@ FR_TYPE_IFID
Interface ID.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_FLOAT64
Double precision floating point.
void * env_data
Per call environment data.
module_instance_t const * mi
Instance of the module being instantiated.
module_instance_t * mi
Module instance to detach.
module_instance_t * mi
Instance of the module being instantiated.
Temporary structure to hold arguments for module calls.
Temporary structure to hold arguments for detach calls.
Temporary structure to hold arguments for instantiation calls.
module_t common
Common fields presented by all modules.
struct RClass * mruby_request_class(mrb_state *mrb, struct RClass *parent)
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, UNUSED bool tainted)
Convert string value to native attribute value.
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
void radius_pairmove(request_t *request, fr_pair_list_t *to, fr_pair_list_t *from)
static const conf_parser_t config[]
int fr_rb_find_or_insert(void **found, fr_rb_tree_t *tree, void const *data)
Attempt to find current data in the tree, if it does not exist insert it.
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
The main red black tree structure.
#define RETURN_MODULE_RCODE(_rcode)
#define RETURN_MODULE_FAIL
rlm_rcode_t
Return codes indicating the result of the module call.
@ RLM_MODULE_INVALID
The module considers the request invalid.
@ RLM_MODULE_OK
The module is OK, continue.
@ RLM_MODULE_FAIL
Module failed, don't reply.
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
@ RLM_MODULE_REJECT
Immediately reject the request.
@ RLM_MODULE_NOTFOUND
User not found.
@ RLM_MODULE_UPDATED
OK (pairs modified).
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
@ RLM_MODULE_NUMCODES
How many valid return codes there are.
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
fr_dict_attr_t const * request_attr_reply
static int mod_detach(module_detach_ctx_t const *mctx)
char * name1
Section name1 where this is called.
static int mruby_vps_to_array(request_t *request, mrb_value *out, mrb_state *mrb, fr_pair_list_t *vps)
static unlang_action_t mod_mruby(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
struct RClass * mruby_request
static void mruby_func_name_safe(char *name)
static int8_t mruby_func_def_cmp(void const *one, void const *two)
How to compare two Ruby function calls.
struct RClass * mruby_module
fr_rb_tree_t funcs
Tree of function calls found by call_env parser.
char const * function_name
Name of the function being called.
bool funcs_init
Has the tree been initialised.
char * name2
Section name2 where this is called.
static const call_env_method_t mruby_method_env
static mrb_value mruby_log(mrb_state *mrb, UNUSED mrb_value self)
fr_rb_node_t node
Node in tree of function calls.
static void add_vp_tuple(TALLOC_CTX *ctx, request_t *request, fr_pair_list_t *vps, mrb_state *mrb, mrb_value value, char const *function_name)
static int mruby_func_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, UNUSED tmpl_rules_t const *t_rules, UNUSED CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
static void mruby_parse_config(mrb_state *mrb, CONF_SECTION *cs, int lvl, mrb_value hash)
static const conf_parser_t module_config[]
static int mod_instantiate(module_inst_ctx_t const *mctx)
static int mruby_set_vps(request_t *request, mrb_state *mrb, mrb_value mruby_request, char const *list_name, fr_pair_list_t *vps)
Translates requests between the server an an mruby interpreter.
static unsigned int hash(char const *username, unsigned int tablesize)
static int instantiate(module_inst_ctx_t const *mctx)
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
char const * name2
Second section name. Usually a packet type like 'access-request', 'access-accept',...
char const * name1
First section name. Usually a verb like 'recv', 'send', etc...
@ MODULE_TYPE_THREAD_UNSAFE
Module is not threadsafe.
module_flags_t flags
Flags that control how a module starts up and how a module is called.
CONF_SECTION * conf
Module's instance configuration.
void * data
Module's instance data.
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Named methods exported by a module.
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, char const *name, tmpl_rules_t const *rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
int tmpl_request_ptr(request_t **request, FR_DLIST_HEAD(tmpl_request_list) const *rql)
Resolve a tmpl_request_ref_t to a request_t.
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Optional arguments passed to vp_tmpl functions.
eap_aka_sim_process_conf_t * inst
Stores an attribute, a value and various bits of other data.
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
fr_table_num_ordered_t const fr_tokens_table[]
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
static fr_slen_t fr_value_box_aprint(TALLOC_CTX *ctx, char **out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules) 1(fr_value_box_print
static size_t char ** out