25RCSID(
"$Id: 9545797fed89ff723f40c42ca6fc197322c61c73 $")
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,
" ");
175 mrb =
inst->mrb = mrb_open();
177 ERROR(
"mruby initialization failed");
182 DEBUG(
"Creating module %s",
inst->module_name);
183 inst->mruby_module = mrb_define_module(mrb,
inst->module_name);
184 if (!
inst->mruby_module) {
185 ERROR(
"Creating module %s failed",
inst->module_name);
190 mrb_define_class_method(mrb,
inst->mruby_module,
"log",
mruby_log, MRB_ARGS_REQ(2));
192#define A(x) mrb_define_const(mrb, inst->mruby_module, #x, mrb_fixnum_value(x));
218 inst->mrubyconf_hash = mrb_hash_new(mrb);
225 DEBUG(
"Loading file %s...",
inst->filename);
226 f = fopen(
inst->filename,
"r");
228 ERROR(
"Opening file failed");
232 status = mrb_load_file(mrb, f);
234 if (mrb_undef_p(status)) {
235 ERROR(
"Parsing file failed");
246 pair_name = talloc_asprintf(func,
"func_%s_%s", func->
name1, func->
name2);
249 if (cp)
goto found_func;
251 pair_name = talloc_asprintf(func,
"func_%s", func->
name1);
258 if (mrb_nil_p(func_sym)) {
265 }
else if (func->
name2) {
268 if (mrb_nil_p(func_sym)) {
276 if (mrb_nil_p(func_sym)) {
285 status = mrb_funcall(mrb, mrb_obj_value(
inst->mruby_module),
"instantiate", 0);
286 if (mrb_undef_p(status)) {
287 ERROR(
"Running instantiate failed");
299 res = mrb_ary_new(mrb);
301 mrb_value tmp, key, val, to_cast;
303 tmp = mrb_ary_new_capa(mrb, 2);
304 key = mrb_str_new(mrb,
vp->
da->name, strlen(
vp->
da->name));
311 switch (
vp->vp_type) {
314 to_cast = mrb_str_new(mrb,
vp->vp_ptr,
vp->vp_length);
319 to_cast = mrb_nil_value();
329 to_cast = mrb_str_new(mrb,
in, len);
335 switch (
vp->vp_type) {
350 val =
vp->vp_bool ? mrb_obj_value(mrb->true_class) : mrb_obj_value(mrb->false_class);
364 val = mrb_convert_type(mrb, to_cast, MRB_TT_FIXNUM,
"Fixnum",
"to_int");
369 val = mrb_convert_type(mrb, to_cast, MRB_TT_FLOAT,
"Float",
"to_f");
377 mrb_ary_push(mrb, tmp, key);
378 mrb_ary_push(mrb, tmp, val);
379 mrb_ary_push(mrb, res, tmp);
394 for (i = 0; i < RARRAY_LEN(
value); i++) {
395 mrb_value tuple = mrb_ary_entry(
value, i);
397 char const *ckey, *cval;
403 if (mrb_type(tuple) != MRB_TT_ARRAY) {
404 REDEBUG(
"add_vp_tuple, %s: non-array passed at index %i", function_name, i);
408 if (RARRAY_LEN(tuple) != 2 && RARRAY_LEN(tuple) != 3) {
409 REDEBUG(
"add_vp_tuple, %s: array with incorrect length passed at index "
410 "%i, expected 2 or 3, got %"PRId64, function_name, i, RARRAY_LEN(tuple));
414 key = mrb_ary_entry(tuple, 0);
415 val = mrb_ary_entry(tuple, -1);
416 if (mrb_type(key) != MRB_TT_STRING) {
417 REDEBUG(
"add_vp_tuple, %s: tuple element %i must have a string as first element", function_name, i);
421 ckey = mrb_str_to_cstr(mrb, key);
422 cval = mrb_str_to_cstr(mrb, mrb_obj_as_string(mrb, val));
423 if (ckey == NULL || cval == NULL) {
424 REDEBUG(
"%s: string conv failed", function_name);
429 if (RARRAY_LEN(tuple) == 3) {
430 if (mrb_type(mrb_ary_entry(tuple, 1)) != MRB_TT_STRING) {
431 REDEBUG(
"Invalid type for operator, expected string, falling back to =");
433 char const *cop = mrb_str_to_cstr(mrb, mrb_ary_entry(tuple, 1));
435 REDEBUG(
"Invalid operator: %s, falling back to =", cop);
445 .dict_def = request->proto_dict,
449 ERROR(
"Failed to find attribute %s", ckey);
454 ERROR(
"Attribute name %s refers to outer request but not in a tunnel, skipping...", ckey);
463 REDEBUG(
"%s: %s = %s failed", function_name, ckey, cval);
465 DEBUG(
"%s: %s = %s OK", function_name, ckey, cval);
478 memset(&res, 0,
sizeof(res));
482 mrb_iv_set(mrb, mruby_request, mrb_intern_cstr(mrb, list_name), res);
491 mrb_state *mrb =
inst->mrb;
492 mrb_value mruby_request, mruby_result;
494 mruby_request = mrb_obj_new(mrb,
inst->mruby_request, 0, NULL);
495 mrb_iv_set(mrb, mruby_request, mrb_intern_cstr(mrb,
"@frconfig"),
inst->mrubyconf_hash);
496 mruby_set_vps(request, mrb, mruby_request,
"@request", &request->request_pairs);
497 mruby_set_vps(request, mrb, mruby_request,
"@reply", &request->reply_pairs);
498 mruby_set_vps(request, mrb, mruby_request,
"@control", &request->control_pairs);
499 mruby_set_vps(request, mrb, mruby_request,
"@session_state", &request->session_state_pairs);
503 mruby_result = mrb_funcall(mrb, mrb_obj_value(
inst->mruby_module), func->
func->
function_name, 1, mruby_request);
515 switch (mrb_type(mruby_result)) {
522 if (RARRAY_LEN(mruby_result) != 3) {
523 ERROR(
"Expected array to have exactly three values, got %" PRId64
" instead", RARRAY_LEN(mruby_result));
528 if (mrb_type(mrb_ary_entry(mruby_result, 0)) != MRB_TT_FIXNUM) {
529 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))));
534 if (mrb_type(mrb_ary_entry(mruby_result, 1)) != MRB_TT_ARRAY) {
535 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))));
537 }
else if (mrb_type(mrb_ary_entry(mruby_result, 2)) != MRB_TT_ARRAY) {
538 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))));
548 ERROR(
"Expected return to be a Fixnum or an Array, got %s instead", RSTRING_PTR(mrb_obj_as_string(mrb, mruby_result)));
561 mrb_close(
inst->mrb);
575 for (i = 0; i < talloc_array_length(
name); i++) {
577 if (!strchr(
"abcdefghijklmnopqrstuvwxyz1234567890", *p)) *p =
'_';
590 if (!
inst->funcs_init) {
592 inst->funcs_init =
true;
601 .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.
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
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_log_err(_cf, _fmt,...)
#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[]
void * fr_rb_iter_init_inorder(fr_rb_iter_inorder_t *iter, fr_rb_tree_t *tree)
Initialise an in-order iterator.
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.
void * fr_rb_iter_next_inorder(fr_rb_iter_inorder_t *iter)
Return the next node.
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
Iterator structure for in-order traversal of an rbtree.
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.
static int talloc_const_free(void const *ptr)
Free const'd memory.
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