📄 rlm_perl.c
字号:
/* * rlm_perl.c * * Version: $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2002,2006 The FreeRADIUS server project * Copyright 2002 Boian Jordanov <bjordanov@orbitel.bg> */#include <freeradius-devel/ident.h>RCSID("$Id$")#include <freeradius-devel/radiusd.h>#include <freeradius-devel/modules.h>#ifdef DEBUG#undef DEBUG#endif#ifdef INADDR_ANY#undef INADDR_ANY#endif#include <EXTERN.h>#include <perl.h>#include <XSUB.h>#include <dlfcn.h>#include <semaphore.h>#ifdef __APPLE__extern char **environ;#endif/* * Define a structure for our module configuration. * * These variables do not need to be in a structure, but it's * a lot cleaner to do so, and a pointer to the structure can * be used as the instance handle. */typedef struct perl_inst { /* Name of the perl module */ char *module; /* Name of the functions for each module method */ char *func_authorize; char *func_authenticate; char *func_accounting; char *func_start_accounting; char *func_stop_accounting; char *func_preacct; char *func_checksimul; char *func_detach; char *func_xlat; char *func_pre_proxy; char *func_post_proxy; char *func_post_auth; char *xlat_name; char *perl_flags; PerlInterpreter *perl;} PERL_INST;/* * A mapping of configuration file names to internal variables. * * Note that the string is dynamically allocated, so it MUST * be freed. When the configuration file parse re-reads the string, * it free's the old one, and strdup's the new one, placing the pointer * to the strdup'd string into 'config.string'. This gets around * buffer over-flows. */static const CONF_PARSER module_config[] = { { "module", PW_TYPE_FILENAME, offsetof(PERL_INST,module), NULL, "module"}, { "func_authorize", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_authorize), NULL, "authorize"}, { "func_authenticate", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_authenticate), NULL, "authenticate"}, { "func_accounting", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_accounting), NULL, "accounting"}, { "func_preacct", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_preacct), NULL, "preacct"}, { "func_checksimul", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_checksimul), NULL, "checksimul"}, { "func_detach", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_detach), NULL, "detach"}, { "func_xlat", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_xlat), NULL, "xlat"}, { "func_pre_proxy", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_pre_proxy), NULL, "pre_proxy"}, { "func_post_proxy", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_post_proxy), NULL, "post_proxy"}, { "func_post_auth", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_post_auth), NULL, "post_auth"}, { "perl_flags", PW_TYPE_STRING_PTR, offsetof(PERL_INST,perl_flags), NULL, NULL}, { "func_start_accounting", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_start_accounting), NULL, NULL}, { "func_stop_accounting", PW_TYPE_STRING_PTR, offsetof(PERL_INST,func_stop_accounting), NULL, NULL}, { NULL, -1, 0, NULL, NULL } /* end the list */};/* * man perlembed */EXTERN_C void boot_DynaLoader(pTHX_ CV* cv);#ifdef USE_ITHREADS#define dl_librefs "DynaLoader::dl_librefs"#define dl_modules "DynaLoader::dl_modules"static void rlm_perl_clear_handles(pTHX){ AV *librefs = get_av(dl_librefs, FALSE); if (librefs) { av_clear(librefs); }}static void **rlm_perl_get_handles(pTHX){ I32 i; AV *librefs = get_av(dl_librefs, FALSE); AV *modules = get_av(dl_modules, FALSE); void **handles; if (!librefs) { radlog(L_ERR, "Could not get @%s for unloading.\n", dl_librefs); return NULL; } if (!(AvFILL(librefs) >= 0)) { return NULL; } handles = (void **)rad_malloc(sizeof(void *) * (AvFILL(librefs)+2)); for (i=0; i<=AvFILL(librefs); i++) { void *handle; SV *handle_sv = *av_fetch(librefs, i, FALSE); if(!handle_sv) { radlog(L_ERR, "Could not fetch $%s[%d]!\n", dl_librefs, (int)i); continue; } handle = (void *)SvIV(handle_sv); if (handle) { handles[i] = handle; } } av_clear(modules); av_clear(librefs); handles[i] = (void *)0; return handles;}static void rlm_perl_close_handles(void **handles){ int i; if (!handles) { return; } for (i=0; handles[i]; i++) { radlog(L_DBG, "close %p\n", handles[i]); dlclose(handles[i]); } free(handles);}static void rlm_perl_destruct(PerlInterpreter *perl){ char **orig_environ = NULL; dTHXa(perl); PERL_SET_CONTEXT(perl); PL_perl_destruct_level = 2; PL_origenviron = environ; { dTHXa(perl); } /* * FIXME: This shouldn't happen * */ while (PL_scopestack_ix > 1 ){ LEAVE; } perl_destruct(perl); perl_free(perl); if (orig_environ) { environ = orig_environ; }}static void rlm_destroy_perl(PerlInterpreter *perl){ void **handles; dTHXa(perl); PERL_SET_CONTEXT(perl); handles = rlm_perl_get_handles(aTHX); rlm_perl_destruct(perl); rlm_perl_close_handles(handles);}static pthread_key_t rlm_perl_key;static pthread_once_t rlm_perl_once = PTHREAD_ONCE_INIT;/* Create Key */static void rlm_perl_make_key(void){ pthread_key_create(&rlm_perl_key, rlm_destroy_perl);}static PerlInterpreter *rlm_perl_clone(PerlInterpreter *perl){ PerlInterpreter *interp; UV clone_flags = 0; PERL_SET_CONTEXT(perl); pthread_once(&rlm_perl_once, rlm_perl_make_key); interp = pthread_getspecific(rlm_perl_key); if (interp) return interp; interp = perl_clone(perl, clone_flags); { dTHXa(interp); }#if PERL_REVISION >= 5 && PERL_VERSION <8 call_pv("CLONE",0);#endif ptr_table_free(PL_ptr_table); PL_ptr_table = NULL; PERL_SET_CONTEXT(aTHX); rlm_perl_clear_handles(aTHX); pthread_setspecific(rlm_perl_key, interp); fprintf(stderr, "GOT CLONE %d %p\n", pthread_self(), interp); return interp;}#endifstatic void xs_init(pTHX){ char *file = __FILE__; /* DynaLoader is a special case */ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);}/* * * This is wrapper for radlog * Now users can call radiusd::radlog(level,msg) wich is the same * calling radlog from C code. * Boyan */static XS(XS_radiusd_radlog){ dXSARGS; if (items !=2) croak("Usage: radiusd::radlog(level, message)"); { int level; char *msg; level = (int) SvIV(ST(0)); msg = (char *) SvPV(ST(1), PL_na); /* * Because 'msg' is a 'char *', we don't want '%s', etc. * in it to give us printf-style vulnerabilities. */ radlog(level, "rlm_perl: %s", msg); } XSRETURN_NO;}/* * The xlat function */static size_t perl_xlat(void *instance, REQUEST *request, char *fmt, char *out, size_t freespace, RADIUS_ESCAPE_STRING func){ PERL_INST *inst= (PERL_INST *) instance; PerlInterpreter *perl; char params[1024], *ptr, *tmp; int count; size_t ret = 0; STRLEN n_a; /* * Do an xlat on the provided string (nice recursive operation). */ if (!radius_xlat(params, sizeof(params), fmt, request, func)) { radlog(L_ERR, "rlm_perl: xlat failed."); return 0; }#ifndef WITH_ITHREADS perl = inst->perl;#else perl = rlm_perl_clone(inst->perl); { dTHXa(perl); }#endif PERL_SET_CONTEXT(perl); { dSP; ENTER;SAVETMPS; ptr = strtok(params, " "); PUSHMARK(SP); while (ptr != NULL) { XPUSHs(sv_2mortal(newSVpv(ptr,0))); ptr = strtok(NULL, " "); } PUTBACK; count = call_pv(inst->func_xlat, G_SCALAR | G_EVAL); SPAGAIN; if (SvTRUE(ERRSV)) { radlog(L_ERR, "rlm_perl: perl_xlat exit %s\n", SvPV(ERRSV,n_a)); POPs ; } else if (count > 0) { tmp = POPp; strlcpy(out, tmp, freespace); ret = strlen(out); radlog(L_DBG,"rlm_perl: Len is %d , out is %s freespace is %d", ret, out,freespace); } PUTBACK ; FREETMPS ; LEAVE ; } return ret;}/* * Do any per-module initialization that is separate to each * configured instance of the module. e.g. set up connections * to external databases, read configuration files, set up * dictionary entries, etc. * * If configuration information is given in the config section * that must be referenced in later calls, store a handle to it * in *instance otherwise put a null pointer there. * * Boyan: * Setup a hashes wich we will use later * parse a module and give him a chance to live * */static int perl_instantiate(CONF_SECTION *conf, void **instance){ PERL_INST *inst = (PERL_INST *) instance; HV *rad_reply_hv; HV *rad_check_hv; HV *rad_config_hv; HV *rad_request_hv; HV *rad_request_proxy_hv; HV *rad_request_proxy_reply_hv; AV *end_AV; char *embed[4]; const char *xlat_name; int exitstatus = 0, argc=0; /* * Set up a storage area for instance data */ inst = rad_malloc(sizeof(PERL_INST)); memset(inst, 0, sizeof(PERL_INST)); /* * If the configuration parameters can't be parsed, then * fail. */ if (cf_section_parse(conf, inst, module_config) < 0) { free(inst); return -1; } embed[0] = NULL; if (inst->perl_flags) { embed[1] = inst->perl_flags; embed[2] = inst->module; embed[3] = "0"; argc = 4; } else { embed[1] = inst->module; embed[2] = "0"; argc = 3; }#ifdef USE_ITHREADS if ((inst->perl = perl_alloc()) == NULL) { radlog(L_DBG, "rlm_perl: No memory for allocating new perl !"); return (-1); } perl_construct(inst->perl); PL_perl_destruct_level = 2; { dTHXa(inst->perl); } PERL_SET_CONTEXT(inst->perl);#else if ((inst->perl = perl_alloc()) == NULL) { radlog(L_ERR, "rlm_perl: No memory for allocating new perl !"); return -1; } perl_construct(inst->perl);#endif#if PERL_REVISION >= 5 && PERL_VERSION >=8 PL_exit_flags |= PERL_EXIT_DESTRUCT_END;#endif exitstatus = perl_parse(inst->perl, xs_init, argc, embed, NULL); end_AV = PL_endav; PL_endav = Nullav; if(!exitstatus) { exitstatus = perl_run(inst->perl); } else { radlog(L_ERR,"rlm_perl: perl_parse failed: %s not found or has syntax errors. \n", inst->module); return (-1); } PL_endav = end_AV; newXS("radiusd::radlog",XS_radiusd_radlog, "rlm_perl.c"); rad_reply_hv = newHV(); rad_check_hv = newHV();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -