Require that the modules call talloc for their instance handle.
[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, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15  
16 /**
17  * $Id$
18  * @file rlm_ruby.c
19  * @brief Translates requests between the server an a ruby interpreter.
20  *
21  * @copyright 2008 Andriy Dmytrenko aka Antti, BuzhNET
22  */
23
24 #include <freeradius-devel/ident.h>
25
26 RCSID("$Id$")
27
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/modules.h>
30
31 /*
32  *      Undefine any HAVE_* flags which may conflict
33  *      ruby.h *REALLY* shouldn't #include its config.h file,
34  *      but it does *sigh*.
35  */
36 #undef HAVE_CRYPT
37
38 #include <ruby.h>
39
40 /*
41  *      Define a structure for our module configuration.
42  *
43  *      These variables do not need to be in a structure, but it's
44  *      a lot cleaner to do so, and a pointer to the structure can
45  *      be used as the instance handle.
46  */
47 typedef struct rlm_ruby_t {
48 #define RLM_RUBY_STRUCT(foo) int func_##foo
49
50     RLM_RUBY_STRUCT(instantiate);
51     RLM_RUBY_STRUCT(authorize);
52     RLM_RUBY_STRUCT(authenticate);
53     RLM_RUBY_STRUCT(preacct);
54     RLM_RUBY_STRUCT(accounting);
55     RLM_RUBY_STRUCT(checksimul);
56     RLM_RUBY_STRUCT(preproxy);
57     RLM_RUBY_STRUCT(postproxy);
58     RLM_RUBY_STRUCT(postauth);
59 #ifdef WITH_COA
60     RLM_RUBY_STRUCT(recvcoa);
61     RLM_RUBY_STRUCT(sendcoa);
62 #endif
63     RLM_RUBY_STRUCT(detach);
64
65     char *scriptFile;
66     char *moduleName;
67     VALUE pModule_builtin;
68
69 } rlm_ruby_t;
70
71 /*
72  *      A mapping of configuration file names to internal variables.
73  *
74  *      Note that the string is dynamically allocated, so it MUST
75  *      be freed.  When the configuration file parse re-reads the string,
76  *      it free's the old one, and strdup's the new one, placing the pointer
77  *      to the strdup'd string into 'config.string'.  This gets around
78  *      buffer over-flows.
79  */
80 static const CONF_PARSER module_config[] = {
81     { "scriptfile", PW_TYPE_FILENAME,
82         offsetof(struct rlm_ruby_t, scriptFile), NULL, NULL},
83     { "modulename", PW_TYPE_STRING_PTR,
84         offsetof(struct rlm_ruby_t, moduleName), NULL, "Radiusd"},
85     { NULL, -1, 0, NULL, NULL} /* end of module_config */
86 };
87
88
89 /*
90  * radiusd Ruby functions
91  */
92
93 /* radlog wrapper */
94
95 static VALUE radlog_rb(UNUSED VALUE self, VALUE msg_type, VALUE rb_msg) {
96     int status;
97     char *msg;
98     status = FIX2INT(msg_type);
99     msg = StringValuePtr(rb_msg);
100     radlog(status, "%s", msg);
101     return Qnil;
102 }
103
104 /* Tuple to value pair conversion */
105
106 static void add_vp_tuple(VALUE_PAIR **vpp, VALUE rb_value,
107         const char *function_name) {
108     int i, outertuplesize;
109     VALUE_PAIR *vp;
110
111     /* If the Ruby function gave us nil for the tuple, then just return. */
112     if (NIL_P(rb_value)) {
113         return;
114     }
115
116     if (TYPE(rb_value) != T_ARRAY) {
117         radlog(L_ERR, "add_vp_tuple, %s: non-array passed", function_name);
118         return;
119     }
120
121     /* Get the array size. */
122     outertuplesize = RARRAY_LEN(rb_value);
123
124     for (i = 0; i < outertuplesize; i++) {
125         VALUE pTupleElement = rb_ary_entry(rb_value, i);
126
127         if ((pTupleElement != 0) &&
128                 (TYPE(pTupleElement) == T_ARRAY)) {
129
130             /* Check if it's a pair */
131             int tuplesize;
132
133             if ((tuplesize = RARRAY_LEN(pTupleElement)) != 2) {
134                 radlog(L_ERR, "%s: tuple element %d is a tuple "
135                         " of size %d. must be 2\n", function_name,
136                         i, tuplesize);
137             } else {
138                 VALUE pString1, pString2;
139
140                 pString1 = rb_ary_entry(pTupleElement, 0);
141                 pString2 = rb_ary_entry(pTupleElement, 1);
142
143                 if ((TYPE(pString1) == T_STRING) &&
144                         (TYPE(pString2) == T_STRING)) {
145
146
147                     const char *s1, *s2;
148
149                     /* pairmake() will convert and find any
150                      * errors in the pair.
151                      */
152
153                     s1 = StringValuePtr(pString1);
154                     s2 = StringValuePtr(pString2);
155
156                     if ((s1 != NULL) && (s2 != NULL)) {
157                         radlog(L_DBG, "%s: %s = %s ",
158                                 function_name, s1, s2);
159
160                         /* xxx Might need to support other T_OP */
161                         vp = pairmake(s1, s2, T_OP_EQ);
162                         if (vp != NULL) {
163                             pairadd(vpp, vp);
164                             radlog(L_DBG, "%s: s1, s2 OK\n",
165                                     function_name);
166                         } else {
167                             radlog(L_DBG, "%s: s1, s2 FAILED\n",
168                                     function_name);
169                         }
170                     } else {
171                         radlog(L_ERR, "%s: string conv failed\n",
172                                 function_name);
173                     }
174
175                 } else {
176                     radlog(L_ERR, "%s: tuple element %d must be "
177                             "(string, string)", function_name, i);
178                 }
179             }
180         } else {
181             radlog(L_ERR, "%s: tuple element %d is not a tuple\n",
182                     function_name, i);
183         }
184     }
185
186 }
187
188 /* This is the core Ruby function that the others wrap around.
189  * Pass the value-pair print strings in a tuple.
190  * xxx We're not checking the errors. If we have errors, what do we do?
191  */
192
193 #define BUF_SIZE 1024    
194 static rlm_rcode_t ruby_function(REQUEST *request, int func,
195                                  VALUE module, const char *function_name) {
196                                  
197     rlm_rcode_t rcode = RLM_MODULE_OK;
198     
199     char buf[BUF_SIZE]; /* same size as vp_print buffer */
200
201     VALUE_PAIR *vp;
202     VALUE rb_request, rb_result, rb_reply_items, rb_config_items;
203
204     int n_tuple;
205     radlog(L_DBG, "Calling ruby function %s which has id: %d\n", function_name, func);
206
207     /* Return with "OK, continue" if the function is not defined. 
208      * TODO: Should check with rb_respond_to each time, just because ruby can define function dynamicly?
209      */
210     if (func == 0) {
211         return rcode;
212     }
213     
214     n_tuple = 0;
215
216     if (request) {
217         for (vp = request->packet->vps; vp; vp = vp->next) {
218             n_tuple++;
219         }
220     }
221     
222     
223     /*
224         Creating ruby array, that contains arrays of [name,value]
225         Maybe we should use hash instead? Can this names repeat?
226      */
227     rb_request = rb_ary_new2(n_tuple);
228     if (request) {
229         for (vp = request->packet->vps; vp; vp = vp->next) {
230             VALUE tmp = rb_ary_new2(2);
231
232             /* The name. logic from vp_prints, lib/print.c */
233             if (vp->da->flags.has_tag) {
234                 snprintf(buf, BUF_SIZE, "%s:%d", vp->da->name, vp->tag);
235             } else {
236                 strlcpy(buf, vp->da->name, sizeof(buf));
237             }
238             VALUE rbString1 = rb_str_new2(buf);
239             /* The value. Use delimiter - don't know what that means */
240             vp_prints_value(buf, sizeof (buf), vp, 1);
241             VALUE rbString2 = rb_str_new2(buf);
242
243             rb_ary_push(tmp, rbString1);
244             rb_ary_push(tmp, rbString2);
245             rb_ary_push(rb_request, tmp);
246         }
247     }
248
249     /* Calling corresponding ruby function, passing request and catching result */
250     rb_result = rb_funcall(module, func, 1, rb_request);
251
252     /* 
253      *  Checking result, it can be array of type [result, 
254      *  [array of reply pairs],[array of config pairs]],
255      *  It can also be just a fixnum, which is a result itself.
256      */
257     if (TYPE(rb_result) == T_ARRAY) {
258         if (!FIXNUM_P(rb_ary_entry(rb_result, 0))) {
259             radlog(L_ERR, "rlm_ruby: First element of an array was not a "
260                    "FIXNUM (Which has to be a return_value)");
261             
262             rcode = RLM_MODULE_FAIL;
263             goto finish;
264         }
265         
266         rcode = FIX2INT(rb_ary_entry(rb_result, 0));
267         
268         /*
269          *      Only process the results if we were passed a request.
270          */
271         if (request) {
272                 rb_reply_items = rb_ary_entry(rb_result, 1);
273                 rb_config_items = rb_ary_entry(rb_result, 2);
274         
275                 add_vp_tuple(&request->reply->vps, rb_reply_items,
276                              function_name);
277                 add_vp_tuple(&request->config_items, rb_config_items,
278                              function_name);
279         }
280     } else if (FIXNUM_P(rb_result)) {
281         rcode = FIX2INT(rb_result);
282     }
283     
284     finish:
285     return rcode;
286 }
287
288 static struct varlookup {
289     const char* name;
290     int value;
291 } constants[] = {
292     { "L_DBG", L_DBG},
293     { "L_AUTH", L_AUTH},
294     { "L_INFO", L_INFO},
295     { "L_ERR", L_ERR},
296     { "L_PROXY", L_PROXY},
297     { "RLM_MODULE_REJECT", RLM_MODULE_REJECT},
298     { "RLM_MODULE_FAIL", RLM_MODULE_FAIL},
299     { "RLM_MODULE_OK", RLM_MODULE_OK},
300     { "RLM_MODULE_HANDLED", RLM_MODULE_HANDLED},
301     { "RLM_MODULE_INVALID", RLM_MODULE_INVALID},
302     { "RLM_MODULE_USERLOCK", RLM_MODULE_USERLOCK},
303     { "RLM_MODULE_NOTFOUND", RLM_MODULE_NOTFOUND},
304     { "RLM_MODULE_NOOP", RLM_MODULE_NOOP},
305     { "RLM_MODULE_UPDATED", RLM_MODULE_UPDATED},
306     { "RLM_MODULE_NUMCODES", RLM_MODULE_NUMCODES},
307     { NULL, 0},
308 };
309
310 /*
311  * Import a user module and load a function from it
312  */
313 static int load_ruby_function(const char *f_name, int *func, VALUE module) {
314     if (f_name == NULL) {
315         *func = 0;
316     } else {
317         *func = rb_intern(f_name);
318         /* rb_intern returns a symbol of a function, not a function itself
319             it can be aplied to any recipient,
320             so we should check it for our module recipient
321          */
322         if (!rb_respond_to(module, *func))
323             *func = 0;
324     }
325     radlog(L_DBG, "load_ruby_function %s, result: %d", f_name, *func);
326     return 0;
327 }
328
329 /*
330  *      Do any per-module initialization that is separate to each
331  *      configured instance of the module.  e.g. set up connections
332  *      to external databases, read configuration files, set up
333  *      dictionary entries, etc.
334  *
335  *      If configuration information is given in the config section
336  *      that must be referenced in later calls, store a handle to it
337  *      in *instance otherwise put a null pointer there.
338  *
339  */
340 static int ruby_instantiate(CONF_SECTION *conf, void **instance)
341 {
342     rlm_ruby_t *data;
343     VALUE module;
344     int idx;
345
346     /*
347      * Initialize Ruby interpreter. Fatal error if this fails.
348      */
349
350     ruby_init();
351     ruby_init_loadpath();
352     ruby_script("radiusd");
353     /* disabling GC, it will eat your memory, but at least it will be stable. */
354     rb_gc_disable();
355
356
357     int status;
358     /*
359      *  Set up a storage area for instance data
360      */
361     *instance = data = talloc_zero(conf, rlm_ruby_t);
362     if (!data) return -1;
363
364     /*
365      *  If the configuration parameters can't be parsed, then
366      *  fail.
367      */
368     if (cf_section_parse(conf, data, module_config) < 0) {
369         return -1;
370     }
371
372     /*
373      * Setup our 'radiusd' module.
374      */
375
376     if ((module = data->pModule_builtin = rb_define_module(data->moduleName)) == 0) {
377         radlog(L_ERR, "Ruby rb_define_module failed");
378         return -1;
379     }
380     /*
381      * Load constants into module
382      */
383     for (idx = 0; constants[idx].name; idx++)
384         rb_define_const(module, constants[idx].name, INT2NUM(constants[idx].value));
385
386     /* Add functions into module */
387     rb_define_module_function(module, "radlog", radlog_rb, 2);
388
389     if (data->scriptFile == NULL) {
390         /* TODO: What actualy should we do? Exit with module fail? Or continue... but what the point then? */
391         radlog(L_ERR, "Script File was not set");
392     } else {
393         radlog(L_DBG, "Loading file %s...", data->scriptFile);
394         rb_load_protect(rb_str_new2(data->scriptFile), 0, &status);
395         if (!status)
396             radlog(L_DBG, "Loaded file %s", data->scriptFile);
397         else
398             radlog(L_ERR, "Error loading file %s status: %d", data->scriptFile, status);
399     }
400     /*
401      * Import user modules.
402      */
403 #define RLM_RUBY_LOAD(foo) if (load_ruby_function(#foo, &data->func_##foo, data->pModule_builtin)==-1) { \
404         return -1; \
405     }
406
407     RLM_RUBY_LOAD(instantiate);
408     RLM_RUBY_LOAD(authenticate);
409     RLM_RUBY_LOAD(authorize);
410     RLM_RUBY_LOAD(preacct);
411     RLM_RUBY_LOAD(accounting);
412     RLM_RUBY_LOAD(checksimul);
413     RLM_RUBY_LOAD(preproxy);
414     RLM_RUBY_LOAD(postproxy);
415     RLM_RUBY_LOAD(postauth);
416 #ifdef WITH_COA
417     RLM_RUBY_LOAD(recvcoa);
418     RLM_RUBY_LOAD(sendcoa);
419 #endif
420     RLM_RUBY_LOAD(detach);
421
422     /* Call the instantiate function.  No request.  Use the return value. */
423     return ruby_function(NULL, data->func_instantiate, data->pModule_builtin, "instantiate");
424 }
425
426 #define RLM_RUBY_FUNC(foo) static rlm_rcode_t ruby_##foo(void *instance, REQUEST *request) \
427 { \
428     return ruby_function(request, \
429                            ((struct rlm_ruby_t *)instance)->func_##foo,((struct rlm_ruby_t *)instance)->pModule_builtin, \
430                            #foo); \
431 }
432
433 RLM_RUBY_FUNC(authorize)
434 RLM_RUBY_FUNC(authenticate)
435 RLM_RUBY_FUNC(preacct)
436 RLM_RUBY_FUNC(accounting)
437 RLM_RUBY_FUNC(checksimul)
438 RLM_RUBY_FUNC(preproxy)
439 RLM_RUBY_FUNC(postproxy)
440 RLM_RUBY_FUNC(postauth)
441 #ifdef WITH_COA
442 RLM_RUBY_FUNC(recvcoa)
443 RLM_RUBY_FUNC(sendcoa)
444 #endif
445
446 static int ruby_detach(UNUSED void *instance)
447 {
448     ruby_finalize();
449     ruby_cleanup(0);
450
451     return 0;
452 }
453
454 /*
455  *      The module name should be the only globally exported symbol.
456  *      That is, everything else should be 'static'.
457  *
458  *      If the module needs to temporarily modify it's instantiation
459  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
460  *      The server will then take care of ensuring that the module
461  *      is single-threaded.
462  */
463 module_t rlm_ruby = {
464     RLM_MODULE_INIT,
465     "ruby",
466     //  RLM_TYPE_THREAD_SAFE,           /* type */
467     RLM_TYPE_THREAD_UNSAFE, /* type, ok, let's be honest, MRI is not yet treadsafe */
468     ruby_instantiate, /* instantiation */
469     ruby_detach, /* detach */
470     {
471         ruby_authenticate, /* authentication */
472         ruby_authorize, /* authorization */
473         ruby_preacct, /* preaccounting */
474         ruby_accounting, /* accounting */
475         ruby_checksimul, /* checksimul */
476         ruby_preproxy, /* pre-proxy */
477         ruby_postproxy, /* post-proxy */
478         ruby_postauth /* post-auth */
479 #ifdef WITH_COA
480         , ruby_recvcoa,
481         ruby_sendcoa
482 #endif
483     },
484 };