Merge tag 'release_3_0_12' into branch moonshot-fr-3.0.12-upgrade.
[freeradius.git] / src / modules / rlm_ruby / rlm_ruby.c
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$
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$")
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         { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED, struct rlm_ruby_t, filename), NULL },
92         { "module", FR_CONF_OFFSET(PW_TYPE_STRING, struct rlm_ruby_t, module_name), "Radiusd" },
93         CONF_PARSER_TERMINATOR
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 vp_print 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 vp_prints, 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                         vp_prints_value(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("radiusd");
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 = {
460         .magic          = RLM_MODULE_INIT,
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 = {
468                 [MOD_AUTHENTICATE]      = mod_authenticate,
469                 [MOD_AUTHORIZE]         = mod_authorize,
470                 [MOD_PREACCT]           = mod_preacct,
471                 [MOD_ACCOUNTING]        = mod_accounting,
472                 [MOD_SESSION]           = mod_checksimul,
473                 [MOD_PRE_PROXY]         = mod_pre_proxy,
474                 [MOD_POST_PROXY]        = mod_post_proxy,
475                 [MOD_POST_AUTH]         = mod_post_auth,
476 #ifdef WITH_COA
477                 [MOD_RECV_COA]          = mod_recv_coa,
478                 [MOD_SEND_COA]          = mod_send_coa
479 #endif
480         },
481 };