--- /dev/null
+#
+# Example Python module for prepaid usage using MySQL
+
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Copyright 2002 Miguel A.L. Paraz <mparaz@mparaz.com>
+# Copyright 2002 Imperium Technology, Inc.
+
+import radiusd
+import MySQLdb
+
+# Configuration
+configDb = 'python' # Database name
+configHost = 'localhost' # Database host
+configUser = 'python' # Database user and password
+configPasswd = 'python'
+
+# xxx Database
+
+# Globals
+dbHandle = None
+
+def log(level, s):
+ """Log function."""
+ radiusd.radlog(level, 'prepaid.py: ' + s)
+
+def instantiate(p):
+ """Module Instantiation. 0 for success, -1 for failure.
+ p is a dummy variable here."""
+ global dbHandle
+
+ p = p
+
+ try:
+ dbHandle = MySQLdb.connect(db=configDb, host=configHost,
+ user=configUser, passwd=configPasswd)
+
+ except MySQLdb.OperationalError, e:
+ # Report the error and return -1 for failure.
+ # xxx A more advanced module would retry the database.
+ log(radiusd.L_ERR, str(e))
+ return -1
+
+ log(radiusd.L_INFO, 'db connection: ' + str(dbHandle))
+
+ return 0
+
+
+def authorize(authData):
+ """Authorization and authentication are done in one step."""
+
+ # Extract the data we need.
+ userName = None
+ userPasswd = None
+
+ for t in authData:
+ if t[0] == 'User-Name':
+ userName = t[1]
+ elif t[0] == 'Password':
+ userPasswd = t[1]
+
+ # Build and log the SQL statement
+ # radiusd puts double quotes (") around the string representation of
+ # the RADIUS packet.
+ sql = 'select passwd, maxseconds from users where username = ' + userName
+
+ log(radiusd.L_DBG, sql)
+
+ # Get a cursor
+ # xxx Or should this be one cursor all throughout?
+ try:
+ dbCursor = dbHandle.cursor()
+ except MySQLdb.OperationalError, e:
+ log(radiusd.L_ERR, str(e))
+ return radiusd.RLM_MODULE_FAIL
+
+ # Execute the SQL statement
+ try:
+ dbCursor.execute(sql)
+ except MySQLdb.OperationalError, e:
+ log(radiusd.L_ERR, str(e))
+ dbCursor.close()
+ return radiusd.RLM_MODULE_FAIL
+
+ # Get the result. (passwd, maxseconds)
+ result = dbCursor.fetchone()
+ if not result:
+ # User not found
+ log(radiusd.L_INFO, 'user not found: ' + userName)
+ dbCursor.close()
+ return radiusd.RLM_MODULE_NOTFOUND
+
+
+
+ # Compare passwords
+ # Ignore the quotes around userPasswd.
+ if result[0] != userPasswd[1:-1]:
+ log(radiusd.L_DBG, 'user password mismatch: ' + userName)
+ return radiusd.RLM_MODULE_REJECT
+
+ maxSeconds = result[1]
+
+ # Compute their session limit
+
+ # Build and log the SQL statement
+ sql = 'select sum(seconds) from sessions where username = ' + userName
+
+ log(radiusd.L_DBG, sql)
+
+ # Execute the SQL statement
+ try:
+ dbCursor.execute(sql)
+ except MySQLdb.OperationalError, e:
+ log(radiusd.L_ERR, str(e))
+ dbCursor.close()
+ return radiusd.RLM_MODULE_FAIL
+
+ # Get the result. (sum,)
+ result = dbCursor.fetchone()
+ if (not result) or (not result[0]):
+ # No usage yet
+ secondsUsed = 0
+ else:
+ secondsUsed = result[0]
+
+ # Done with cursor
+ dbCursor.close()
+
+ # Note that MySQL returns the result of SUM() as a float.
+ sessionTimeout = maxSeconds - int(secondsUsed)
+
+ if sessionTimeout <= 0:
+ # No more time, reject outright
+ log(radiusd.L_INFO, 'user out of time: ' + userName)
+ return radiusd.RLM_MODULE_REJECT
+
+ # Log the success
+ log(radiusd.L_DBG, 'user accepted: %s, %d seconds' %
+ (userName, sessionTimeout))
+
+ # We are adding to the RADIUS packet
+ # Note that the session timeout integer must be converted to string.
+ # We need to set an Auth-Type.
+
+ return (radiusd.RLM_MODULE_UPDATED,
+ (('Session-Timeout', str(sessionTimeout)),),
+ (('Auth-Type', 'python'),))
+
+
+
+def authenticate(p):
+ p = p
+ return radiusd.RLM_MODULE_OK
+
+
+def preacct(p):
+ p = p
+ return radiusd.RLM_MODULE_OK
+
+
+def accounting(acctData):
+ """Accounting."""
+ # Extract the data we need.
+
+ userName = None
+ acctSessionTime = None
+ acctStatusType = None
+
+ # xxx A dict would make this nice.
+ for t in acctData:
+ if t[0] == 'User-Name':
+ userName = t[1]
+ elif t[0] == 'Acct-Session-Time':
+ acctSessionTime = t[1]
+ elif t[0] == 'Acct-Status-Type':
+ acctStatusType = t[1]
+
+
+ # We will not deal with Start for now.
+ # We may later, for simultaneous checks and the like.
+ if acctStatusType == 'Start':
+ return radiusd.RLM_MODULE_OK
+
+ # Build and log the SQL statement
+ # radiusd puts double quotes (") around the string representation of
+ # the RADIUS packet.
+ #
+ # xxx This is simplistic as it does not record the time, etc.
+ #
+ sql = 'insert into sessions (username, seconds) values (%s, %d)' % \
+ (userName, int(acctSessionTime))
+
+ log(radiusd.L_DBG, sql)
+
+ # Get a cursor
+ # xxx Or should this be one cursor all throughout?
+ try:
+ dbCursor = dbHandle.cursor()
+ except MySQLdb.OperationalError, e:
+ log(radiusd.L_ERR, str(e))
+ return radiusd.RLM_MODULE_FAIL
+
+ # Execute the SQL statement
+ try:
+ dbCursor.execute(sql)
+ except MySQLdb.OperationalError, e:
+ log(radiusd.L_ERR, str(e))
+ dbCursor.close()
+ return radiusd.RLM_MODULE_FAIL
+
+
+ return radiusd.RLM_MODULE_OK
+
+
+def detach():
+ """Detach and clean up."""
+ # Shut down the database connection.
+ global dbHandle
+ log(radiusd.L_DBG, 'closing database handle: ' + str(dbHandle))
+ dbHandle.close()
+
+ return radiusd.RLM_MODULE_OK
+
+
+
+# Test the modules
+if __name__ == '__main__':
+ instantiate(None)
+ print authorize((('User-Name', '"map"'), ('User-Password', '"abc"')))
*
* Copyright 2000 The FreeRADIUS server project
* Copyright 2002 Miguel A.L. Paraz <mparaz@mparaz.com>
+ * Copyright 2002 Imperium Technology, Inc.
*/
+#include <Python.h>
+
#include "autoconf.h"
#include "libradius.h"
#include "modules.h"
#include "conffile.h"
-#include <Python.h>
-
-#if 0
static const char rcsid[] = "$Id$";
-#endif
/*
* Define a structure for our module configuration.
*/
typedef struct rlm_python_t {
/* Config section */
- char *mod_authorize; /* Name of authorization module */
- char *func_authorize; /* Name of authorization function */
+
+ /* Names of modules */
+ char
+ *mod_instantiate,
+ *mod_authorize,
+ *mod_authenticate,
+ *mod_preacct,
+ *mod_accounting,
+ *mod_checksimul,
+ *mod_detach,
+
+ /* Names of functions */
+ *func_instantiate,
+ *func_authorize,
+ *func_authenticate,
+ *func_preacct,
+ *func_accounting,
+ *func_checksimul,
+ *func_detach;
+
+
/* End Config section */
- /* xxx To keep things simple, all functions should initially be
- * xxx in one module.
- */
- PyObject *pModule, *pFunc_authorize;
+ /* Python objects for modules */
+ PyObject
+ *pModule_builtin,
+ *pModule_instantiate,
+ *pModule_authorize,
+ *pModule_authenticate,
+ *pModule_preacct,
+ *pModule_accounting,
+ *pModule_checksimul,
+ *pModule_detach,
+
+
+ /* Functions */
+
+ *pFunc_instantiate,
+ *pFunc_authorize,
+ *pFunc_authenticate,
+ *pFunc_preacct,
+ *pFunc_accounting,
+ *pFunc_checksimul,
+ *pFunc_detach;
+
} rlm_python_t;
/*
* to the strdup'd string into 'config.string'. This gets around
* buffer over-flows.
*/
-
-#if 0
static CONF_PARSER module_config[] = {
- { "integer", PW_TYPE_INTEGER, offsetof(rlm_python_t,value), NULL, "1" },
- { "boolean", PW_TYPE_BOOLEAN, offsetof(rlm_python_t,boolean), NULL, "no"},
- { "string", PW_TYPE_STRING_PTR, offsetof(rlm_python_t,string), NULL, NULL},
- { "ipaddr", PW_TYPE_IPADDR, offsetof(rlm_python_t,ipaddr), NULL, "*" },
-
- { NULL, -1, 0, NULL, NULL } /* end the list */
-};
-
-#else
+ { "mod_instantiate", PW_TYPE_STRING_PTR,
+ offsetof(rlm_python_t, mod_instantiate), NULL, NULL},
+ { "func_instantiate", PW_TYPE_STRING_PTR,
+ offsetof(rlm_python_t, func_instantiate), NULL, NULL},
-static CONF_PARSER module_config[] = {
{ "mod_authorize", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t,mod_authorize), NULL, NULL},
+ offsetof(rlm_python_t, mod_authorize), NULL, NULL},
{ "func_authorize", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t,func_authorize), NULL, NULL},
+ offsetof(rlm_python_t, func_authorize), NULL, NULL},
+
+ { "mod_authenticate", PW_TYPE_STRING_PTR,
+ offsetof(rlm_python_t, mod_authenticate), NULL, NULL},
+ { "func_authenticate", PW_TYPE_STRING_PTR,
+ offsetof(rlm_python_t, func_authenticate), NULL, NULL},
+
+ { "mod_preacct", PW_TYPE_STRING_PTR,
+ offsetof(rlm_python_t, mod_preacct), NULL, NULL},
+ { "func_preacct", PW_TYPE_STRING_PTR,
+ offsetof(rlm_python_t, func_preacct), NULL, NULL},
+
+ { "mod_accounting", PW_TYPE_STRING_PTR,
+ offsetof(rlm_python_t, mod_accounting), NULL, NULL},
+ { "func_accounting", PW_TYPE_STRING_PTR,
+ offsetof(rlm_python_t, func_accounting), NULL, NULL},
+
+ { "mod_checksimul", PW_TYPE_STRING_PTR,
+ offsetof(rlm_python_t, mod_checksimul), NULL, NULL},
+ { "func_checksimul", PW_TYPE_STRING_PTR,
+ offsetof(rlm_python_t, func_checksimul), NULL, NULL},
+
+ { "mod_detach", PW_TYPE_STRING_PTR,
+ offsetof(rlm_python_t, mod_detach), NULL, NULL},
+ { "func_detach", PW_TYPE_STRING_PTR,
+ offsetof(rlm_python_t, func_detach), NULL, NULL},
+
{ NULL, -1, 0, NULL, NULL } /* end the list */
};
-#endif
+/*
+ * radiusd Python functions
+ */
+
+/* radlog wrapper */
+static PyObject *radlog_py(const PyObject *self, PyObject *args) {
+ int status;
+ char *msg;
+
+ if (!PyArg_ParseTuple(args, "is", &status, &msg)) {
+ return NULL;
+ }
+
+ radlog(status, msg);
+ return Py_None;
+}
+
+static PyMethodDef radiusd_methods[] = {
+ {"radlog", (PyCFunction)radlog_py, METH_VARARGS, "freeradius radlog()."},
+ {NULL, NULL, 0, NULL}
+};
/*
* Do any per-module initialization. e.g. set up connections
static int python_init(void)
{
/*
- * Initialize Python interpreter
+ * Initialize Python interpreter. Fatal error if this fails.
*/
Py_Initialize();
+ radlog(L_DBG, "python_init done");
+
return 0;
}
-/*
- * 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.
- */
-static int python_instantiate(CONF_SECTION *conf, void **instance)
-{
- rlm_python_t *data;
- PyObject *pName;
-
- /*
- * Set up a storage area for instance data
- */
- data = rad_malloc(sizeof(*data));
+/* Extract string representation of Python error. */
+static void python_error(void) {
+ PyObject *pType, *pValue, *pTraceback, *pStr1, *pStr2;
- /*
- * If the configuration parameters can't be parsed, then
- * fail.
- */
- if (cf_section_parse(conf, data, module_config) < 0) {
- free(data);
- return -1;
- }
-
- *instance = data;
+ PyErr_Fetch(&pType, &pValue, &pTraceback);
+ pStr1 = PyObject_Str(pType);
+ pStr2 = PyObject_Str(pValue);
+
+ radlog(L_ERR, "%s: %s\n",
+ PyString_AsString(pStr1), PyString_AsString(pStr2));
+}
- pName = PyString_FromString(data->mod_authorize);
+/* 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;
- /* Import module */
- data->pModule = PyImport_Import(pName);
- if (data->pModule != NULL) {
- PyObject *pDict;
+ /* If the Python function gave us None for the tuple, then just return. */
+ if (pValue == Py_None) {
+ return;
+ }
+
+ if (!PyTuple_Check(pValue)) {
+ radlog(L_ERR, "%s: non-tuple passed", function_name);
+ }
- pDict = PyModule_GetDict(data->pModule);
- /* pDict: borrowed reference */
+ /* 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))) {
- data->pFunc_authorize =
- PyDict_GetItemString(pDict, data->func_authorize);
- /* pFunc: Borrowed reference */
+ /* 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 {
- /* xxx Change this to dump error message to log xxx */
- PyErr_Print();
-
- radlog(L_ERR, "Failed to load \"%s\"\n", data->mod_authorize);
- return -1;
+ radlog(L_ERR, "%s: tuple element %d is not a tuple\n",
+ function_name, i);
}
+ }
- /* xxx Should we check if function is callable now?
- * xxx or later when it is used, since it can change...
- */
-
-
- return 0;
}
-/* Pass the value-pair print strings in a tuple.
- xxx We're not checking the errors. If we have errors, what do we do?
-*/
-static int python_authorize(void *instance, REQUEST *request)
+/* 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?
+ */
+
+static int python_function(REQUEST *request,
+ PyObject *pFunc, const char *function_name)
{
+#define BUF_SIZE 1024
+
+ char buf[BUF_SIZE]; /* same size as vp_print buffer */
+
VALUE_PAIR *vp;
- char buf[1024]; /* same size as vp_print buffer */
+
PyObject *pValue, *pValuePairContainer, **pValueHolder, **pValueHolderPtr;
int i, n_tuple, return_value;
-#define inst ((struct rlm_python_t *)instance)
-
- /* Default return value is failure */
- return_value = -1;
+ /* Return with "OK, continue" if the function is not defined. */
+ if (pFunc == NULL) {
+ return RLM_MODULE_OK;
+ }
+ /* 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;
- for (vp = request->packet->vps; vp; vp = vp->next) {
- n_tuple++;
+ 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.
*/
- pValuePairContainer = PyTuple_New(n_tuple);
+
if (NULL == (pValueHolder = pValueHolderPtr =
malloc(sizeof(PyObject *) * n_tuple))) {
-
- radlog(L_ERR, "malloc of %d bytes failed\n",
- sizeof(PyObject *) * n_tuple);
-
+
+ radlog(L_ERR, "%s: malloc of %d bytes failed\n",
+ function_name, sizeof(PyObject *) * n_tuple);
+
return -1;
}
+
+ if (n_tuple == 0) {
+ pValuePairContainer = Py_None;
+ }
+ else {
+ pValuePairContainer = PyTuple_New(n_tuple);
- i = 0;
- for (vp = request->packet->vps; vp; vp = vp->next) {
- PyObject *pValuePair, *pString1, *pString2;
+ i = 0;
+ for (vp = request->packet->vps; vp; vp = vp->next) {
+ PyObject *pValuePair, *pString1, *pString2;
- /* The inside tuple has two only: */
- pValuePair = PyTuple_New(2);
+ /* The inside tuple has two only: */
+ pValuePair = PyTuple_New(2);
- /* The name. logic from vp_prints, lib/print.c */
- if (vp->flags.has_tag) {
- sprintf(buf, "%s:%d", vp->name, vp->flags.tag);
- }
- else {
- strcpy(buf, vp->name);
- }
+ /* 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);
+ }
- pString1 = PyString_FromString(buf);
- PyTuple_SetItem(pValuePair, 0, pString1);
+ pString1 = PyString_FromString(buf);
+ PyTuple_SetItem(pValuePair, 0, pString1);
- /* 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);
+ /* 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);
- /* Put the tuple inside the container */
- PyTuple_SetItem(pValuePairContainer, i++, pValuePair);
+ /* Put the tuple inside the container */
+ PyTuple_SetItem(pValuePairContainer, i++, pValuePair);
- /* Store the pointer in our malloc() storage */
- *pValueHolderPtr++ = pValuePair;
+ /* Store the pointer in our malloc() storage */
+ *pValueHolderPtr++ = pValuePair;
+ }
}
+
/* Call Python function.
- * xxx need to make visible wrappers for functions such as radlog
*/
- if (inst->pFunc_authorize && PyCallable_Check(inst->pFunc_authorize)) {
+ if (pFunc && PyCallable_Check(pFunc)) {
PyObject *pArgs;
- /* xxx this should have error checking xxx */
-
/* call the function with a singleton tuple containing the
- * value-pair container tuple.
+ * container tuple.
*/
- pArgs = PyTuple_New(1);
- PyTuple_SetItem(pArgs, 0, pValuePairContainer);
-
- pValue = PyObject_CallObject(inst->pFunc_authorize, pArgs);
+
+ if ((pArgs = PyTuple_New(1)) == NULL) {
+ radlog(L_ERR, "%s: could not create tuple", function_name);
+ return -1;
+ }
+ if ((PyTuple_SetItem(pArgs, 0, pValuePairContainer)) != 0) {
+ radlog(L_ERR, "%s: could not set tuple item", function_name);
+ return -1;
+ }
- if (pValue == NULL) {
- PyErr_Print();
+ if ((pValue = PyObject_CallObject(pFunc, pArgs)) == NULL) {
+ radlog(L_ERR, "%s: function call failed", function_name);
+ python_error();
return -1;
}
+
+ /* 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!
+ */
- /* Returns a tuple for the function return value,
- * then the strings to build the reply with. */
if (PyTuple_Check(pValue)) {
PyObject *pTupleInt;
- int n;
- n = PyTuple_Size(pValue);
+ if (PyTuple_Size(pValue) != 3) {
+ radlog(L_ERR, "%s: tuple must be " \
+ "(return, replyTuple, configTuple)",
+ function_name);
-
- if (n == 0) {
- radlog(L_ERR, "tuple must have at least one element");
- }
- else if (pTupleInt = PyTuple_GetItem(pValue, 0),
- !PyInt_Check(pTupleInt)) {
- radlog(L_ERR, "first tuple element not an integer");
}
else {
- return_value = PyInt_AsLong(pTupleInt);
-
-
-
- for (i = 1; i < n; i++) {
- PyObject *pTupleElement = PyTuple_GetItem(pValue, i);
+ 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);
- if (PyTuple_Check(pTupleElement)) {
- /* Check if it's a pair */
- int m;
-
- if ((m = PyTuple_Size(pTupleElement)) != 2) {
- radlog(L_ERR, "tuple element %d is a tuple "
- " of size %d. must be 2\n", i, m);
- }
- else {
- PyObject *pString1, *pString2;
-
- pString1 = PyTuple_GetItem(pTupleElement, 0);
- pString2 = PyTuple_GetItem(pTupleElement, 1);
+ /* Reply item tuple */
+ add_vp_tuple(&request->reply->vps,
+ PyTuple_GetItem(pValue, 1), function_name);
- if (PyString_Check(pString1) &&
- PyString_Check(pString2)) {
- char *s1, *s2;
-
- /* pairmake() will convert and find any
- * errors in the pair.
- */
-
- s1 = PyString_AsString(pString1);
- s2 = PyString_AsString(pString2);
-
- radlog(L_DBG, "python: %s = %s ", s1, s2);
-
- vp = pairmake(s1, s2, T_OP_EQ);
- if (vp != NULL) {
- pairadd(&request->packet->vps, vp);
- radlog(L_DBG, "OK\n");
- }
- else {
- radlog(L_DBG, "FAILED\n");
- }
-
- }
- else {
- radlog(L_ERR, "tuple element %d must be "
- "(string, string)", i);
- }
- }
- }
- else {
- radlog(L_ERR, "tuple element %d is not a tuple\n", i);
- }
+ /* 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 a tuple */
- radlog(L_ERR, "authorize function did not return a tuple\n");
+ /* Not tuple or None */
+ radlog(L_ERR, "%s function did not return a tuple or None\n",
+ function_name);
}
/* 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));
+ }
/* Return the specified by the Python module */
return return_value;
}
+/*
+ * 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.
+ *
+ */
+static int python_instantiate(CONF_SECTION *conf, void **instance)
+{
+ rlm_python_t *data;
+ PyObject *pName, *module;
+
+ /*
+ * Set up a storage area for instance data
+ */
+ data = rad_malloc(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;
+ }
+
+ *instance = data;
+
+
+ /*
+ * 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");
+ }
+
+ /*
+ * Constants
+ * xxx Find a way to automatically update this when C source changes.
+ */
+ if ((PyModule_AddIntConstant(module, "L_DBG", L_DBG) == -1) ||
+ (PyModule_AddIntConstant(module, "L_AUTH", L_AUTH) == -1) ||
+ (PyModule_AddIntConstant(module, "L_INFO", L_INFO) == -1) ||
+ (PyModule_AddIntConstant(module, "L_ERR", L_ERR) == -1) ||
+ (PyModule_AddIntConstant(module, "L_PROXY", L_PROXY) == -1) ||
+ (PyModule_AddIntConstant(module, "L_CONS", L_CONS) == -1) ||
+ (PyModule_AddIntConstant(module, "RLM_MODULE_REJECT",
+ RLM_MODULE_REJECT) == -1) ||
+ (PyModule_AddIntConstant(module, "RLM_MODULE_FAIL",
+ RLM_MODULE_FAIL) == -1) ||
+ (PyModule_AddIntConstant(module, "RLM_MODULE_OK",
+ RLM_MODULE_OK) == -1) ||
+ (PyModule_AddIntConstant(module, "RLM_MODULE_HANDLED",
+ RLM_MODULE_HANDLED) == -1) ||
+ (PyModule_AddIntConstant(module, "RLM_MODULE_INVALID",
+ RLM_MODULE_INVALID) == -1) ||
+ (PyModule_AddIntConstant(module, "RLM_MODULE_USERLOCK",
+ RLM_MODULE_USERLOCK) == -1) ||
+ (PyModule_AddIntConstant(module, "RLM_MODULE_NOTFOUND",
+ RLM_MODULE_NOTFOUND) == -1) ||
+ (PyModule_AddIntConstant(module, "RLM_MODULE_NOOP",
+ RLM_MODULE_NOOP) == -1) ||
+ (PyModule_AddIntConstant(module, "RLM_MODULE_UPDATED",
+ RLM_MODULE_UPDATED) == -1) ||
+ (PyModule_AddIntConstant(module, "RLM_MODULE_NUMCODES",
+ RLM_MODULE_NUMCODES) == -1)) {
+
+ radlog(L_ERR, "Python AddIntConstant failed");
+ return -1;
+ }
+
+
+ /*
+ * Import user modules.
+ * xxx There should be a more elegant way of writing this.
+ * xxx Loop through a pointer array for the objects and name strings?
+ */
+
+ if ((data->mod_instantiate == NULL) || (data->func_instantiate == NULL)) {
+ data->pFunc_instantiate = NULL;
+ }
+ else {
+ pName = PyString_FromString(data->mod_instantiate);
+ data->pModule_instantiate = PyImport_Import(pName);
+ if (data->pModule_instantiate != NULL) {
+ PyObject *pDict;
+
+ pDict = PyModule_GetDict(data->pModule_instantiate);
+ /* pDict: borrowed reference */
+
+ data->pFunc_instantiate =
+ PyDict_GetItemString(pDict, data->func_instantiate);
+ /* pFunc: Borrowed reference */
+ }
+
+ else {
+ python_error();
+
+ /* No need to decrement references because we are exiting. */
+
+ radlog(L_ERR, "Failed to import python module \"%s\"\n",
+ data->mod_instantiate);
+ return -1;
+ }
+ }
+
+ if ((data->mod_authenticate == NULL) || (data->func_authenticate == NULL)) {
+ data->pFunc_authenticate = NULL;
+ }
+ else {
+ pName = PyString_FromString(data->mod_authenticate);
+
+ /* Import modules */
+ data->pModule_authenticate = PyImport_Import(pName);
+ if (data->pModule_authenticate != NULL) {
+ PyObject *pDict;
+
+ pDict = PyModule_GetDict(data->pModule_authenticate);
+ /* pDict: borrowed reference */
+
+ data->pFunc_authenticate =
+ PyDict_GetItemString(pDict, data->func_authenticate);
+ /* pFunc: Borrowed reference */
+ }
+ else {
+ python_error();
+
+ /* No need to decrement references because we are exiting. */
+
+ radlog(L_ERR, "Failed to import Python module \"%s\"\n",
+ data->mod_authenticate);
+ return -1;
+ }
+ }
+
+
+ if ((data->mod_authorize == NULL) || (data->func_authorize == NULL)) {
+ data->pFunc_authorize = NULL;
+ }
+ else {
+ pName = PyString_FromString(data->mod_authorize);
+ data->pModule_authorize = PyImport_Import(pName);
+ if (data->pModule_authorize != NULL) {
+ PyObject *pDict;
+
+ pDict = PyModule_GetDict(data->pModule_authorize);
+ /* pDict: borrowed reference */
+
+ data->pFunc_authorize =
+ PyDict_GetItemString(pDict, data->func_authorize);
+ /* pFunc: Borrowed reference */
+ }
+
+ else {
+ python_error();
+
+ /* No need to decrement references because we are exiting. */
+
+ radlog(L_ERR, "Failed to import python module \"%s\"\n",
+ data->mod_authorize);
+ return -1;
+ }
+ }
+
+ if ((data->mod_authenticate == NULL) || (data->func_authenticate == NULL)) {
+ data->pFunc_authenticate = NULL;
+ }
+ else {
+ pName = PyString_FromString(data->mod_authenticate);
+
+ /* Import modules */
+ data->pModule_authenticate = PyImport_Import(pName);
+ if (data->pModule_authenticate != NULL) {
+ PyObject *pDict;
+
+ pDict = PyModule_GetDict(data->pModule_authenticate);
+ /* pDict: borrowed reference */
+
+ data->pFunc_authenticate =
+ PyDict_GetItemString(pDict, data->func_authenticate);
+ /* pFunc: Borrowed reference */
+ }
+ else {
+ python_error();
+
+ /* No need to decrement references because we are exiting. */
+
+ radlog(L_ERR, "Failed to import Python module \"%s\"\n",
+ data->mod_authenticate);
+ return -1;
+ }
+ }
+
+ if ((data->mod_preacct == NULL) || (data->func_preacct == NULL)) {
+ data->pFunc_preacct = NULL;
+ }
+ else {
+ pName = PyString_FromString(data->mod_preacct);
+
+ /* Import modules */
+ data->pModule_preacct = PyImport_Import(pName);
+ if (data->pModule_preacct != NULL) {
+ PyObject *pDict;
+
+ pDict = PyModule_GetDict(data->pModule_preacct);
+ /* pDict: borrowed reference */
+
+ data->pFunc_preacct =
+ PyDict_GetItemString(pDict, data->func_preacct);
+ /* pFunc: Borrowed reference */
+ }
+ else {
+ python_error();
+
+ /* No need to decrement references because we are exiting. */
+
+ radlog(L_ERR, "Failed to import Python module \"%s\"\n",
+ data->mod_preacct);
+ return -1;
+ }
+ }
+
+
+ if ((data->mod_accounting == NULL) || (data->func_accounting == NULL)){
+ data->pFunc_accounting = NULL;
+ }
+ else {
+ pName = PyString_FromString(data->mod_accounting);
+
+ /* Import modules */
+ data->pModule_accounting = PyImport_Import(pName);
+ if (data->pModule_accounting != NULL) {
+ PyObject *pDict;
+
+ pDict = PyModule_GetDict(data->pModule_accounting);
+ /* pDict: borrowed reference */
+
+ data->pFunc_accounting =
+ PyDict_GetItemString(pDict, data->func_accounting);
+ /* pFunc: Borrowed reference */
+ }
+ else {
+ python_error();
+
+ /* No need to decrement references because we are exiting. */
+
+ radlog(L_ERR, "Failed to import Python module \"%s\"\n",
+ data->mod_accounting);
+ return -1;
+ }
+ }
+
+ if ((data->mod_checksimul == NULL) || (data->func_checksimul == NULL)){
+ data->pFunc_checksimul = NULL;
+ }
+ else {
+ pName = PyString_FromString(data->mod_checksimul);
+
+ /* Import modules */
+ data->pModule_checksimul = PyImport_Import(pName);
+ if (data->pModule_checksimul != NULL) {
+ PyObject *pDict;
+
+ pDict = PyModule_GetDict(data->pModule_checksimul);
+ /* pDict: borrowed reference */
+
+ data->pFunc_checksimul =
+ PyDict_GetItemString(pDict, data->func_checksimul);
+ /* pFunc: Borrowed reference */
+ }
+ else {
+ python_error();
+
+ /* No need to decrement references because we are exiting. */
+
+ radlog(L_ERR, "Failed to import Python module \"%s\"\n",
+ data->mod_checksimul);
+ return -1;
+ }
+ }
+
+
+ if ((data->mod_detach == NULL) || (data->func_detach == NULL)) {
+ data->func_detach = NULL;
+ }
+ else {
+ pName = PyString_FromString(data->mod_detach);
+
+ /* Import modules */
+ data->pModule_detach = PyImport_Import(pName);
+ if (data->pModule_detach != NULL) {
+ PyObject *pDict;
+
+ pDict = PyModule_GetDict(data->pModule_detach);
+ /* pDict: borrowed reference */
+
+ data->pFunc_detach =
+ PyDict_GetItemString(pDict, data->func_detach);
+ /* pFunc: Borrowed reference */
+ }
+ else {
+ python_error();
+
+ /* No need to decrement references because we are exiting. */
+
+ radlog(L_ERR, "Failed to import Python module \"%s\"\n",
+ data->mod_detach);
+ return -1;
+ }
+ }
+
+
+ /* Call the instantiate function. No request. Use the return value. */
+ return python_function(NULL, data->pFunc_instantiate, "instantiate");
+}
+
+/* Wrapper functions */
+static int python_authorize(void *instance, REQUEST *request)
+{
+ return python_function(request,
+ ((struct rlm_python_t *)instance)->pFunc_authorize,
+ "authorize");
+}
+
+static int python_authenticate(void *instance, REQUEST *request)
+{
+ return python_function(
+ request,
+ ((struct rlm_python_t *)instance)->pFunc_authenticate,
+ "authenticate");
+}
+
+static int python_preacct(void *instance, REQUEST *request)
+{
+ return python_function(
+ request,
+ ((struct rlm_python_t *)instance)->pFunc_preacct,
+ "preacct");
+}
+
+static int python_accounting(void *instance, REQUEST *request)
+{
+ return python_function(
+ request,
+ ((struct rlm_python_t *)instance)->pFunc_accounting,
+ "accounting");
+}
+
+static int python_checksimul(void *instance, REQUEST *request)
+{
+ return python_function(
+ request,
+ ((struct rlm_python_t *)instance)->pFunc_checksimul,
+ "checksimul");
+}
+
static int python_detach(void *instance)
{
- free(instance);
- return 0;
+ 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;
}
/*
python_init, /* initialization */
python_instantiate, /* instantiation */
{
-#if 0
python_authenticate, /* authentication */
-#else
- NULL,
-#endif
python_authorize, /* authorization */
-#if 0
-
- python_preacct, /* preaccounting */
+ python_preacct, /* preaccounting */
python_accounting, /* accounting */
python_checksimul /* checksimul */
-#else
- NULL,
- NULL,
- NULL,
-#endif
},
python_detach, /* detach */
NULL, /* destroy */