6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2002 The FreeRADIUS server project
21 * Copyright 2002 Boian Jordanov <bjordanov@orbitel.bg>
25 #include "libradius.h"
45 static const char rcsid[] = "$Id$";
48 * Define a structure for our module configuration.
50 * These variables do not need to be in a structure, but it's
51 * a lot cleaner to do so, and a pointer to the structure can
52 * be used as the instance handle.
54 typedef struct perl_config {
60 * Some other things will be added in future
62 typedef struct perl_inst {
63 PerlInterpreter *perl;
70 * A mapping of configuration file names to internal variables.
72 * Note that the string is dynamically allocated, so it MUST
73 * be freed. When the configuration file parse re-reads the string,
74 * it free's the old one, and strdup's the new one, placing the pointer
75 * to the strdup'd string into 'config.string'. This gets around
78 static CONF_PARSER module_config[] = {
79 { "cmd", PW_TYPE_STRING_PTR, offsetof(PERL_CONFIG,cmd), NULL, NULL},
80 { "persistent", PW_TYPE_STRING_PTR, offsetof(PERL_CONFIG,persistent), NULL, NULL},
81 { NULL, -1, 0, NULL, NULL } /* end the list */
87 EXTERN_C void boot_DynaLoader(pTHX_ CV* cv);
90 * Do any per-module initialization. e.g. set up connections
91 * to external databases, read configuration files, set up
92 * dictionary entries, etc.
94 * Try to avoid putting too much stuff in here - it's better to
95 * do it in instantiate() where it is not global.
97 static int perl_init(void)
100 * Everything's OK, return without an error.
109 static void xs_init(pTHX)
111 const char *file = __FILE__;
114 /* DynaLoader is a special case */
115 DEBUG("rlm_perl:: xs_init enter \n");
116 newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
117 DEBUG("rlm_perl:: xs_init leave \n");
124 * Do any per-module initialization that is separate to each
125 * configured instance of the module. e.g. set up connections
126 * to external databases, read configuration files, set up
127 * dictionary entries, etc.
129 * If configuration information is given in the config section
130 * that must be referenced in later calls, store a handle to it
131 * in *instance otherwise put a null pointer there.
133 static int perl_instantiate(CONF_SECTION *conf, void **instance)
140 * Set up a storage area for instance data
143 inst = rad_malloc(sizeof(PERL_INST));
144 memset(inst, 0, sizeof(PERL_INST));
146 inst->config = rad_malloc(sizeof(PERL_CONFIG));
147 memset(inst->config, 0, sizeof(PERL_CONFIG));
149 * If the configuration parameters can't be parsed, then
152 if (cf_section_parse(conf, inst->config, module_config) < 0) {
160 * Prepare perl instance
163 if((inst->perl = perl_alloc()) == NULL) {
164 radlog(L_INFO, "no memory!");
168 PERL_SET_CONTEXT(inst->perl);
169 perl_construct(inst->perl);
171 PERL_SET_CONTEXT(inst->perl);
173 embed[0] = inst->config->persistent;
175 exitstatus = perl_parse(inst->perl, xs_init, 1, embed, NULL);
177 PERL_SET_CONTEXT(inst->perl);
179 exitstatus = perl_run(inst->perl);
181 radlog(L_INFO,"perl_parse failed: %s not found or has syntax errors. \n", inst->config->persistent);
185 inst->env_hv = perl_get_hv("ENV",0);
186 inst->result_hv = perl_get_hv("main::result",1);
194 * Boyan get the request and put them in perl hash
195 * which will be given to perl cmd
197 static void perl_env(VALUE_PAIR *vp, PERL_INST *inst)
201 hv_clear(inst->env_hv);
202 hv_clear(inst->result_hv);
204 for ( ; vp != NULL; vp = vp->next) {
207 len = vp_prints_value(buffer, sizeof(buffer), vp, FALSE);
209 hv_store(inst->env_hv, vp->name, strlen(vp->name),
210 newSVpv(buffer, len),0);
215 * return structs and status 0 OK 1 Not
218 static int rlmperl_call(void *instance, REQUEST *request)
221 PERL_INST *inst = (PERL_INST *) instance;
224 char *key, *val, *ptr, *p;
225 char *args[] = {NULL, DO_CLEAN, NULL};
229 int exitstatus = 0, comma = 0;
232 args[0] = inst->config->cmd;
234 perl_env(request->packet->vps, inst);
236 for (i = hv_iterinit(inst->env_hv); i > 0; i--) {
237 res_sv = hv_iternextsv(inst->env_hv, &key, &key_len);
238 val = SvPV(res_sv,val_len);
239 radlog(L_DBG, "ENV %s= %s", key, val);
242 PERL_SET_CONTEXT(inst->perl);
243 call_argv("Embed::Persistent::eval_file", G_DISCARD | G_EVAL, args);
248 exitstatus = SvIV(perl_get_sv("!",FALSE));;
249 radlog(L_INFO, "exit status=%d, %s\n", exitstatus,
254 PERL_SET_CONTEXT(inst->perl);
256 for (i = hv_iterinit(inst->result_hv); i > 0; i--) {
257 res_sv = hv_iternextsv(inst->result_hv,&key,&key_len);
258 val = SvPV(res_sv,val_len);
259 sprintf(ptr, "%s=\"%s\"\n", key, val); /* FIXME: snprintf */
260 ptr += key_len + val_len + 4;
262 /* perl_free(inst->perl); */
267 for (p = answer; *p; p++) {
269 *p = comma ? ' ' : ',';
272 if (*p == ',') comma++;
276 * Replace any trailing comma by a NUL.
278 if (answer[strlen(answer) - 1] == ',') {
279 answer[strlen(answer) - 1] = '\0';
281 radlog(L_INFO,"perl_embed :: value-pairs: %s", answer);
283 if (userparse(answer, &vp) < 0) {
284 radlog(L_ERR, "perl_embed :: %s: unparsable reply", args[0]);
286 pairmove(&request->reply->vps, &vp);
294 * Find the named user in this modules database. Create the set
295 * of attribute-value pairs to check and reply with for this user
296 * from the database. The authentication code only needs to check
297 * the password, the rest is done here.
299 static int perl_authorize(void *instance, REQUEST *request)
303 radlog(L_INFO,"perl_embed :: Enter Authorize");
305 if ((status = rlmperl_call(instance, request)) == 0) {
306 return RLM_MODULE_OK;
309 return RLM_MODULE_FAIL;
313 * Authenticate the user with the given password.
315 static int perl_authenticate(void *instance, REQUEST *request)
319 radlog(L_INFO,"perl_embed :: Enter Auth");
321 if ((status = rlmperl_call(instance, request)) == 0) {
322 return RLM_MODULE_OK;
325 return RLM_MODULE_FAIL;
330 * Massage the request before recording it or proxying it
332 static int perl_preacct(void *instance, REQUEST *request)
336 radlog(L_INFO,"mod_perl :: Enter PreAccounting");
338 if ((status = rlmperl_call(instance, request)) == 0) {
339 return RLM_MODULE_OK;
342 return RLM_MODULE_FAIL;
346 * Write accounting information to this modules database.
349 static int perl_accounting(void *instance, REQUEST *request)
353 radlog(L_INFO,"mod_perl :: Enter Accounting");
355 if ((status = (rlmperl_call(instance, request))) == 0) {
356 return RLM_MODULE_OK;
359 return RLM_MODULE_FAIL;
363 * Detach a instance free all ..
365 static int perl_detach(void *instance)
367 PERL_INST *inst=instance;
368 PERL_SET_CONTEXT(inst->perl);
369 perl_destruct(inst->perl);
370 PERL_SET_CONTEXT(inst->perl);
371 perl_free(inst->perl);
374 hv_clear(inst->env_hv);
375 hv_clear(inst->result_hv);
382 * The module name should be the only globally exported symbol.
383 * That is, everything else should be 'static'.
385 * If the module needs to temporarily modify it's instantiation
386 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
387 * The server will then take care of ensuring that the module
388 * is single-threaded.
390 module_t rlm_perl = {
392 RLM_TYPE_THREAD_UNSAFE, /* type */
393 perl_init, /* initialization */
394 perl_instantiate, /* instantiation */
401 perl_detach, /* detach */