#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <Python.h>
#define Pyx_BLOCK_THREADS {PyGILState_STATE __gstate = PyGILState_Ensure();
#define Pyx_UNBLOCK_THREADS PyGILState_Release(__gstate);}
struct py_function_def {
PyObject *module;
PyObject *function;
char *module_name;
char *function_name;
};
struct rlm_python_t {
struct py_function_def
instantiate,
authorize,
authenticate,
preacct,
accounting,
checksimul,
pre_proxy,
post_proxy,
post_auth,
#ifdef WITH_COA
recv_coa,
send_coa,
#endif
detach;
};
static CONF_PARSER module_config[] = {
#define A(x) { "mod_" #x, PW_TYPE_STRING_PTR, offsetof(struct rlm_python_t, x.module_name), NULL, NULL }, \
{ "func_" #x, PW_TYPE_STRING_PTR, offsetof(struct rlm_python_t, x.function_name), NULL, NULL },
A(instantiate)
A(authorize)
A(authenticate)
A(preacct)
A(accounting)
A(checksimul)
A(pre_proxy)
A(post_proxy)
A(post_auth)
#ifdef WITH_COA
A(recv_coa)
A(send_coa)
#endif
A(detach)
#undef A
{ NULL, -1, 0, NULL, NULL }
};
static struct {
const char *name;
int value;
} radiusd_constants[] = {
#define A(x) { #x, x },
A(L_DBG)
A(L_AUTH)
A(L_INFO)
A(L_ERR)
A(L_PROXY)
A(L_CONS)
A(RLM_MODULE_REJECT)
A(RLM_MODULE_FAIL)
A(RLM_MODULE_OK)
A(RLM_MODULE_HANDLED)
A(RLM_MODULE_INVALID)
A(RLM_MODULE_USERLOCK)
A(RLM_MODULE_NOTFOUND)
A(RLM_MODULE_NOOP)
A(RLM_MODULE_UPDATED)
A(RLM_MODULE_NUMCODES)
#undef A
{ NULL, 0 },
};
static PyObject *radiusd_module = NULL;
static PyObject *python_radlog(UNUSED PyObject *module, PyObject *args)
{
int status;
char *msg;
if (!PyArg_ParseTuple(args, "is", &status, &msg)) {
return NULL;
}
radlog(status, "%s", msg);
Py_INCREF(Py_None);
return Py_None;
}
static PyMethodDef radiusd_methods[] = {
{ "radlog", &python_radlog, METH_VARARGS,
"radiusd.radlog(level, msg)\n\n" \
"Print a message using radiusd logging system. level should be one of the\n" \
"constants L_DBG, L_AUTH, L_INFO, L_ERR, L_PROXY, L_CONS\n"
},
{ NULL, NULL, 0, NULL },
};
static void python_error(void)
{
PyObject
*pType = NULL,
*pValue = NULL,
*pTraceback = NULL,
*pStr1 = NULL,
*pStr2 = NULL;
Pyx_BLOCK_THREADS
PyErr_Fetch(&pType, &pValue, &pTraceback);
if (pType == NULL || pValue == NULL)
goto failed;
if (((pStr1 = PyObject_Str(pType)) == NULL) ||
((pStr2 = PyObject_Str(pValue)) == NULL))
goto failed;
radlog(L_ERR, "rlm_python:EXCEPT:%s: %s", PyString_AsString(pStr1), PyString_AsString(pStr2));
failed:
Py_XDECREF(pStr1);
Py_XDECREF(pStr2);
Py_XDECREF(pType);
Py_XDECREF(pValue);
Py_XDECREF(pTraceback);
Pyx_UNBLOCK_THREADS
}
static int python_init(void)
{
int i;
if (radiusd_module) return 0;
Py_SetProgramName("radiusd");
Py_Initialize();
PyEval_InitThreads();
if ((radiusd_module = Py_InitModule3("radiusd", radiusd_methods,
"FreeRADIUS Module.")) == NULL)
goto failed;
for (i = 0; radiusd_constants[i].name; i++)
if ((PyModule_AddIntConstant(radiusd_module,
radiusd_constants[i].name,
radiusd_constants[i].value)) < 0)
goto failed;
PyEval_ReleaseLock();
radlog(L_DBG, "python_init done");
return 0;
failed:
python_error();
Py_XDECREF(radiusd_module);
radiusd_module = NULL;
Py_Finalize();
return -1;
}
#if 0
static int python_destroy(void)
{
Pyx_BLOCK_THREADS
Py_XDECREF(radiusd_module);
Py_Finalize();
Pyx_UNBLOCK_THREADS
return 0;
}
#endif
static void python_vptuple(VALUE_PAIR **vpp, PyObject *pValue,
const char *funcname)
{
int i;
int tuplesize;
VALUE_PAIR *vp;
if (pValue == Py_None)
return;
if (!PyTuple_CheckExact(pValue)) {
radlog(L_ERR, "rlm_python:%s: non-tuple passed", funcname);
return;
}
tuplesize = PyTuple_GET_SIZE(pValue);
for (i = 0; i < tuplesize; i++) {
PyObject *pTupleElement = PyTuple_GET_ITEM(pValue, i);
PyObject *pStr1;
PyObject *pStr2;
int pairsize;
const char *s1;
const char *s2;
if (!PyTuple_CheckExact(pTupleElement)) {
radlog(L_ERR, "rlm_python:%s: tuple element %d is not a tuple", funcname, i);
continue;
}
if ((pairsize = PyTuple_GET_SIZE(pTupleElement)) != 2) {
radlog(L_ERR, "rlm_python:%s: tuple element %d is a tuple of size %d. Must be 2", funcname, i, pairsize);
continue;
}
pStr1 = PyTuple_GET_ITEM(pTupleElement, 0);
pStr2 = PyTuple_GET_ITEM(pTupleElement, 1);
if ((!PyString_CheckExact(pStr1)) || (!PyString_CheckExact(pStr2))) {
radlog(L_ERR, "rlm_python:%s: tuple element %d must be as (str, str)", funcname, i);
continue;
}
s1 = PyString_AsString(pStr1);
s2 = PyString_AsString(pStr2);
vp = pairmake(s1, s2, T_OP_EQ);
if (vp != NULL) {
pairadd(vpp, vp);
radlog(L_DBG, "rlm_python:%s: '%s' = '%s'", funcname, s1, s2);
} else {
radlog(L_DBG, "rlm_python:%s: Failed: '%s' = '%s'", funcname, s1, s2);
}
}
}
static int python_populate_vptuple(PyObject *pPair, VALUE_PAIR *vp)
{
PyObject *pStr = NULL;
char buf[1024];
if (vp->flags.has_tag)
pStr = PyString_FromFormat("%s:%d", vp->name, vp->flags.tag);
else
pStr = PyString_FromString(vp->name);
if (pStr == NULL)
goto failed;
PyTuple_SET_ITEM(pPair, 0, pStr);
vp_prints_value(buf, sizeof(buf), vp, 1);
if ((pStr = PyString_FromString(buf)) == NULL)
goto failed;
PyTuple_SET_ITEM(pPair, 1, pStr);
return 0;
failed:
return -1;
}
static int python_function(REQUEST *request, PyObject *pFunc,
const char *funcname)
{
VALUE_PAIR *vp;
PyObject *pRet = NULL;
PyObject *pArgs = NULL;
int tuplelen;
int ret;
PyGILState_STATE gstate;
if (pFunc == NULL)
return RLM_MODULE_OK;
ret = RLM_MODULE_OK;
tuplelen = 0;
if (request != NULL) {
for (vp = request->packet->vps; vp; vp = vp->next)
tuplelen++;
}
gstate = PyGILState_Ensure();
if (tuplelen == 0) {
Py_INCREF(Py_None);
pArgs = Py_None;
} else {
int i = 0;
if ((pArgs = PyTuple_New(tuplelen)) == NULL)
goto failed;
for (vp = request->packet->vps;
vp != NULL;
vp = vp->next, i++) {
PyObject *pPair;
if ((pPair = PyTuple_New(2)) == NULL)
goto failed;
if (python_populate_vptuple(pPair, vp) == 0) {
PyTuple_SET_ITEM(pArgs, i, pPair);
} else {
Py_INCREF(Py_None);
PyTuple_SET_ITEM(pArgs, i, Py_None);
Py_DECREF(pPair);
}
}
}
pRet = PyObject_CallFunctionObjArgs(pFunc, pArgs, NULL);
if (pRet == NULL)
goto failed;
if (request == NULL)
goto okay;
if (PyTuple_CheckExact(pRet)) {
PyObject *pTupleInt;
if (PyTuple_GET_SIZE(pRet) != 3) {
radlog(L_ERR, "rlm_python:%s: tuple must be (return, replyTuple, configTuple)", funcname);
goto failed;
}
pTupleInt = PyTuple_GET_ITEM(pRet, 0);
if (!PyInt_CheckExact(pTupleInt)) {
radlog(L_ERR, "rlm_python:%s: first tuple element not an integer", funcname);
goto failed;
}
ret = PyInt_AsLong(pTupleInt);
python_vptuple(&request->reply->vps,
PyTuple_GET_ITEM(pRet, 1), funcname);
python_vptuple(&request->config_items,
PyTuple_GET_ITEM(pRet, 2), funcname);
} else if (PyInt_CheckExact(pRet)) {
ret = PyInt_AsLong(pRet);
} else if (pRet == Py_None) {
ret = RLM_MODULE_OK;
} else {
radlog(L_ERR, "rlm_python:%s: function did not return a tuple or None", funcname);
goto failed;
}
okay:
Py_DECREF(pArgs);
Py_DECREF(pRet);
PyGILState_Release(gstate);
return ret;
failed:
python_error();
Py_XDECREF(pArgs);
Py_XDECREF(pRet);
PyGILState_Release(gstate);
return -1;
}
static int python_load_function(struct py_function_def *def)
{
const char *funcname = "python_load_function";
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
if (def->module_name != NULL && def->function_name != NULL) {
if ((def->module = PyImport_ImportModule(def->module_name)) == NULL) {
radlog(L_ERR, "rlm_python:%s: module '%s' is not found", funcname, def->module_name);
goto failed;
}
if ((def->function = PyObject_GetAttrString(def->module, def->function_name)) == NULL) {
radlog(L_ERR, "rlm_python:%s: function '%s.%s' is not found", funcname, def->module_name, def->function_name);
goto failed;
}
if (!PyCallable_Check(def->function)) {
radlog(L_ERR, "rlm_python:%s: function '%s.%s' is not callable", funcname, def->module_name, def->function_name);
goto failed;
}
}
PyGILState_Release(gstate);
return 0;
failed:
python_error();
radlog(L_ERR, "rlm_python:%s: failed to import python function '%s.%s'", funcname, def->module_name, def->function_name);
Py_XDECREF(def->function);
def->function = NULL;
Py_XDECREF(def->module);
def->module = NULL;
PyGILState_Release(gstate);
return -1;
}
static void python_objclear(PyObject **ob)
{
if (*ob != NULL) {
Pyx_BLOCK_THREADS
Py_DECREF(*ob);
Pyx_UNBLOCK_THREADS
*ob = NULL;
}
}
static void free_and_null(char **p)
{
if (*p != NULL) {
free(*p);
*p = NULL;
}
}
static void python_funcdef_clear(struct py_function_def *def)
{
python_objclear(&def->function);
python_objclear(&def->module);
free_and_null(&def->function_name);
free_and_null(&def->module_name);
}
static void python_instance_clear(struct rlm_python_t *data)
{
#define A(x) python_funcdef_clear(&data->x)
A(instantiate);
A(authorize);
A(authenticate);
A(preacct);
A(accounting);
A(checksimul);
A(detach);
#undef A
}
static int python_instantiate(CONF_SECTION *conf, void **instance)
{
struct rlm_python_t *data = NULL;
if ((data = malloc(sizeof(*data))) == NULL)
return -1;
memset(data, 0, sizeof(*data));
if (python_init() != 0) {
free(data);
return -1;
}
if (cf_section_parse(conf, data, module_config) < 0) {
free(data);
return -1;
}
#define A(x) if (python_load_function(&data->x) < 0) goto failed
A(instantiate);
A(authenticate);
A(authorize);
A(preacct);
A(accounting);
A(checksimul);
A(pre_proxy);
A(post_proxy);
A(post_auth);
#ifdef WITH_COA
A(recv_coa);
A(send_coa);
#endif
A(detach);
#undef A
*instance = data;
return python_function(NULL, data->instantiate.function,
"instantiate");
failed:
python_error();
python_instance_clear(data);
free(data);
return -1;
}
static int python_detach(void *instance)
{
struct rlm_python_t *data = (struct rlm_python_t *) instance;
int ret;
ret = python_function(NULL, data->detach.function, "detach");
python_instance_clear(data);
free(data);
return ret;
}
#define A(x) static int python_##x(void *instance, REQUEST *request) { \
return python_function(request, ((struct rlm_python_t *)instance)->x.function, #x); \
}
A(authenticate)
A(authorize)
A(preacct)
A(accounting)
A(checksimul)
A(pre_proxy)
A(post_proxy)
A(post_auth)
#ifdef WITH_COA
A(recv_coa)
A(send_coa)
#endif
#undef A
module_t rlm_python = {
RLM_MODULE_INIT,
"python",
RLM_TYPE_THREAD_SAFE,
python_instantiate,
python_detach,
{
python_authenticate,
python_authorize,
python_preacct,
python_accounting,
python_checksimul,
python_pre_proxy,
python_post_proxy,
python_post_auth
#ifdef WITH_COA
, python_recv_coa,
python_send_coa
#endif
}
};