📄 rlm_perl.c
字号:
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 ; }#ifdef USE_ITHREADS pool_release(handle, instance);#endif 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 inst->perl = interp; 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(); rad_config_hv = newHV(); rad_request_hv = newHV(); rad_request_proxy_hv = newHV(); rad_request_proxy_reply_hv = newHV(); rad_reply_hv = get_hv("RAD_REPLY",1); rad_check_hv = get_hv("RAD_CHECK",1); rad_config_hv = get_hv("RAD_CONFIG",1); rad_request_hv = get_hv("RAD_REQUEST",1); rad_request_proxy_hv = get_hv("RAD_REQUEST_PROXY",1); rad_request_proxy_reply_hv = get_hv("RAD_REQUEST_PROXY_REPLY",1); xlat_name = cf_section_name2(conf); if (xlat_name == NULL) xlat_name = cf_section_name1(conf); if (xlat_name){ inst->xlat_name = strdup(xlat_name); xlat_register(xlat_name, perl_xlat, inst); }#ifdef USE_ITHREADS if ((init_pool(conf, inst)) == -1) { radlog(L_ERR,"Couldn't init a pool of perl clones. Exiting"); return -1; }#endif *instance = inst; return 0;}/* * get the vps and put them in perl hash * If one VP have multiple values it is added as array_ref * Example for this is Cisco-AVPair that holds multiple values. * Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'} */static void perl_store_vps(VALUE_PAIR *vp, HV *rad_hv){ VALUE_PAIR *nvp, *vpa, *vpn; AV *av; char buffer[1024]; int attr, len; hv_undef(rad_hv); nvp = paircopy(vp); while (nvp != NULL) { attr = nvp->attribute; vpa = paircopy2(nvp,attr); if (vpa->next) { av = newAV(); vpn = vpa; while (vpn) { len = vp_prints_value(buffer, sizeof(buffer), vpn, FALSE); av_push(av, newSVpv(buffer, len)); vpn = vpn->next; } hv_store(rad_hv, nvp->name, strlen(nvp->name), newRV_noinc((SV *) av), 0); } else { len = vp_prints_value(buffer, sizeof(buffer), vpa, FALSE); hv_store(rad_hv, vpa->name, strlen(vpa->name), newSVpv(buffer, len), 0); } pairfree(&vpa); vpa = nvp; while ((vpa != NULL) && (vpa->attribute == attr)) vpa = vpa->next; pairdelete(&nvp, attr); nvp = vpa; }}/* * * Verify that a Perl SV is a string and save it in FreeRadius * Value Pair Format * */static int pairadd_sv(VALUE_PAIR **vp, char *key, SV *sv, int operator) { char *val; VALUE_PAIR *vpp; if (SvOK(sv)) { val = SvPV_nolen(sv); vpp = pairmake(key, val, operator); if (vpp != NULL) { pairadd(vp, vpp); radlog(L_DBG, "rlm_perl: Added pair %s = %s", key, val); return 1; } else { radlog(L_DBG, "rlm_perl: ERROR: Failed to create pair %s = %s", key, val); } } return 0;}/* * Boyan : * Gets the content from hashes */static int get_hv_content(HV *my_hv, VALUE_PAIR **vp){ SV *res_sv, **av_sv; AV *av; char *key; I32 key_len, len, i, j; int ret=0; *vp = NULL; for (i = hv_iterinit(my_hv); i > 0; i--) { res_sv = hv_iternextsv(my_hv,&key,&key_len); if (SvROK(res_sv) && (SvTYPE(SvRV(res_sv)) == SVt_PVAV)) { av = (AV*)SvRV(res_sv); len = av_len(av); for (j = 0; j <= len; j++) { av_sv = av_fetch(av, j, 0); ret = pairadd_sv(vp, key, *av_sv, T_OP_ADD) + ret; } } else ret = pairadd_sv(vp, key, res_sv, T_OP_EQ) + ret; } return ret;}/* * Call the function_name inside the module * Store all vps in hashes %RAD_CHECK %RAD_REPLY %RAD_REQUEST * */static int rlmperl_call(void *instance, REQUEST *request, char *function_name){ PERL_INST *inst = instance; VALUE_PAIR *vp; int exitstatus=0, count; STRLEN n_a; 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;#ifdef USE_ITHREADS POOL_HANDLE *handle; if ((handle = pool_pop(instance)) == NULL) { return RLM_MODULE_FAIL; } radlog(L_DBG,"found interpetator at address 0x%lx",(unsigned long) handle->clone); { dTHXa(handle->clone); PERL_SET_CONTEXT(handle->clone); }#else PERL_SET_CONTEXT(inst->perl); radlog(L_DBG,"Using perl at 0x%lx",(unsigned long) inst->perl);#endif { dSP; ENTER; SAVETMPS; /* * Radius has told us to call this function, but none * is defined. */ if (!function_name) { return RLM_MODULE_FAIL; } rad_reply_hv = get_hv("RAD_REPLY",1); rad_check_hv = get_hv("RAD_CHECK",1); rad_config_hv = get_hv("RAD_CONFIG",1); rad_request_hv = get_hv("RAD_REQUEST",1); rad_request_proxy_hv = get_hv("RAD_REQUEST_PROXY",1); rad_request_proxy_reply_hv = get_hv("RAD_REQUEST_PROXY_REPLY",1); perl_store_vps(request->reply->vps, rad_reply_hv); perl_store_vps(request->config_items, rad_check_hv); perl_store_vps(request->packet->vps, rad_request_hv); perl_store_vps(request->config_items, rad_config_hv); if (request->proxy != NULL) { perl_store_vps(request->proxy->vps, rad_request_proxy_hv); } else { hv_undef(rad_request_proxy_hv); } if (request->proxy_reply !=NULL) { perl_store_vps(request->proxy_reply->vps, rad_request_proxy_reply_hv); } else { hv_undef(rad_request_proxy_reply_hv); } PUSHMARK(SP); /* * This way %RAD_xx can be pushed onto stack as sub parameters. * XPUSHs( newRV_noinc((SV *)rad_request_hv) ); * XPUSHs( newRV_noinc((SV *)rad_reply_hv) ); * XPUSHs( newRV_noinc((SV *)rad_check_hv) ); * PUTBACK; */ count = call_pv(function_name, G_SCALAR | G_EVAL | G_NOARGS); SPAGAIN; if (SvTRUE(ERRSV)) { radlog(L_ERR, "rlm_perl: perl_embed:: module = %s , func = %s exit status= %s\n", inst->module, function_name, SvPV(ERRSV,n_a)); POPs; } if (count == 1) { exitstatus = POPi; if (exitstatus >= 100 || exitstatus < 0) { exitstatus = RLM_MODULE_FAIL; } } PUTBACK; FREETMPS; LEAVE; vp = NULL; if ((get_hv_content(rad_request_hv, &vp)) > 0 ) { pairfree(&request->packet->vps); request->packet->vps = vp; vp = NULL; /* * Update cached copies */ request->username = pairfind(request->packet->vps, PW_USER_NAME); request->password = pairfind(request->packet->vps, PW_USER_PASSWORD); if (!request->password) request->password = pairfind(request->packet->vps, PW_CHAP_PASSWORD); } if ((get_hv_content(rad_reply_hv, &vp)) > 0 ) { pairfree(&request->reply->vps); request->reply->vps = vp; vp = NULL; } if ((get_hv_content(rad_check_hv, &vp)) > 0 ) { pairfree(&request->config_items); request->config_items = vp; vp = NULL; } if (request->proxy && (get_hv_content(rad_request_proxy_hv, &vp) > 0)) { pairfree(&request->proxy->vps); request->proxy->vps = vp; vp = NULL; } if (request->proxy_reply && (get_hv_content(rad_request_proxy_reply_hv, &vp) > 0)) { pairfree(&request->proxy_reply->vps); request->proxy_reply->vps = vp; vp = NULL; } }#ifdef USE_ITHREADS pool_release(handle,instance); radlog(L_DBG,"Unreserve perl at address 0x%lx", (unsigned long) handle->clone);#endif return exitstatus;}/* * Find the named user in this modules database. Create the set * of attribute-value pairs to check and reply with for this user * from the database. The authentication code only needs to check * the password, the rest is done here. */static int perl_authorize(void *instance, REQUEST *request){ return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_authorize);}/* * Authenticate the user with the given password. */static int perl_authenticate(void *instance, REQUEST *request){ return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_authenticate);}/* * Massage the request before recording it or proxying it */static int perl_preacct(void *instance, REQUEST *request){ return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_preacct);}/* * Write accounting information to this modules database. */static int perl_accounting(void *instance, REQUEST *request){ VALUE_PAIR *pair; int acctstatustype=0; if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) { acctstatustype = pair->vp_integer; } else { radlog(L_ERR, "Invalid Accounting Packet"); return RLM_MODULE_INVALID; } switch (acctstatustype) { case PW_STATUS_START: if (((PERL_INST *)instance)->func_start_accounting) { return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_start_accounting); } else { return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_accounting); } break; case PW_STATUS_STOP: if (((PERL_INST *)instance)->func_stop_accounting) { return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_stop_accounting); } else { return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_accounting); } break; default: return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_accounting); }}/* * Check for simultaneouse-use */static int perl_checksimul(void *instance, REQUEST *request){ return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_checksimul);}/* * Pre-Proxy request */static int perl_pre_proxy(void *instance, REQUEST *request){ return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_pre_proxy);}/* * Post-Proxy request */static int perl_post_proxy(void *instance, REQUEST *request){ return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_post_proxy);}/* * Pre-Auth request */static int perl_post_auth(void *instance, REQUEST *request){ return rlmperl_call(instance, request, ((PERL_INST *)instance)->func_post_auth);}/* * Detach a instance give a chance to a module to make some internal setup ... */static int perl_detach(void *instance){ PERL_INST *inst = (PERL_INST *) instance; int exitstatus = 0, count = 0;#ifdef USE_ITHREADS POOL_HANDLE *handle, *tmp, *tmp2; MUTEX_LOCK(&inst->perl_pool->mutex); inst->perl_pool->detach = yes; MUTEX_UNLOCK(&inst->perl_pool->mutex); for (handle = inst->perl_pool->head; handle != NULL; handle = handle->next) { radlog(L_DBG,"Detach perl 0x%lx", (unsigned long) handle->clone); /* * Wait until clone becomes idle */ MUTEX_LOCK(&handle->lock); /* * Give a clones chance to run detach function */ { dTHXa(handle->clone); PERL_SET_CONTEXT(handle->clone); { dSP; ENTER; SAVETMPS; PUSHMARK(SP); count = call_pv(inst->func_detach, G_SCALAR | G_EVAL ); SPAGAIN; if (count == 1) { exitstatus = POPi; /* * FIXME: bug in perl * */ if (exitstatus >= 100 || exitstatus < 0) { exitstatus = RLM_MODULE_FAIL; } } PUTBACK; FREETMPS; LEAVE; radlog(L_DBG,"detach at 0x%lx returned status %d", (unsigned long) handle->clone, exitstatus); } } MUTEX_UNLOCK(&handle->lock); } /* * Free handles */ for (tmp = inst->perl_pool->head; tmp !=NULL ; tmp = tmp2) { tmp2 = tmp->next; radlog(L_DBG,"rlm_perl:: Destroy perl"); rlm_perl_destruct(tmp->clone); delete_pool_handle(tmp,inst); } { dTHXa(inst->perl);#endif /* USE_ITHREADS */ PERL_SET_CONTEXT(inst->perl); { dSP; ENTER; SAVETMPS; PUSHMARK(SP); count = call_pv(inst->func_detach, G_SCALAR | G_EVAL ); SPAGAIN; if (count == 1) { exitstatus = POPi; if (exitstatus >= 100 || exitstatus < 0) { exitstatus = RLM_MODULE_FAIL; } } PUTBACK; FREETMPS; LEAVE; }#ifdef USE_ITHREADS }#endif xlat_unregister(inst->xlat_name, perl_xlat); free(inst->xlat_name);#ifdef USE_ITHREADS free(inst->perl_pool->head); free(inst->perl_pool->tail); MUTEX_DESTROY(&inst->perl_pool->mutex); free(inst->perl_pool); rlm_perl_destruct(inst->perl);#else perl_destruct(inst->perl); perl_free(inst->perl);#endif free(inst); return exitstatus;}/* * The module name should be the only globally exported symbol. * That is, everything else should be 'static'. * * If the module needs to temporarily modify it's instantiation * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE. * The server will then take care of ensuring that the module * is single-threaded. */module_t rlm_perl = { RLM_MODULE_INIT, "perl", /* Name */#ifdef USE_ITHREADS RLM_TYPE_THREAD_SAFE, /* type */#else RLM_TYPE_THREAD_UNSAFE,#endif perl_instantiate, /* instantiation */ perl_detach, /* detach */ { perl_authenticate, /* authenticate */ perl_authorize, /* authorize */ perl_preacct, /* preacct */ perl_accounting, /* accounting */ perl_checksimul, /* check simul */ perl_pre_proxy, /* pre-proxy */ perl_post_proxy, /* post-proxy */ perl_post_auth /* post-auth */ },};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -