diff --git a/src/modules/rlm_python/rlm_python.c b/src/modules/rlm_python/rlm_python.c index b3fcd5a..9c5636a 100644 --- a/src/modules/rlm_python/rlm_python.c +++ b/src/modules/rlm_python/rlm_python.c @@ -16,47 +16,44 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * - * Copyright 2000,2006 The FreeRADIUS server project + * Copyright 2000 The FreeRADIUS server project * Copyright 2002 Miguel A.L. Paraz * Copyright 2002 Imperium Technology, Inc. + * - rewritten by Paul P. Komkoff Jr */ #include RCSID("$Id$") - #include #include #include -/* - * 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 rlm_python_t { +#define Pyx_BLOCK_THREADS {PyGILState_STATE __gstate = PyGILState_Ensure(); +#define Pyx_UNBLOCK_THREADS PyGILState_Release(__gstate);} -#define RLM_PYTHON_STRUCT(foo) char *mod_##foo; \ - char *func_##foo; \ - PyObject *pModule_##foo; \ - PyObject *pFunc_##foo - - RLM_PYTHON_STRUCT(instantiate); - RLM_PYTHON_STRUCT(authorize); - RLM_PYTHON_STRUCT(authenticate); - RLM_PYTHON_STRUCT(preacct); - RLM_PYTHON_STRUCT(accounting); - RLM_PYTHON_STRUCT(checksimul); - RLM_PYTHON_STRUCT(preproxy); - RLM_PYTHON_STRUCT(postproxy); - RLM_PYTHON_STRUCT(postauth); - RLM_PYTHON_STRUCT(detach); - - PyObject *pModule_builtin; +/* TODO: The only needed thing here is function. Anything else is required for + * initialization only. I will remove it, putting a symbolic constant here instead. + */ -} rlm_python_t; +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, + detach; +}; /* * A mapping of configuration file names to internal variables. @@ -67,429 +64,458 @@ typedef struct rlm_python_t { * to the strdup'd string into 'config.string'. This gets around * buffer over-flows. */ -#define RLM_PYTHON_CONF(foo) { "mod_"#foo, PW_TYPE_STRING_PTR, \ - offsetof(rlm_python_t, mod_##foo), NULL, NULL}, \ - { "func_"#foo, PW_TYPE_STRING_PTR, \ - offsetof(rlm_python_t, func_##foo), NULL, NULL} - -static const CONF_PARSER module_config[] = { - RLM_PYTHON_CONF(instantiate), - RLM_PYTHON_CONF(authorize), - RLM_PYTHON_CONF(authenticate), - RLM_PYTHON_CONF(preacct), - RLM_PYTHON_CONF(accounting), - RLM_PYTHON_CONF(checksimul), - RLM_PYTHON_CONF(preproxy), - RLM_PYTHON_CONF(postproxy), - RLM_PYTHON_CONF(postauth), - RLM_PYTHON_CONF(detach), - - { NULL, -1, 0, NULL, NULL } /* end the list */ +static CONF_PARSER module_config[] = { + +#define A(x) { "mod_" #x, PW_TYPE_STRING_PTR, offsetof(struct rlm_python_t, x.module_name) }, \ + { "func_" #x, PW_TYPE_STRING_PTR, offsetof(struct rlm_python_t, x.function_name) }, + + A(instantiate) + A(authorize) + A(authenticate) + A(preacct) + A(accounting) + A(checksimul) + A(detach) + +#undef A + + { NULL, -1, 0, NULL, NULL } /* end the list */ +}; + +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 + + { 0 }, }; + +/* Let assume that radiusd module is only one since we have only one intepreter */ + +static PyObject *radiusd_module = NULL; + /* * radiusd Python functions */ /* radlog wrapper */ -static PyObject *radlog_py(UNUSED const PyObject *self, PyObject *args) { - int status; - char *msg; +static PyObject *python_radlog(PyObject *module, PyObject *args) { + int status; + char *msg; - if (!PyArg_ParseTuple(args, "is", &status, &msg)) { - return NULL; - } + if (!PyArg_ParseTuple(args, "is", &status, &msg)) { + return NULL; + } - radlog(status, msg); - return Py_None; + radlog(status, "%s", msg); + Py_INCREF(Py_None); + + return Py_None; } static PyMethodDef radiusd_methods[] = { - {"radlog", (PyCFunction)radlog_py, METH_VARARGS, "freeradius radlog()."}, - {NULL, NULL, 0, NULL} + { "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" + }, + { 0 }, }; +static void python_error() { + 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 +} -/* Extract string representation of Python error. */ -static void python_error(void) { - PyObject *pType, *pValue, *pTraceback, *pStr1, *pStr2; +static int python_init_really() { + int i; - PyErr_Fetch(&pType, &pValue, &pTraceback); - pStr1 = PyObject_Str(pType); - pStr2 = PyObject_Str(pValue); + Py_SetProgramName("radiusd"); + Py_Initialize(); + PyEval_InitThreads(); // This also grabs a lock - radlog(L_ERR, "%s: %s\n", - PyString_AsString(pStr1), PyString_AsString(pStr2)); -} + if ((radiusd_module = Py_InitModule3("radiusd", radiusd_methods, + "FreeRADIUS Module.")) == NULL) + goto failed; -/* Tuple to value pair conversion */ -static void add_vp_tuple(VALUE_PAIR **vpp, PyObject *pValue, - const char *function_name) { - int i, outertuplesize; - VALUE_PAIR *vp; + for (i = 0; radiusd_constants[i].name; i++) + if ((PyModule_AddIntConstant(radiusd_module, radiusd_constants[i].name, + radiusd_constants[i].value)) < 0) + goto failed; - /* If the Python function gave us None for the tuple, then just return. */ - if (pValue == Py_None) { - return; - } + PyEval_ReleaseLock(); // Drop lock grabbed by InitThreads - if (!PyTuple_Check(pValue)) { - radlog(L_ERR, "%s: non-tuple passed", function_name); - } + radlog(L_DBG, "python_init done"); + return 0; - /* Get the tuple size. */ - outertuplesize = PyTuple_Size(pValue); - - for (i = 0; i < outertuplesize; i++) { - PyObject *pTupleElement = PyTuple_GetItem(pValue, i); - - if ((pTupleElement != NULL) && - (PyTuple_Check(pTupleElement))) { - - /* Check if it's a pair */ - int tuplesize; - - if ((tuplesize = PyTuple_Size(pTupleElement)) != 2) { - radlog(L_ERR, "%s: tuple element %d is a tuple " - " of size %d. must be 2\n", function_name, - i, tuplesize); - } - else { - PyObject *pString1, *pString2; - - pString1 = PyTuple_GetItem(pTupleElement, 0); - pString2 = PyTuple_GetItem(pTupleElement, 1); - - /* xxx PyString_Check does not compile here */ - if ((pString1 != NULL) && - (pString2 != NULL) && - PyObject_TypeCheck(pString1,&PyString_Type) && - PyObject_TypeCheck(pString2,&PyString_Type)) { - - - const char *s1, *s2; - - /* pairmake() will convert and find any - * errors in the pair. - */ - - s1 = PyString_AsString(pString1); - s2 = PyString_AsString(pString2); - - if ((s1 != NULL) && (s2 != NULL)) { - radlog(L_DBG, "%s: %s = %s ", - function_name, s1, s2); - - /* xxx Might need to support other T_OP */ - vp = pairmake(s1, s2, T_OP_EQ); - if (vp != NULL) { - pairadd(vpp, vp); - radlog(L_DBG, "%s: s1, s2 OK\n", - function_name); - } - else { - radlog(L_DBG, "%s: s1, s2 FAILED\n", - function_name); - } - } - else { - radlog(L_ERR, "%s: string conv failed\n", - function_name); - } - - } - else { - radlog(L_ERR, "%s: tuple element %d must be " - "(string, string)", function_name, i); - } - } - } - else { - radlog(L_ERR, "%s: tuple element %d is not a tuple\n", - function_name, i); - } - } +failed: + python_error(); + Py_XDECREF(radiusd_module); + radiusd_module = NULL; + Py_Finalize(); + return -1; +} +static int python_init() { + if (!radiusd_module) + return python_init_really(); + + return 0; } -/* This is the core Python function that the others wrap around. - * Pass the value-pair print strings in a tuple. - * xxx We're not checking the errors. If we have errors, what do we do? - */ +#if 0 -static int python_function(REQUEST *request, - PyObject *pFunc, const char *function_name) -{ -#define BUF_SIZE 1024 +static int python_destroy() { + Pyx_BLOCK_THREADS + Py_XDECREF(radiusd_module); + Py_Finalize(); + Pyx_UNBLOCK_THREADS - char buf[BUF_SIZE]; /* same size as vp_print buffer */ + return 0; +} - VALUE_PAIR *vp; +/* This will need reconsidering in a future. Maybe we'll need to have our own + * reference counting for radiusd_module + */ - PyObject *pValue, *pValuePairContainer, **pValueHolder, **pValueHolderPtr; - int i, n_tuple, return_value; +#endif - /* Return with "OK, continue" if the function is not defined. */ - if (pFunc == NULL) { - return RLM_MODULE_OK; - } +/* TODO: Convert this function to accept any iterable objects? */ + +static void python_vptuple(VALUE_PAIR **vpp, PyObject *pValue, const char *funcname) { + int i; + int tuplesize; + VALUE_PAIR *vp; + + /* If the Python function gave us None for the tuple, then just return. */ + if (pValue == Py_None) + return; + + if (!PyTuple_CheckExact(pValue)) { + radlog(L_ERR, "rlm_python:%s: non-tuple passed", funcname); + return; + } + /* Get the tuple tuplesize. */ + 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; + } + /* Check if it's a pair */ + 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); + /* xxx Might need to support other T_OP */ + 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); + } + } +} - /* Default return value is "OK, continue" */ - return_value = RLM_MODULE_OK; - - /* We will pass a tuple containing (name, value) tuples - * We can safely use the Python function to build up a tuple, - * since the tuple is not used elsewhere. - * - * Determine the size of our tuple by walking through the packet. - * If request is NULL, pass None. - */ - n_tuple = 0; - - if (request != NULL) { - for (vp = request->packet->vps; vp; vp = vp->next) { - n_tuple++; - } - } - /* Create the tuple and a holder for the pointers, so that we can - * decref more efficiently later without the overhead of reading - * the tuple. - * - * We use malloc() instead of the Python memory allocator since we - * are not embedded. - */ +/* This is the core Python function that the others wrap around. + * Pass the value-pair print strings in a tuple. + * xxx We're not checking the errors. If we have errors, what do we do? + */ - if (NULL == (pValueHolder = pValueHolderPtr = - malloc(sizeof(PyObject *) * n_tuple))) { +static int python_populate_vptuple(PyObject *pPair, VALUE_PAIR *vp) { + PyObject* + pStr = NULL; - radlog(L_ERR, "%s: malloc of %d bytes failed\n", - function_name, sizeof(PyObject *) * n_tuple); + char buf[1024]; - return -1; - } + /* Look at the vp_print_name? */ - if (n_tuple == 0) { - pValuePairContainer = Py_None; - } - else { - pValuePairContainer = PyTuple_New(n_tuple); + if (vp->flags.has_tag) + pStr = PyString_FromFormat("%s:%d", vp->name, vp->flags.tag); + else + pStr = PyString_FromString(vp->name); - i = 0; - for (vp = request->packet->vps; vp; vp = vp->next) { - PyObject *pValuePair, *pString1, *pString2; + if (pStr == NULL) + goto failed; - /* The inside tuple has two only: */ - pValuePair = PyTuple_New(2); + PyTuple_SET_ITEM(pPair, 0, pStr); - /* The name. logic from vp_prints, lib/print.c */ - if (vp->flags.has_tag) { - snprintf(buf, BUF_SIZE, "%s:%d", vp->name, vp->flags.tag); - } - else { - strcpy(buf, vp->name); - } + vp_prints_value(buf, sizeof(buf), vp, 1); - pString1 = PyString_FromString(buf); - PyTuple_SetItem(pValuePair, 0, pString1); + if ((pStr = PyString_FromString(buf)) == NULL) + goto failed; + PyTuple_SET_ITEM(pPair, 1, pStr); - /* The value. Use delimiter - don't know what that means */ - vp_prints_value(buf, sizeof(buf), vp, 1); - pString2 = PyString_FromString(buf); - PyTuple_SetItem(pValuePair, 1, pString2); + return 0; - /* Put the tuple inside the container */ - PyTuple_SetItem(pValuePairContainer, i++, pValuePair); +failed: + return -1; +} - /* Store the pointer in our malloc() storage */ - *pValueHolderPtr++ = pValuePair; - } +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; + + /* Return with "OK, continue" if the function is not defined. */ + if (pFunc == NULL) + return RLM_MODULE_OK; + + /* Default return value is "OK, continue" */ + ret = RLM_MODULE_OK; + + /* We will pass a tuple containing (name, value) tuples + * We can safely use the Python function to build up a tuple, + * since the tuple is not used elsewhere. + * + * Determine the size of our tuple by walking through the packet. + * If request is NULL, pass None. + */ + 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; + + /* The inside tuple has two only: */ + if ((pPair = PyTuple_New(2)) == NULL) + goto failed; + + if (python_populate_vptuple(pPair, vp) == 0) { + /* Put the tuple inside the container */ + PyTuple_SET_ITEM(pArgs, i, pPair); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(pArgs, i, Py_None); + Py_DECREF(pPair); + } + } + } + + /* Call Python function. */ + pRet = PyObject_CallFunctionObjArgs(pFunc, pArgs, NULL); + + if (pRet == NULL) + goto failed; + + if (request == NULL) + goto okay; + /* The function returns either: + * 1. (returnvalue, replyTuple, configTuple), where + * - returnvalue is one of the constants RLM_* + * - replyTuple and configTuple are tuples of string tuples of size 2 + * + * 2. the function return value alone + * + * 3. None - default return value is set + * + * xxx This code is messy! + */ + 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; + } + /* Now have the return value */ + ret = PyInt_AsLong(pTupleInt); + /* Reply item tuple */ + python_vptuple(&request->reply->vps, PyTuple_GET_ITEM(pRet, 1), funcname); + /* Config item tuple */ + python_vptuple(&request->config_items, PyTuple_GET_ITEM(pRet, 2), funcname); + } else + if (PyInt_CheckExact(pRet)) { + /* Just an integer */ + ret = PyInt_AsLong(pRet); + } else + if (pRet == Py_None) { + /* returned 'None', return value defaults to "OK, continue." */ + ret = RLM_MODULE_OK; + } else { + /* Not tuple or None */ + radlog(L_ERR, "rlm_python:%s: function did not return a tuple or None", funcname); + goto failed; + } + + if (ret == RLM_MODULE_REJECT && request != NULL) + pairfree(&request->reply->vps); + +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; +} - /* Call Python function. - */ - - if (pFunc && PyCallable_Check(pFunc)) { - PyObject *pArgs; - - /* call the function with a singleton tuple containing the - * container tuple. - */ - - if ((pArgs = PyTuple_New(1)) == NULL) { - Py_DECREF(pValuePairContainer); - free(pValueHolder); - radlog(L_ERR, "%s: could not create tuple", function_name); - return -1; - } - if ((PyTuple_SetItem(pArgs, 0, pValuePairContainer)) != 0) { - Py_DECREF(pValuePairContainer); - free(pValueHolder); - Py_DECREF(pArgs); - radlog(L_ERR, "%s: could not set tuple item", function_name); - return -1; - } - - if ((pValue = PyObject_CallObject(pFunc, pArgs)) == NULL) { - Py_DECREF(pValuePairContainer); - free(pValueHolder); - Py_DECREF(pArgs); - radlog(L_ERR, "%s: function call failed", function_name); - python_error(); - return -1; - } +/* + * Import a user module and load a function from it + */ - /* The function returns either: - * 1. tuple containing the integer return value, - * then the integer reply code (or None to not set), - * then the string tuples to build the reply with. - * (returnvalue, (p1, s1), (p2, s2)) - * - * 2. the function return value alone - * - * 3. None - default return value is set - * - * xxx This code is messy! - */ - - if (PyTuple_Check(pValue)) { - PyObject *pTupleInt; - - if (PyTuple_Size(pValue) != 3) { - radlog(L_ERR, "%s: tuple must be " \ - "(return, replyTuple, configTuple)", - function_name); - - } - else { - pTupleInt = PyTuple_GetItem(pValue, 0); - - if ((pTupleInt == NULL) || !PyInt_Check(pTupleInt)) { - radlog(L_ERR, "%s: first tuple element not an integer", - function_name); - } - else { - /* Now have the return value */ - return_value = PyInt_AsLong(pTupleInt); - - /* Reply item tuple */ - add_vp_tuple(&request->reply->vps, - PyTuple_GetItem(pValue, 1), function_name); - - /* Config item tuple */ - add_vp_tuple(&request->config_items, - PyTuple_GetItem(pValue, 2), function_name); - } - } - } - else if (PyInt_Check(pValue)) { - /* Just an integer */ - return_value = PyInt_AsLong(pValue); - } - else if (pValue == Py_None) { - /* returned 'None', return value defaults to "OK, continue." */ - return_value = RLM_MODULE_OK; - } - else { - /* Not tuple or None */ - radlog(L_ERR, "%s function did not return a tuple or None\n", - function_name); - } +static int python_load_function(struct py_function_def * def) { + const char *funcname = "python_load_function"; + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); - /* Decrease reference counts for the argument and return tuple */ - Py_DECREF(pArgs); - Py_DECREF(pValue); + 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; } - /* Decrease reference count for the tuples passed, the - * container tuple, and the return value. - */ - - pValueHolderPtr = pValueHolder; - i = n_tuple; - while (i--) { - /* Can't write as pValueHolderPtr since Py_DECREF is a macro */ - Py_DECREF(*pValueHolderPtr); - pValueHolderPtr++; + 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; } - free(pValueHolder); - Py_DECREF(pValuePairContainer); - - /* pDict and pFunc are borrowed and must not be Py_DECREF-ed */ - /* Free pairs if we are rejecting. - * xxx Shouldn't the core do that? - */ - - if ((return_value == RLM_MODULE_REJECT) && (request != NULL)) { - pairfree(&(request->reply->vps)); + 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; +} - /* Return the specified by the Python module */ - return return_value; +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 struct varlookup { - const char* name; - int value; -} constants[] = { - { "L_DBG", L_DBG }, - { "L_AUTH", L_AUTH }, - { "L_INFO", L_INFO }, - { "L_ERR", L_ERR }, - { "L_PROXY", L_PROXY }, - { "L_CONS", L_CONS }, - { "RLM_MODULE_REJECT", RLM_MODULE_REJECT }, - { "RLM_MODULE_FAIL", RLM_MODULE_FAIL }, - { "RLM_MODULE_OK", RLM_MODULE_OK }, - { "RLM_MODULE_HANDLED", RLM_MODULE_HANDLED }, - { "RLM_MODULE_INVALID", RLM_MODULE_INVALID }, - { "RLM_MODULE_USERLOCK",RLM_MODULE_USERLOCK }, - { "RLM_MODULE_NOTFOUND",RLM_MODULE_NOTFOUND }, - { "RLM_MODULE_NOOP", RLM_MODULE_NOOP }, - { "RLM_MODULE_UPDATED", RLM_MODULE_UPDATED }, - { "RLM_MODULE_NUMCODES",RLM_MODULE_NUMCODES }, - { NULL, 0 }, -}; +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); +} -/* - * Import a user module and load a function from it - */ -static int load_python_function(const char* module, const char* func, - PyObject** pyModule, PyObject** pyFunc) { - - if ((module==NULL) || (func==NULL)) { - *pyFunc=NULL; - *pyModule=NULL; - } else { - PyObject *pName; - - pName = PyString_FromString(module); - Py_INCREF(pName); - *pyModule = PyImport_Import(pName); - Py_DECREF(pName); - if (*pyModule != NULL) { - PyObject *pDict; - - pDict = PyModule_GetDict(*pyModule); - /* pDict: borrowed reference */ - - *pyFunc = PyDict_GetItemString(pDict, func); - /* pFunc: Borrowed reference */ - } else { - python_error(); - - radlog(L_ERR, "Failed to import python module \"%s\"\n", module); - return -1; - } - } +static void python_instance_clear(struct rlm_python_t *data) { - return 0; -} +#define A(x) python_funcdef_clear(&data->x); + A(instantiate) + A(authorize) + A(authenticate) + A(preacct) + A(accounting) + A(checksimul) + A(detach) + +#undef A +} /* * Do any per-module initialization that is separate to each @@ -502,152 +528,80 @@ static int load_python_function(const char* module, const char* func, * in *instance otherwise put a null pointer there. * */ -static int python_instantiate(CONF_SECTION *conf, void **instance) -{ - rlm_python_t *data; - PyObject *module; - int idx; - - /* - * Initialize Python interpreter. Fatal error if this fails. - */ - Py_Initialize(); - - /* - * Set up a storage area for instance data - */ - data = rad_malloc(sizeof(*data)); - if (!data) { - return -1; - } - memset(data, 0, sizeof(*data)); - - /* - * If the configuration parameters can't be parsed, then - * fail. - */ - if (cf_section_parse(conf, data, module_config) < 0) { - free(data); - return -1; - } - - - /* - * Setup our 'radiusd' module. - */ - - /* Code */ - if ((module = data->pModule_builtin = - Py_InitModule3("radiusd", radiusd_methods, - "FreeRADIUS Module.")) == NULL) { - - radlog(L_ERR, "Python Py_InitModule3 failed"); - free(data); - return -1; - } - - /* - * Load constants into module - */ - for (idx=0; constants[idx].name; idx++) - if ((PyModule_AddIntConstant(module, constants[idx].name, constants[idx].value)) == -1) { - - radlog(L_ERR, "Python AddIntConstant failed"); - } +static int python_instantiate(CONF_SECTION *conf, void **instance) { + struct rlm_python_t *data = NULL; + + /* + * Set up a storage area for instance data + */ + if ((data = malloc(sizeof(*data))) == NULL) + return -1; + bzero(data, sizeof(*data)); + + + + if (python_init() != 0) { + free(data); + return -1; + } + + /* + * If the configuration parameters can't be parsed, then + * fail. + */ + 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(detach) + +#undef A + + *instance = data; + /* Call the instantiate function. No request. Use the return value. */ + + return python_function(NULL, data->instantiate.function, "instantiate"); +failed: + python_error(); + python_instance_clear(data); + free(data); + return -1; +} - /* - * Import user modules. - */ -#define RLM_PYTHON_LOAD(foo) if (load_python_function(data->mod_##foo, data->func_##foo, \ - &data->pModule_##foo, &data->pFunc_##foo)==-1) { \ - /* TODO: check if we need to cleanup data */ \ - return -1; \ - } +static int python_detach(void *instance) { + struct rlm_python_t *data = (struct rlm_python_t *) instance; + int ret; - RLM_PYTHON_LOAD(instantiate); - RLM_PYTHON_LOAD(authenticate); - RLM_PYTHON_LOAD(authorize); - RLM_PYTHON_LOAD(preacct); - RLM_PYTHON_LOAD(accounting); - RLM_PYTHON_LOAD(checksimul); - RLM_PYTHON_LOAD(preproxy); - RLM_PYTHON_LOAD(postproxy); - RLM_PYTHON_LOAD(detach); + ret = python_function(NULL, data->detach.function, "detach"); - *instance=data; + python_instance_clear(data); - /* Call the instantiate function. No request. Use the return value. */ - return python_function(NULL, data->pFunc_instantiate, "instantiate"); + free(data); + return ret; } -#define RLM_PYTHON_FUNC(foo) static int python_##foo(void *instance, REQUEST *request) \ -{ \ - return python_function(request, \ - ((struct rlm_python_t *)instance)->pFunc_##foo, \ - #foo); \ +#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) -RLM_PYTHON_FUNC(authorize) -RLM_PYTHON_FUNC(authenticate) -RLM_PYTHON_FUNC(preacct) -RLM_PYTHON_FUNC(accounting) -RLM_PYTHON_FUNC(checksimul) -RLM_PYTHON_FUNC(preproxy) -RLM_PYTHON_FUNC(postproxy) -RLM_PYTHON_FUNC(postauth) - -static int python_detach(void *instance) -{ - int return_value; - - /* Default return value is failure */ - return_value = -1; - - if (((rlm_python_t *)instance)->pFunc_detach && - PyCallable_Check(((rlm_python_t *)instance)->pFunc_detach)) { - - PyObject *pArgs, *pValue; - - /* call the function with an empty tuple */ - - pArgs = PyTuple_New(0); - pValue = PyObject_CallObject(((rlm_python_t *)instance)->pFunc_detach, - pArgs); - - if (pValue == NULL) { - python_error(); - return -1; - } - else { - if (!PyInt_Check(pValue)) { - radlog(L_ERR, "detach: return value not an integer"); - } - else { - return_value = PyInt_AsLong(pValue); - } - } - - /* Decrease reference counts for the argument and return tuple */ - Py_DECREF(pArgs); - Py_DECREF(pValue); - } - - free(instance); - -#if 0 - /* xxx test delete module object so it will be reloaded later. - * xxx useless since we can't SIGHUP reliably, anyway. - */ - PyObject_Del(((struct rlm_python_t *)instance)->pModule_accounting); -#endif - - radlog(L_DBG, "python_detach done"); - - /* Return the specified by the Python module */ - return return_value; -} +#undef A /* * The module name should be the only globally exported symbol. @@ -663,15 +617,15 @@ module_t rlm_python = { "python", RLM_TYPE_THREAD_SAFE, /* type */ python_instantiate, /* instantiation */ - python_detach, /* detach */ + python_detach, { python_authenticate, /* authentication */ python_authorize, /* authorization */ python_preacct, /* preaccounting */ python_accounting, /* accounting */ python_checksimul, /* checksimul */ - python_preproxy, /* pre-proxy */ - python_postproxy, /* post-proxy */ - python_postauth /* post-auth */ - }, + NULL, /* pre-proxy */ + NULL, /* post-proxy */ + NULL /* post-auth */ + } };