All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_ruby.c
Go to the documentation of this file.
1 /*
2  * This program is is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: 43a8c1530a9eacca900f9e579e742f676dc55efc $
19  * @file rlm_ruby.c
20  * @brief Translates requests between the server an a ruby interpreter.
21  *
22  * @note Maintainers note
23  * @note Please don't use this module, Matz ruby was never designed for embedding.
24  * @note This module leaks memory, and the ruby code installs signal handlers
25  * @note which interfere with normal operation of the server. It's all bad...
26  * @note mruby shows some promise, feel free to rewrite the module to use that.
27  * @note https://github.com/mruby/mruby
28  *
29  * @copyright 2008 Andriy Dmytrenko aka Antti, BuzhNET
30  */
31 
32 
33 RCSID("$Id: 43a8c1530a9eacca900f9e579e742f676dc55efc $")
34 
35 #include <freeradius-devel/radiusd.h>
36 #include <freeradius-devel/modules.h>
37 
38 /*
39  * Undefine any HAVE_* flags which may conflict
40  * ruby.h *REALLY* shouldn't #include its config.h file,
41  * but it does *sigh*.
42  */
43 #undef HAVE_CRYPT
44 
45 #ifdef __clang__
46 DIAG_OFF(disabled-macro-expansion)
47 #endif
48 #include <ruby.h>
49 
50 /*
51  * Define a structure for our module configuration.
52  *
53  * These variables do not need to be in a structure, but it's
54  * a lot cleaner to do so, and a pointer to the structure can
55  * be used as the instance handle.
56  */
57 typedef struct rlm_ruby_t {
58 #define RLM_RUBY_STRUCT(foo) unsigned long func_##foo
59 
60  RLM_RUBY_STRUCT(instantiate);
61  RLM_RUBY_STRUCT(authorize);
62  RLM_RUBY_STRUCT(authenticate);
63  RLM_RUBY_STRUCT(preacct);
64  RLM_RUBY_STRUCT(accounting);
65  RLM_RUBY_STRUCT(checksimul);
66  RLM_RUBY_STRUCT(pre_proxy);
67  RLM_RUBY_STRUCT(post_proxy);
68  RLM_RUBY_STRUCT(post_auth);
69 #ifdef WITH_COA
70  RLM_RUBY_STRUCT(recv_coa);
71  RLM_RUBY_STRUCT(send_coa);
72 #endif
73  RLM_RUBY_STRUCT(detach);
74 
75  char const *filename;
76  char const *module_name;
77  VALUE module;
78 
79 } rlm_ruby_t;
80 
81 /*
82  * A mapping of configuration file names to internal variables.
83  *
84  * Note that the string is dynamically allocated, so it MUST
85  * be freed. When the configuration file parse re-reads the string,
86  * it free's the old one, and strdup's the new one, placing the pointer
87  * to the strdup'd string into 'config.string'. This gets around
88  * buffer over-flows.
89  */
90 static const CONF_PARSER module_config[] = {
91  { FR_CONF_OFFSET("filename", PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED, struct rlm_ruby_t, filename) },
92  { FR_CONF_OFFSET("module", PW_TYPE_STRING, struct rlm_ruby_t, module_name), .dflt = "Radiusd" },
94 };
95 
96 
97 /*
98  * radiusd Ruby functions
99  */
100 
101 /* radlog wrapper */
102 
103 static VALUE radlog_rb(UNUSED VALUE self, VALUE msg_type, VALUE rb_msg) {
104  int status;
105  char *msg;
106  status = FIX2INT(msg_type);
107  msg = StringValuePtr(rb_msg);
108  radlog(status, "%s", msg);
109  return Qnil;
110 }
111 
112 /* Tuple to value pair conversion */
113 
114 static void add_vp_tuple(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vpp, VALUE rb_value,
115  char const *function_name) {
116  int i;
117  long outertuplesize;
118  VALUE_PAIR *vp;
119 
120  /* If the Ruby function gave us nil for the tuple, then just return. */
121  if (NIL_P(rb_value)) {
122  return;
123  }
124 
125  if (TYPE(rb_value) != T_ARRAY) {
126  REDEBUG("add_vp_tuple, %s: non-array passed", function_name);
127  return;
128  }
129 
130  /* Get the array size. */
131  outertuplesize = RARRAY_LEN(rb_value);
132 
133  for (i = 0; i < outertuplesize; i++) {
134  VALUE pTupleElement = rb_ary_entry(rb_value, i);
135 
136  if ((pTupleElement != 0) &&
137  (TYPE(pTupleElement) == T_ARRAY)) {
138 
139  /* Check if it's a pair */
140  long tuplesize;
141 
142  if ((tuplesize = RARRAY_LEN(pTupleElement)) != 2) {
143  REDEBUG("%s: tuple element %i is a tuple "
144  " of size %li. must be 2\n", function_name,
145  i, tuplesize);
146  } else {
147  VALUE pString1, pString2;
148 
149  pString1 = rb_ary_entry(pTupleElement, 0);
150  pString2 = rb_ary_entry(pTupleElement, 1);
151 
152  if ((TYPE(pString1) == T_STRING) &&
153  (TYPE(pString2) == T_STRING)) {
154 
155 
156  char const *s1, *s2;
157 
158  /* fr_pair_make() will convert and find any
159  * errors in the pair.
160  */
161 
162  s1 = StringValuePtr(pString1);
163  s2 = StringValuePtr(pString2);
164 
165  if ((s1 != NULL) && (s2 != NULL)) {
166  DEBUG("%s: %s = %s ",
167  function_name, s1, s2);
168 
169  /* xxx Might need to support other T_OP */
170  vp = fr_pair_make(ctx, vpp, s1, s2, T_OP_EQ);
171  if (vp != NULL) {
172  DEBUG("%s: s1, s2 OK", function_name);
173  } else {
174  DEBUG("%s: s1, s2 FAILED", function_name);
175  }
176  } else {
177  REDEBUG("%s: string conv failed", function_name);
178  }
179 
180  } else {
181  REDEBUG("%s: tuple element %d must be "
182  "(string, string)", function_name, i);
183  }
184  }
185  } else {
186  REDEBUG("%s: tuple element %d is not a tuple\n",
187  function_name, i);
188  }
189  }
190 
191 }
192 
193 /* This is the core Ruby function that the others wrap around.
194  * Pass the value-pair print strings in a tuple.
195  * xxx We're not checking the errors. If we have errors, what do we do?
196  */
197 
198 #define BUF_SIZE 1024
199 static rlm_rcode_t CC_HINT(nonnull (4)) do_ruby(REQUEST *request, unsigned long func,
200  VALUE module, char const *function_name)
201 {
202  rlm_rcode_t rcode = RLM_MODULE_OK;
203  vp_cursor_t cursor;
204 
205  char buf[BUF_SIZE]; /* same size as fr_pair_fprint buffer */
206 
207  VALUE_PAIR *vp;
208  VALUE rb_request, rb_result, rb_reply_items, rb_config, rbString1, rbString2;
209 
210  int n_tuple;
211  DEBUG("Calling ruby function %s which has id: %lu\n", function_name, func);
212 
213  /* Return with "OK, continue" if the function is not defined.
214  * TODO: Should check with rb_respond_to each time, just because ruby can define function dynamicly?
215  */
216  if (func == 0) {
217  return rcode;
218  }
219 
220  n_tuple = 0;
221  if (request) {
222  for (vp = fr_cursor_init(&cursor, &request->packet->vps);
223  vp;
224  vp = fr_cursor_next(&cursor)) {
225  n_tuple++;
226  }
227  }
228 
229  /*
230  Creating ruby array, that contains arrays of [name,value]
231  Maybe we should use hash instead? Can this names repeat?
232  */
233  rb_request = rb_ary_new2(n_tuple);
234 
235  if (request) {
236  for (vp = fr_cursor_init(&cursor, &request->packet->vps);
237  vp;
238  vp = fr_cursor_next(&cursor)) {
239  VALUE tmp = rb_ary_new2(2);
240 
241  /* The name. logic from fr_pair_snprint, lib/print.c */
242  if (vp->da->flags.has_tag) {
243  snprintf(buf, BUF_SIZE, "%s:%d", vp->da->name, vp->tag);
244  } else {
245  strlcpy(buf, vp->da->name, sizeof(buf));
246  }
247  rbString1 = rb_str_new2(buf);
248  fr_pair_value_snprint(buf, sizeof (buf), vp, '"');
249  rbString2 = rb_str_new2(buf);
250 
251  rb_ary_push(tmp, rbString1);
252  rb_ary_push(tmp, rbString2);
253  rb_ary_push(rb_request, tmp);
254  }
255  }
256 
257  /* Calling corresponding ruby function, passing request and catching result */
258  rb_result = rb_funcall(module, func, 1, rb_request);
259 
260  /*
261  * Checking result, it can be array of type [result,
262  * [array of reply pairs],[array of config pairs]],
263  * It can also be just a fixnum, which is a result itself.
264  */
265  if (TYPE(rb_result) == T_ARRAY) {
266  if (!FIXNUM_P(rb_ary_entry(rb_result, 0))) {
267  ERROR("First element of an array was not a FIXNUM (Which has to be a return_value)");
268 
269  rcode = RLM_MODULE_FAIL;
270  goto finish;
271  }
272 
273  rcode = FIX2INT(rb_ary_entry(rb_result, 0));
274 
275  /*
276  * Only process the results if we were passed a request.
277  */
278  if (request) {
279  rb_reply_items = rb_ary_entry(rb_result, 1);
280  rb_config = rb_ary_entry(rb_result, 2);
281 
282  add_vp_tuple(request->reply, request, &request->reply->vps,
283  rb_reply_items, function_name);
284  add_vp_tuple(request, request, &request->config,
285  rb_config, function_name);
286  }
287  } else if (FIXNUM_P(rb_result)) {
288  rcode = FIX2INT(rb_result);
289  }
290 
291 finish:
292  return rcode;
293 }
294 
295 static struct varlookup {
296  char const* name;
297  int value;
298 } constants[] = {
299  { "L_DBG", L_DBG},
300  { "L_AUTH", L_AUTH},
301  { "L_INFO", L_INFO},
302  { "L_ERR", L_ERR},
303  { "L_PROXY", L_PROXY},
304  { "RLM_MODULE_REJECT", RLM_MODULE_REJECT},
305  { "RLM_MODULE_FAIL", RLM_MODULE_FAIL},
306  { "RLM_MODULE_OK", RLM_MODULE_OK},
307  { "RLM_MODULE_HANDLED", RLM_MODULE_HANDLED},
308  { "RLM_MODULE_INVALID", RLM_MODULE_INVALID},
309  { "RLM_MODULE_USERLOCK", RLM_MODULE_USERLOCK},
310  { "RLM_MODULE_NOTFOUND", RLM_MODULE_NOTFOUND},
311  { "RLM_MODULE_NOOP", RLM_MODULE_NOOP},
312  { "RLM_MODULE_UPDATED", RLM_MODULE_UPDATED},
313  { "RLM_MODULE_NUMCODES", RLM_MODULE_NUMCODES},
314  { NULL, 0},
315 };
316 
317 /*
318  * Import a user module and load a function from it
319  */
320 static int load_function(char const *f_name, unsigned long *func, VALUE module) {
321  if (!f_name) {
322  *func = 0;
323  } else {
324  *func = rb_intern(f_name);
325  /* rb_intern returns a symbol of a function, not a function itself
326  it can be aplied to any recipient,
327  so we should check it for our module recipient
328  */
329  if (!rb_respond_to(module, *func))
330  *func = 0;
331  }
332  DEBUG("load_function %s, result: %lu", f_name, *func);
333  return 0;
334 }
335 
336 /*
337  * Do any per-module initialization that is separate to each
338  * configured instance of the module. e.g. set up connections
339  * to external databases, read configuration files, set up
340  * dictionary entries, etc.
341  *
342  * If configuration information is given in the config section
343  * that must be referenced in later calls, store a handle to it
344  * in *instance otherwise put a null pointer there.
345  */
346 static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
347 {
348  rlm_ruby_t *inst = instance;
349  VALUE module;
350 
351  int idx;
352  int status;
353 
354  /*
355  * Initialize Ruby interpreter. Fatal error if this fails.
356  */
357  ruby_init();
358  ruby_init_loadpath();
359  ruby_script(main_config.name);
360 
361  /* disabling GC, it will eat your memory, but at least it will be stable. */
362  rb_gc_disable();
363 
364  /*
365  * Setup our 'radiusd' module.
366  */
367  module = inst->module = rb_define_module(inst->module_name);
368  if (!module) {
369  ERROR("Ruby rb_define_module failed");
370 
371  return -1;
372  }
373 
374  /*
375  * Load constants into module
376  */
377  for (idx = 0; constants[idx].name; idx++) {
378  rb_define_const(module, constants[idx].name, INT2NUM(constants[idx].value));
379  }
380 
381  /*
382  * Expose some FreeRADIUS API functions as ruby functions
383  */
384  rb_define_module_function(module, "radlog", radlog_rb, 2);
385 
386  DEBUG("Loading file %s...", inst->filename);
387  rb_load_protect(rb_str_new2(inst->filename), 0, &status);
388  if (status) {
389  ERROR("Error loading file %s status: %d", inst->filename, status);
390 
391  return -1;
392  }
393  DEBUG("Loaded file %s", inst->filename);
394 
395  /*
396  * Import user modules.
397  */
398 #define RLM_RUBY_LOAD(foo) if (load_function(#foo, &inst->func_##foo, inst->module)==-1) { \
399  return -1; \
400  }
401 
402  RLM_RUBY_LOAD(instantiate);
403  RLM_RUBY_LOAD(authenticate);
404  RLM_RUBY_LOAD(authorize);
405  RLM_RUBY_LOAD(preacct);
406  RLM_RUBY_LOAD(accounting);
407  RLM_RUBY_LOAD(checksimul);
408  RLM_RUBY_LOAD(pre_proxy);
409  RLM_RUBY_LOAD(post_proxy);
410  RLM_RUBY_LOAD(post_auth);
411 #ifdef WITH_COA
412  RLM_RUBY_LOAD(recv_coa);
413  RLM_RUBY_LOAD(send_coa);
414 #endif
415  RLM_RUBY_LOAD(detach);
416 
417  /* Call the instantiate function. No request. Use the return value. */
418  return do_ruby(NULL, inst->func_instantiate, inst->module, "instantiate");
419 }
420 
421 #define RLM_RUBY_FUNC(foo) static rlm_rcode_t CC_HINT(nonnull) mod_##foo(void *instance, REQUEST *request) \
422  { \
423  return do_ruby(request, \
424  ((struct rlm_ruby_t *)instance)->func_##foo,((struct rlm_ruby_t *)instance)->module, \
425  #foo); \
426  }
427 
428 RLM_RUBY_FUNC(authorize)
429 RLM_RUBY_FUNC(authenticate)
430 RLM_RUBY_FUNC(preacct)
431 RLM_RUBY_FUNC(accounting)
432 RLM_RUBY_FUNC(checksimul)
433 RLM_RUBY_FUNC(pre_proxy)
434 RLM_RUBY_FUNC(post_proxy)
435 RLM_RUBY_FUNC(post_auth)
436 #ifdef WITH_COA
437 RLM_RUBY_FUNC(recv_coa)
438 RLM_RUBY_FUNC(send_coa)
439 #endif
440 
441 static int mod_detach(UNUSED void *instance)
442 {
443  ruby_finalize();
444  ruby_cleanup(0);
445 
446  return 0;
447 }
448 
449 /*
450  * The module name should be the only globally exported symbol.
451  * That is, everything else should be 'static'.
452  *
453  * If the module needs to temporarily modify it's instantiation
454  * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
455  * The server will then take care of ensuring that the module
456  * is single-threaded.
457  */
458 extern module_t rlm_ruby;
459 module_t rlm_ruby = {
461  .name = "ruby",
462  .type = RLM_TYPE_THREAD_UNSAFE, /* type, ok, let's be honest, MRI is not yet treadsafe */
463  .inst_size = sizeof(rlm_ruby_t),
464  .config = module_config,
465  .instantiate = mod_instantiate,
466  .detach = mod_detach,
467  .methods = {
470  [MOD_PREACCT] = mod_preacct,
473  [MOD_PRE_PROXY] = mod_pre_proxy,
474  [MOD_POST_PROXY] = mod_post_proxy,
476 #ifdef WITH_COA
477  [MOD_RECV_COA] = mod_recv_coa,
478  [MOD_SEND_COA] = mod_send_coa
479 #endif
480  },
481 };
5 methods index for preproxy section.
Definition: modules.h:46
#define PW_TYPE_FILE_INPUT
File matching value must exist, and must be readable.
Definition: conffile.h:204
Only displayed when debugging is enabled.
Definition: log.h:41
Main server configuration.
Definition: radiusd.h:108
static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
Write accounting data to Couchbase documents.
char const * name
Definition: rlm_ruby.c:296
The module is OK, continue.
Definition: radiusd.h:91
static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) CC_HINT(nonnull)
Metadata exported by the module.
Definition: modules.h:134
int radlog(log_type_t lvl, char const *fmt,...) CC_HINT(format(printf
#define RLM_TYPE_THREAD_UNSAFE
Module is not threadsafe.
Definition: modules.h:76
RLM_RUBY_STRUCT(instantiate)
static VALUE radlog_rb(UNUSED VALUE self, VALUE msg_type, VALUE rb_msg)
Definition: rlm_ruby.c:103
7 methods index for postauth section.
Definition: modules.h:48
#define DIAG_OFF(_x)
Definition: build.h:102
static char const * name
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
Handle authorization requests using Couchbase document data.
#define UNUSED
Definition: libradius.h:134
#define RLM_MODULE_INIT
Definition: modules.h:86
Error message.
Definition: log.h:36
int8_t tag
Tag value used to group valuepairs.
Definition: pair.h:121
void size_t fr_pair_value_snprint(char *out, size_t outlen, VALUE_PAIR const *vp, char quote)
Print the value of an attribute to a string.
Definition: pair.c:2107
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
static int mod_detach(UNUSED void *instance)
Definition: rlm_ruby.c:441
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
#define inst
Definition: token.h:46
int value
Definition: rlm_ruby.c:297
The module considers the request invalid.
Definition: radiusd.h:93
char const * name
Name of the daemon, usually 'radiusd'.
Definition: radiusd.h:109
static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request) CC_HINT(nonnull)
#define RLM_RUBY_LOAD(foo)
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
fr_dict_attr_flags_t flags
Flags.
Definition: dict.h:88
Reject the request (user is locked out).
Definition: radiusd.h:94
module_t rlm_ruby
Definition: rlm_ruby.c:459
#define DEBUG(fmt,...)
Definition: log.h:175
static const CONF_PARSER module_config[]
Definition: rlm_ruby.c:90
4 methods index for checksimul section.
Definition: modules.h:45
Immediately reject the request.
Definition: radiusd.h:89
VALUE module
Definition: rlm_ruby.c:77
3 methods index for accounting section.
Definition: modules.h:44
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request)
Check if a given user is already logged in.
0 methods index for authenticate section.
Definition: modules.h:41
static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
Definition: rlm_ruby.c:346
Authentication message.
Definition: log.h:34
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
static rs_t * conf
Definition: radsniff.c:46
Module succeeded without doing anything.
Definition: radiusd.h:96
char name[1]
Attribute name.
Definition: dict.h:89
uint64_t magic
Used to validate module struct.
Definition: modules.h:135
Module failed, don't reply.
Definition: radiusd.h:90
static int load_function(char const *f_name, unsigned long *func, VALUE module)
Definition: rlm_ruby.c:320
static void add_vp_tuple(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vpp, VALUE rb_value, char const *function_name)
Definition: rlm_ruby.c:114
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
#define BUF_SIZE
Definition: rlm_ruby.c:198
Proxy messages.
Definition: log.h:38
char const * filename
Definition: rlm_ruby.c:75
#define REDEBUG(fmt,...)
Definition: log.h:254
unsigned int has_tag
Tagged attribute.
Definition: dict.h:46
static struct varlookup constants[]
6 methods index for postproxy section.
Definition: modules.h:47
2 methods index for preacct section.
Definition: modules.h:43
#define PW_TYPE_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition: conffile.h:200
8 methods index for recvcoa section.
Definition: modules.h:50
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
char const * module_name
Definition: rlm_ruby.c:76
9 methods index for sendcoa section.
Definition: modules.h:51
How many valid return codes there are.
Definition: radiusd.h:98
String of printable characters.
Definition: radius.h:33
Informational message.
Definition: log.h:35
1 methods index for authorize section.
Definition: modules.h:42
User not found.
Definition: radiusd.h:95
#define RCSID(id)
Definition: build.h:135
OK (pairs modified).
Definition: radiusd.h:97
The module handled the request, so stop.
Definition: radiusd.h:92
VALUE_PAIR * fr_pair_make(TALLOC_CTX *ctx, VALUE_PAIR **vps, char const *attribute, char const *value, FR_TOKEN op)
Create a VALUE_PAIR from ASCII strings.
Definition: pair.c:338
#define ERROR(fmt,...)
Definition: log.h:145
#define RLM_RUBY_FUNC(foo)
Definition: rlm_ruby.c:421
static rlm_rcode_t CC_HINT(nonnull(4))
Definition: rlm_ruby.c:199
struct rlm_ruby_t rlm_ruby_t