Updated the spec file for moonshot-targeted-ids
[freeradius.git] / src / modules / rlm_python / rlm_python.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or (at
5  *   your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16
17 /**
18  * $Id$
19  * @file rlm_python.c
20  * @brief Translates requests between the server an a python interpreter.
21  *
22  * @note Rewritten by Paul P. Komkoff Jr <i@stingr.net>.
23  *
24  * @copyright 2000,2006,2015-2016  The FreeRADIUS server project
25  * @copyright 2002  Miguel A.L. Paraz <mparaz@mparaz.com>
26  * @copyright 2002  Imperium Technology, Inc.
27  */
28 RCSID("$Id$")
29
30 #define LOG_PREFIX "rlm_python - "
31
32 #include "config.h"
33 #include <freeradius-devel/radiusd.h>
34 #include <freeradius-devel/modules.h>
35 #include <freeradius-devel/rad_assert.h>
36
37 #include <Python.h>
38 #include <dlfcn.h>
39 #ifdef HAVE_DL_ITERATE_PHDR
40 #include <link.h>
41 #endif
42
43 #define LIBPYTHON_LINKER_NAME \
44         "libpython" STRINGIFY(PY_MAJOR_VERSION) "." STRINGIFY(PY_MINOR_VERSION) ".so"
45
46 static uint32_t         python_instances = 0;
47 static void             *python_dlhandle;
48
49 static PyThreadState    *main_interpreter;      //!< Main interpreter (cext safe)
50 static PyObject         *main_module;           //!< Pthon configuration dictionary.
51
52 /** Specifies the module.function to load for processing a section
53  *
54  */
55 typedef struct python_func_def {
56         PyObject        *module;                //!< Python reference to module.
57         PyObject        *function;              //!< Python reference to function in module.
58
59         char const      *module_name;           //!< String name of module.
60         char const      *function_name;         //!< String name of function in module.
61 } python_func_def_t;
62
63 /** An instance of the rlm_python module
64  *
65  */
66 typedef struct rlm_python_t {
67         char const      *name;                  //!< Name of the module instance
68         PyThreadState   *sub_interpreter;       //!< The main interpreter/thread used for this instance.
69         char const      *python_path;           //!< Path to search for python files in.
70         PyObject        *module;                //!< Local, interpreter specific module, containing
71                                                 //!< FreeRADIUS functions.
72         bool            cext_compat;            //!< Whether or not to create sub-interpreters per module
73                                                 //!< instance.
74
75         python_func_def_t
76         instantiate,
77         authorize,
78         authenticate,
79         preacct,
80         accounting,
81         checksimul,
82         pre_proxy,
83         post_proxy,
84         post_auth,
85 #ifdef WITH_COA
86         recv_coa,
87         send_coa,
88 #endif
89         detach;
90
91         PyObject        *pythonconf_dict;       //!< Configuration parameters defined in the module
92                                                 //!< made available to the python script.
93 } rlm_python_t;
94
95 /** Tracks a python module inst/thread state pair
96  *
97  * Multiple instances of python create multiple interpreters and each
98  * thread must have a PyThreadState per interpreter, to track execution.
99  */
100 typedef struct python_thread_state {
101         PyThreadState           *state;         //!< Module instance/thread specific state.
102         rlm_python_t            *inst;          //!< Module instance that created this thread state.
103 } python_thread_state_t;
104
105 /*
106  *      A mapping of configuration file names to internal variables.
107  */
108 static CONF_PARSER module_config[] = {
109
110 #define A(x) { "mod_" #x, FR_CONF_OFFSET(PW_TYPE_STRING, rlm_python_t, x.module_name), "${.module}" }, \
111         { "func_" #x, FR_CONF_OFFSET(PW_TYPE_STRING, rlm_python_t, x.function_name), NULL },
112
113         A(instantiate)
114         A(authorize)
115         A(authenticate)
116         A(preacct)
117         A(accounting)
118         A(checksimul)
119         A(pre_proxy)
120         A(post_proxy)
121         A(post_auth)
122 #ifdef WITH_COA
123         A(recv_coa)
124         A(send_coa)
125 #endif
126         A(detach)
127
128 #undef A
129
130         { "python_path", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_python_t, python_path), NULL },
131         { "cext_compat", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_python_t, cext_compat), "yes" },
132
133         CONF_PARSER_TERMINATOR
134 };
135
136 static struct {
137         char const *name;
138         int  value;
139 } radiusd_constants[] = {
140
141 #define A(x) { #x, x },
142
143         A(L_DBG)
144         A(L_WARN)
145         A(L_AUTH)
146         A(L_INFO)
147         A(L_ERR)
148         A(L_PROXY)
149         A(L_WARN)
150         A(L_ACCT)
151         A(L_DBG_WARN)
152         A(L_DBG_ERR)
153         A(L_DBG_WARN_REQ)
154         A(L_DBG_ERR_REQ)
155         A(RLM_MODULE_REJECT)
156         A(RLM_MODULE_FAIL)
157         A(RLM_MODULE_OK)
158         A(RLM_MODULE_HANDLED)
159         A(RLM_MODULE_INVALID)
160         A(RLM_MODULE_USERLOCK)
161         A(RLM_MODULE_NOTFOUND)
162         A(RLM_MODULE_NOOP)
163         A(RLM_MODULE_UPDATED)
164         A(RLM_MODULE_NUMCODES)
165
166 #undef A
167
168         { NULL, 0 },
169 };
170
171 /*
172  *      This allows us to initialise PyThreadState on a per thread basis
173  */
174 fr_thread_local_setup(rbtree_t *, local_thread_state)   /* macro */
175
176 /*
177  *      radiusd Python functions
178  */
179
180 /** Allow radlog to be called from python
181  *
182  */
183 static PyObject *mod_radlog(UNUSED PyObject *module, PyObject *args)
184 {
185         int status;
186         char *msg;
187
188         if (!PyArg_ParseTuple(args, "is", &status, &msg)) {
189                 return NULL;
190         }
191
192         radlog(status, "%s", msg);
193         Py_INCREF(Py_None);
194
195         return Py_None;
196 }
197
198 static PyMethodDef module_methods[] = {
199         { "radlog", &mod_radlog, METH_VARARGS,
200           "radiusd.radlog(level, msg)\n\n" \
201           "Print a message using radiusd logging system. level should be one of the\n" \
202           "constants L_DBG, L_AUTH, L_INFO, L_ERR, L_PROXY\n"
203         },
204         { NULL, NULL, 0, NULL },
205 };
206
207 /** Print out the current error
208  *
209  * Must be called with a valid thread state set
210  */
211 static void python_error_log(void)
212 {
213         PyObject *pType = NULL, *pValue = NULL, *pTraceback = NULL, *pStr1 = NULL, *pStr2 = NULL;
214
215         PyErr_Fetch(&pType, &pValue, &pTraceback);
216         if (!pType || !pValue)
217                 goto failed;
218         if (((pStr1 = PyObject_Str(pType)) == NULL) ||
219             ((pStr2 = PyObject_Str(pValue)) == NULL))
220                 goto failed;
221
222         ERROR("%s (%s)", PyString_AsString(pStr1), PyString_AsString(pStr2));
223
224 failed:
225         Py_XDECREF(pStr1);
226         Py_XDECREF(pStr2);
227         Py_XDECREF(pType);
228         Py_XDECREF(pValue);
229         Py_XDECREF(pTraceback);
230 }
231
232 static void mod_vptuple(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, PyObject *pValue,
233                         char const *funcname, char const *list_name)
234 {
235         int          i;
236         int          tuplesize;
237         vp_tmpl_t       dst;
238         VALUE_PAIR      *vp;
239         REQUEST         *current = request;
240
241         memset(&dst, 0, sizeof(dst));
242
243         /*
244          *      If the Python function gave us None for the tuple,
245          *      then just return.
246          */
247         if (pValue == Py_None) return;
248
249         if (!PyTuple_CheckExact(pValue)) {
250                 ERROR("%s - non-tuple passed to %s", funcname, list_name);
251                 return;
252         }
253         /* Get the tuple tuplesize. */
254         tuplesize = PyTuple_GET_SIZE(pValue);
255         for (i = 0; i < tuplesize; i++) {
256                 PyObject        *pTupleElement = PyTuple_GET_ITEM(pValue, i);
257                 PyObject        *pStr1;
258                 PyObject        *pStr2;
259                 PyObject        *pOp;
260                 int             pairsize;
261                 char const      *s1;
262                 char const      *s2;
263                 FR_TOKEN        op = T_OP_EQ;
264
265                 if (!PyTuple_CheckExact(pTupleElement)) {
266                         ERROR("%s - Tuple element %d of %s is not a tuple", funcname, i, list_name);
267                         continue;
268                 }
269                 /* Check if it's a pair */
270
271                 pairsize = PyTuple_GET_SIZE(pTupleElement);
272                 if ((pairsize < 2) || (pairsize > 3)) {
273                         ERROR("%s - Tuple element %d of %s is a tuple of size %d. Must be 2 or 3",
274                               funcname, i, list_name, pairsize);
275                         continue;
276                 }
277
278                 pStr1 = PyTuple_GET_ITEM(pTupleElement, 0);
279                 pStr2 = PyTuple_GET_ITEM(pTupleElement, pairsize-1);
280
281                 if ((!PyString_CheckExact(pStr1)) || (!PyString_CheckExact(pStr2))) {
282                         ERROR("%s - Tuple element %d of %s must be as (str, str)",
283                               funcname, i, list_name);
284                         continue;
285                 }
286                 s1 = PyString_AsString(pStr1);
287                 s2 = PyString_AsString(pStr2);
288
289                 if (pairsize == 3) {
290                         pOp = PyTuple_GET_ITEM(pTupleElement, 1);
291                         if (PyString_CheckExact(pOp)) {
292                                 if (!(op = fr_str2int(fr_tokens, PyString_AsString(pOp), 0))) {
293                                         ERROR("%s - Invalid operator %s:%s %s %s, falling back to '='",
294                                               funcname, list_name, s1, PyString_AsString(pOp), s2);
295                                         op = T_OP_EQ;
296                                 }
297                         } else if (PyInt_Check(pOp)) {
298                                 op      = PyInt_AsLong(pOp);
299                                 if (!fr_int2str(fr_tokens, op, NULL)) {
300                                         ERROR("%s - Invalid operator %s:%s %i %s, falling back to '='",
301                                               funcname, list_name, s1, op, s2);
302                                         op = T_OP_EQ;
303                                 }
304                         } else {
305                                 ERROR("%s - Invalid operator type for %s:%s ? %s, using default '='",
306                                       funcname, list_name, s1, s2);
307                         }
308                 }
309
310                 if (tmpl_from_attr_str(&dst, s1, REQUEST_CURRENT, PAIR_LIST_REPLY, false, false) <= 0) {
311                         ERROR("%s - Failed to find attribute %s:%s", funcname, list_name, s1);
312                         continue;
313                 }
314
315                 if (radius_request(&current, dst.tmpl_request) < 0) {
316                         ERROR("%s - Attribute name %s:%s refers to outer request but not in a tunnel, skipping...",
317                               funcname, list_name, s1);
318                         continue;
319                 }
320
321                 if (!(vp = fr_pair_afrom_da(ctx, dst.tmpl_da))) {
322                         ERROR("%s - Failed to create attribute %s:%s", funcname, list_name, s1);
323                         continue;
324                 }
325
326                 vp->op = op;
327                 if (fr_pair_value_from_str(vp, s2, -1) < 0) {
328                         DEBUG("%s - Failed: '%s:%s' %s '%s'", funcname, list_name, s1,
329                               fr_int2str(fr_tokens, op, "="), s2);
330                 } else {
331                         DEBUG("%s - '%s:%s' %s '%s'", funcname, list_name, s1,
332                               fr_int2str(fr_tokens, op, "="), s2);
333                 }
334
335                 radius_pairmove(current, vps, vp, false);
336         }
337 }
338
339
340 /*
341  *      This is the core Python function that the others wrap around.
342  *      Pass the value-pair print strings in a tuple.
343  *
344  *      FIXME: We're not checking the errors. If we have errors, what
345  *      do we do?
346  */
347 static int mod_populate_vptuple(PyObject *pPair, VALUE_PAIR *vp)
348 {
349         PyObject *pStr = NULL;
350         char buf[1024];
351
352         /* Look at the fr_pair_fprint_name? */
353
354         if (vp->da->flags.has_tag) {
355                 pStr = PyString_FromFormat("%s:%d", vp->da->name, vp->tag);
356         } else {
357                 pStr = PyString_FromString(vp->da->name);
358         }
359
360         if (!pStr) return -1;
361
362         PyTuple_SET_ITEM(pPair, 0, pStr);
363
364         vp_prints_value(buf, sizeof(buf), vp, '\0');    /* Python doesn't need any escaping */
365
366         pStr = PyString_FromString(buf);
367         if (pStr == NULL) return -1;
368
369         PyTuple_SET_ITEM(pPair, 1, pStr);
370
371         return 0;
372 }
373
374 static rlm_rcode_t do_python_single(REQUEST *request, PyObject *pFunc, char const *funcname)
375 {
376         vp_cursor_t     cursor;
377         VALUE_PAIR      *vp;
378         PyObject        *pRet = NULL;
379         PyObject        *pArgs = NULL;
380         int             tuplelen;
381         int             ret;
382
383         /* Default return value is "OK, continue" */
384         ret = RLM_MODULE_OK;
385
386         /*
387          *      We will pass a tuple containing (name, value) tuples
388          *      We can safely use the Python function to build up a
389          *      tuple, since the tuple is not used elsewhere.
390          *
391          *      Determine the size of our tuple by walking through the packet.
392          *      If request is NULL, pass None.
393          */
394         tuplelen = 0;
395         if (request != NULL) {
396                 for (vp = fr_cursor_init(&cursor, &request->packet->vps);
397                      vp;
398                      vp = fr_cursor_next(&cursor)) tuplelen++;
399         }
400
401         if (tuplelen == 0) {
402                 Py_INCREF(Py_None);
403                 pArgs = Py_None;
404         } else {
405                 int i = 0;
406                 if ((pArgs = PyTuple_New(tuplelen)) == NULL) {
407                         ret = RLM_MODULE_FAIL;
408                         goto finish;
409                 }
410
411                 for (vp = fr_cursor_init(&cursor, &request->packet->vps);
412                      vp;
413                      vp = fr_cursor_next(&cursor), i++) {
414                         PyObject *pPair;
415
416                         /* The inside tuple has two only: */
417                         if ((pPair = PyTuple_New(2)) == NULL) {
418                                 ret = RLM_MODULE_FAIL;
419                                 goto finish;
420                         }
421
422                         if (mod_populate_vptuple(pPair, vp) == 0) {
423                                 /* Put the tuple inside the container */
424                                 PyTuple_SET_ITEM(pArgs, i, pPair);
425                         } else {
426                                 Py_INCREF(Py_None);
427                                 PyTuple_SET_ITEM(pArgs, i, Py_None);
428                                 Py_DECREF(pPair);
429                         }
430                 }
431         }
432
433         /* Call Python function. */
434         pRet = PyObject_CallFunctionObjArgs(pFunc, pArgs, NULL);
435         if (!pRet) {
436                 ret = RLM_MODULE_FAIL;
437                 goto finish;
438         }
439
440         if (!request) {
441                 // check return code at module instantiation time
442                 if (PyInt_CheckExact(pRet)) ret = PyInt_AsLong(pRet);
443                 goto finish;
444         }
445
446         /*
447          *      The function returns either:
448          *  1. (returnvalue, replyTuple, configTuple), where
449          *   - returnvalue is one of the constants RLM_*
450          *   - replyTuple and configTuple are tuples of string
451          *      tuples of size 2
452          *
453          *  2. the function return value alone
454          *
455          *  3. None - default return value is set
456          *
457          * xxx This code is messy!
458          */
459         if (PyTuple_CheckExact(pRet)) {
460                 PyObject *pTupleInt;
461
462                 if (PyTuple_GET_SIZE(pRet) != 3) {
463                         ERROR("%s - Tuple must be (return, replyTuple, configTuple)", funcname);
464                         ret = RLM_MODULE_FAIL;
465                         goto finish;
466                 }
467
468                 pTupleInt = PyTuple_GET_ITEM(pRet, 0);
469                 if (!PyInt_CheckExact(pTupleInt)) {
470                         ERROR("%s - First tuple element not an integer", funcname);
471                         ret = RLM_MODULE_FAIL;
472                         goto finish;
473                 }
474                 /* Now have the return value */
475                 ret = PyInt_AsLong(pTupleInt);
476                 /* Reply item tuple */
477                 mod_vptuple(request->reply, request, &request->reply->vps,
478                             PyTuple_GET_ITEM(pRet, 1), funcname, "reply");
479                 /* Config item tuple */
480                 mod_vptuple(request, request, &request->config,
481                             PyTuple_GET_ITEM(pRet, 2), funcname, "config");
482
483         } else if (PyInt_CheckExact(pRet)) {
484                 /* Just an integer */
485                 ret = PyInt_AsLong(pRet);
486
487         } else if (pRet == Py_None) {
488                 /* returned 'None', return value defaults to "OK, continue." */
489                 ret = RLM_MODULE_OK;
490         } else {
491                 /* Not tuple or None */
492                 ERROR("%s - Function did not return a tuple or None", funcname);
493                 ret = RLM_MODULE_FAIL;
494                 goto finish;
495         }
496
497
498 finish:
499         Py_XDECREF(pArgs);
500         Py_XDECREF(pRet);
501
502         return ret;
503 }
504
505 static void python_interpreter_free(PyThreadState *interp)
506 {
507         PyEval_AcquireLock();
508         PyThreadState_Swap(interp);
509         Py_EndInterpreter(interp);
510         PyEval_ReleaseLock();
511 }
512
513 /** Destroy a thread state
514  *
515  * @param thread to destroy.
516  * @return 0
517  */
518 static int _python_thread_free(python_thread_state_t *thread)
519 {
520         PyEval_RestoreThread(thread->state);    /* Swap in our local thread state */
521         PyThreadState_Clear(thread->state);
522         PyEval_SaveThread();
523
524         PyThreadState_Delete(thread->state);    /* Don't need to hold lock for this */
525
526         return 0;
527 }
528
529 /** Callback for rbtree delete walker
530  *
531  */
532 static void _python_thread_entry_free(void *arg)
533 {
534         talloc_free(arg);
535 }
536
537 /** Cleanup any thread local storage on pthread_exit()
538  *
539  * @param arg The thread currently exiting.
540  */
541 static void _python_thread_tree_free(void *arg)
542 {
543         rad_assert(arg == local_thread_state);
544
545         rbtree_t *tree = talloc_get_type_abort(arg, rbtree_t);
546         rbtree_free(tree);      /* Needs to be this not talloc_free to execute delete walker */
547
548         local_thread_state = NULL;      /* Prevent double free in unittest env */
549 }
550
551 /** Compare instance pointers
552  *
553  */
554 static int _python_inst_cmp(const void *a, const void *b)
555 {
556         python_thread_state_t const *a_p = a, *b_p = b;
557
558         if (a_p->inst < b_p->inst) return -1;
559         if (a_p->inst > b_p->inst) return +1;
560         return 0;
561 }
562
563 /** Thread safe call to a python function
564  *
565  * Will swap in thread state specific to module/thread.
566  */
567 static rlm_rcode_t do_python(rlm_python_t *inst, REQUEST *request, PyObject *pFunc, char const *funcname)
568 {
569         int                     ret;
570         rbtree_t                *thread_tree;
571         python_thread_state_t   *this_thread;
572         python_thread_state_t   find;
573
574         /*
575          *      It's a NOOP if the function wasn't defined
576          */
577         if (!pFunc) return RLM_MODULE_NOOP;
578
579         /*
580          *      Check to see if we've got a thread state tree
581          *      If not, create one.
582          */
583         thread_tree = fr_thread_local_init(local_thread_state, _python_thread_tree_free);
584         if (!thread_tree) {
585                 thread_tree = rbtree_create(NULL, _python_inst_cmp, _python_thread_entry_free, 0);
586                 if (!thread_tree) {
587                         RERROR("Failed allocating thread state tree");
588                         return RLM_MODULE_FAIL;
589                 }
590
591                 ret = fr_thread_local_set(local_thread_state, thread_tree);
592                 if (ret != 0) {
593                         talloc_free(thread_tree);
594                         return RLM_MODULE_FAIL;
595                 }
596         }
597
598         find.inst = inst;
599         /*
600          *      Find the thread state associated with this instance
601          *      and this thread, or create a new thread state.
602          */
603         this_thread = rbtree_finddata(thread_tree, &find);
604         if (!this_thread) {
605                 PyThreadState *state;
606
607                 state = PyThreadState_New(inst->sub_interpreter->interp);
608
609                 RDEBUG3("Initialised new thread state %p", state);
610                 if (!state) {
611                         REDEBUG("Failed initialising local PyThreadState on first run");
612                         return RLM_MODULE_FAIL;
613                 }
614
615                 this_thread = talloc(NULL, python_thread_state_t);
616                 this_thread->inst = inst;
617                 this_thread->state = state;
618                 talloc_set_destructor(this_thread, _python_thread_free);
619
620                 if (!rbtree_insert(thread_tree, this_thread)) {
621                         RERROR("Failed inserting thread state into TLS tree");
622                         talloc_free(this_thread);
623
624                         return RLM_MODULE_FAIL;
625                 }
626         }
627         RDEBUG3("Using thread state %p", this_thread->state);
628
629         PyEval_RestoreThread(this_thread->state);       /* Swap in our local thread state */
630         ret = do_python_single(request, pFunc, funcname);
631         PyEval_SaveThread();
632
633         return ret;
634 }
635
636 #define MOD_FUNC(x) \
637 static rlm_rcode_t CC_HINT(nonnull) mod_##x(void *instance, REQUEST *request) { \
638         return do_python((rlm_python_t *) instance, request, ((rlm_python_t *)instance)->x.function, #x);\
639 }
640
641 MOD_FUNC(authenticate)
642 MOD_FUNC(authorize)
643 MOD_FUNC(preacct)
644 MOD_FUNC(accounting)
645 MOD_FUNC(checksimul)
646 MOD_FUNC(pre_proxy)
647 MOD_FUNC(post_proxy)
648 MOD_FUNC(post_auth)
649 #ifdef WITH_COA
650 MOD_FUNC(recv_coa)
651 MOD_FUNC(send_coa)
652 #endif
653 static void python_obj_destroy(PyObject **ob)
654 {
655         if (*ob != NULL) {
656                 Py_DECREF(*ob);
657                 *ob = NULL;
658         }
659 }
660
661 static void python_function_destroy(python_func_def_t *def)
662 {
663         python_obj_destroy(&def->function);
664         python_obj_destroy(&def->module);
665 }
666
667 /** Import a user module and load a function from it
668  *
669  */
670 static int python_function_load(python_func_def_t *def)
671 {
672         char const *funcname = "python_function_load";
673
674         if (def->module_name == NULL || def->function_name == NULL) return 0;
675
676         def->module = PyImport_ImportModule(def->module_name);
677         if (!def->module) {
678                 ERROR("%s - Module '%s' not found", funcname, def->module_name);
679
680         error:
681                 python_error_log();
682                 ERROR("%s - Failed to import python function '%s.%s'",
683                       funcname, def->module_name, def->function_name);
684                 Py_XDECREF(def->function);
685                 def->function = NULL;
686                 Py_XDECREF(def->module);
687                 def->module = NULL;
688
689                 return -1;
690         }
691
692         def->function = PyObject_GetAttrString(def->module, def->function_name);
693         if (!def->function) {
694                 ERROR("%s - Function '%s.%s' is not found", funcname, def->module_name, def->function_name);
695                 goto error;
696         }
697
698         if (!PyCallable_Check(def->function)) {
699                 ERROR("%s - Function '%s.%s' is not callable", funcname, def->module_name, def->function_name);
700                 goto error;
701         }
702
703         return 0;
704 }
705
706 /*
707  *      Parse a configuration section, and populate a dict.
708  *      This function is recursively called (allows to have nested dicts.)
709  */
710 static void python_parse_config(CONF_SECTION *cs, int lvl, PyObject *dict)
711 {
712         int             indent_section = (lvl + 1) * 4;
713         int             indent_item = (lvl + 2) * 4;
714         CONF_ITEM       *ci = NULL;
715
716         if (!cs || !dict) return;
717
718         DEBUG("%*s%s {", indent_section, " ", cf_section_name1(cs));
719
720         while ((ci = cf_item_find_next(cs, ci))) {
721                 /*
722                  *  This is a section.
723                  *  Create a new dict, store it in current dict,
724                  *  Then recursively call python_parse_config with this section and the new dict.
725                  */
726                 if (cf_item_is_section(ci)) {
727                         CONF_SECTION *sub_cs = cf_item_to_section(ci);
728                         char const *key = cf_section_name1(sub_cs); /* dict key */
729                         PyObject *sub_dict, *pKey;
730
731                         if (!key) continue;
732
733                         pKey = PyString_FromString(key);
734                         if (!pKey) continue;
735
736                         if (PyDict_Contains(dict, pKey)) {
737                                 WARN("rlm_python: Ignoring duplicate config section '%s'", key);
738                                 continue;
739                         }
740
741                         if (!(sub_dict = PyDict_New())) {
742                                 WARN("rlm_python: Unable to create subdict for config section '%s'", key);
743                         }
744
745                         (void)PyDict_SetItem(dict, pKey, sub_dict);
746
747                         python_parse_config(sub_cs, lvl + 1, sub_dict);
748                 } else if (cf_item_is_pair(ci)) {
749                         CONF_PAIR *cp = cf_item_to_pair(ci);
750                         char const  *key = cf_pair_attr(cp); /* dict key */
751                         char const  *value = cf_pair_value(cp); /* dict value */
752                         PyObject *pKey, *pValue;
753
754                         if (!key || !value) continue;
755
756                         pKey = PyString_FromString(key);
757                         pValue = PyString_FromString(value);
758                         if (!pKey || !pValue) continue;
759
760                         /*
761                          *  This is an item.
762                          *  Store item attr / value in current dict.
763                          */
764                         if (PyDict_Contains(dict, pKey)) {
765                                 WARN("rlm_python: Ignoring duplicate config item '%s'", key);
766                                 continue;
767                         }
768
769                         (void)PyDict_SetItem(dict, pKey, pValue);
770
771                         DEBUG("%*s%s = %s", indent_item, " ", key, value);
772                 }
773         }
774
775         DEBUG("%*s}", indent_section, " ");
776 }
777
778 #ifdef HAVE_DL_ITERATE_PHDR
779 static int dlopen_libpython_cb(struct dl_phdr_info *info,
780                                         UNUSED size_t size, void *data)
781 {
782         const char *pattern = "/" LIBPYTHON_LINKER_NAME;
783         char **ppath = (char **)data;
784
785         if (strstr(info->dlpi_name, pattern) != NULL) {
786                 if (*ppath != NULL) {
787                         talloc_free(*ppath);
788                         *ppath = NULL;
789                         return EEXIST;
790                 } else {
791                         *ppath = talloc_strdup(NULL, info->dlpi_name);
792                         if (*ppath == NULL) {
793                                 return errno;
794                         }
795                 }
796         }
797         return 0;
798 }
799
800 /* Dlopen the already linked libpython */
801 static void *dlopen_libpython(int flags)
802 {
803         char *path = NULL;
804         int rc;
805         void *handle;
806
807         /* Find the linked libpython path */
808         rc = dl_iterate_phdr(dlopen_libpython_cb, &path);
809         if (rc != 0) {
810                 WARN("Failed searching for libpython "
811                         "among linked libraries: %s", strerror(rc));
812                 return NULL;
813         } else if (path == NULL) {
814                 WARN("Libpython is not found among linked libraries");
815                 return NULL;
816         }
817
818         /* Dlopen the found library */
819         handle = dlopen(path, flags);
820         if (handle == NULL) {
821                 WARN("Failed loading %s: %s", path, dlerror());
822         }
823         talloc_free(path);
824         return handle;
825 }
826 #else   /* ! HAVE_DL_ITERATE_PHDR */
827 /* Dlopen libpython by its linker name (bare soname) */
828 static void *dlopen_libpython(int flags)
829 {
830         const char *name = LIBPYTHON_LINKER_NAME;
831         void *handle;
832         handle = dlopen(name, flags);
833         if (handle == NULL) {
834                 WARN("Failed loading %s: %s", name, dlerror());
835         }
836         return handle;
837 }
838 #endif  /* ! HAVE_DL_ITERATE_PHDR */
839
840 /** Initialises a separate python interpreter for this module instance
841  *
842  */
843 static int python_interpreter_init(rlm_python_t *inst, CONF_SECTION *conf)
844 {
845         int i;
846
847         /*
848          *      Explicitly load libpython, so symbols will be available to lib-dynload modules
849          */
850         if (python_instances == 0) {
851                 INFO("Python version: %s", Py_GetVersion());
852
853                 python_dlhandle = dlopen_libpython(RTLD_NOW | RTLD_GLOBAL);
854                 if (!python_dlhandle) WARN("Failed loading libpython symbols into global symbol table");
855
856 #if PY_VERSION_HEX > 0x03050000
857                 {
858                         wchar_t *name;
859
860                         wide_name = Py_DecodeLocale(main_config.name, strlen(main_config.name));
861                         Py_SetProgramName(name);                /* The value of argv[0] as a wide char string */
862                         PyMem_RawFree(name);
863                 }
864 #else
865                 {
866                         char *name;
867
868                         name = talloc_strdup(NULL, main_config.name);
869                         Py_SetProgramName(name);                /* The value of argv[0] as a wide char string */
870                         talloc_free(name);
871                 }
872 #endif
873
874                 Py_InitializeEx(0);                     /* Don't override signal handlers - noop on subs calls */
875                 PyEval_InitThreads();                   /* This also grabs a lock (which we then need to release) */
876                 main_interpreter = PyThreadState_Get(); /* Store reference to the main interpreter */
877         }
878         rad_assert(PyEval_ThreadsInitialized());
879
880         /*
881          *      Increment the reference counter
882          */
883         python_instances++;
884
885         /*
886          *      This sets up a separate environment for each python module instance
887          *      These will be destroyed on Py_Finalize().
888          */
889         if (!inst->cext_compat) {
890                 inst->sub_interpreter = Py_NewInterpreter();
891         } else {
892                 inst->sub_interpreter = main_interpreter;
893         }
894
895         PyThreadState_Swap(inst->sub_interpreter);
896
897         /*
898          *      Due to limitations in Python, sub-interpreters don't work well
899          *      with Python C extensions if they use GIL lock functions.
900          */
901         if (!inst->cext_compat || !main_module) {
902                 CONF_SECTION *cs;
903
904                 /*
905                  *      Set the python search path
906                  */
907                 if (inst->python_path) {
908 #if PY_VERSION_HEX > 0x03050000
909                         {
910                                 wchar_t *name;
911
912                                 path = Py_DecodeLocale(inst->python_path, strlen(inst->python_path));
913                                 PySys_SetPath(path);
914                                 PyMem_RawFree(path);
915                         }
916 #else
917                         {
918                                 char *path;
919
920                                 path = talloc_strdup(NULL, inst->python_path);
921                                 PySys_SetPath(path);
922                                 talloc_free(path);
923                         }
924 #endif
925                 }
926
927                 /*
928                  *      Initialise a new module, with our default methods
929                  */
930                 inst->module = Py_InitModule3("radiusd", module_methods, "FreeRADIUS python module");
931                 if (!inst->module) {
932                 error:
933                         python_error_log();
934                         PyEval_SaveThread();
935                         return -1;
936                 }
937
938                 /*
939                  *      Py_InitModule3 returns a borrowed ref, the actual
940                  *      module is owned by sys.modules, so we also need
941                  *      to own the module to prevent it being freed early.
942                  */
943                 Py_IncRef(inst->module);
944
945                 if (inst->cext_compat) main_module = inst->module;
946
947                 for (i = 0; radiusd_constants[i].name; i++) {
948                         if ((PyModule_AddIntConstant(inst->module, radiusd_constants[i].name,
949                                                      radiusd_constants[i].value)) < 0)
950                                 goto error;
951                 }
952
953                 /*
954                  *      Convert a FreeRADIUS config structure into a python
955                  *      dictionary.
956                  */
957                 inst->pythonconf_dict = PyDict_New();
958                 if (!inst->pythonconf_dict) {
959                         ERROR("Unable to create python dict for config");
960                         python_error_log();
961                         return -1;
962                 }
963
964                 /*
965                  *      Add module configuration as a dict
966                  */
967                 if (PyModule_AddObject(inst->module, "config", inst->pythonconf_dict) < 0) goto error;
968
969                 cs = cf_section_sub_find(conf, "config");
970                 if (cs) python_parse_config(cs, 0, inst->pythonconf_dict);
971         } else {
972                 inst->module = main_module;
973                 Py_IncRef(inst->module);
974                 inst->pythonconf_dict = PyObject_GetAttrString(inst->module, "config");
975                 Py_IncRef(inst->pythonconf_dict);
976         }
977
978         PyEval_SaveThread();
979
980         return 0;
981 }
982
983 /*
984  *      Do any per-module initialization that is separate to each
985  *      configured instance of the module.  e.g. set up connections
986  *      to external databases, read configuration files, set up
987  *      dictionary entries, etc.
988  *
989  *      If configuration information is given in the config section
990  *      that must be referenced in later calls, store a handle to it
991  *      in *instance otherwise put a null pointer there.
992  *
993  */
994 static int mod_instantiate(CONF_SECTION *conf, void *instance)
995 {
996         rlm_python_t    *inst = instance;
997         int             code = 0;
998
999         inst->name = cf_section_name2(conf);
1000         if (!inst->name) inst->name = cf_section_name1(conf);
1001
1002         /*
1003          *      Load the python code required for this module instance
1004          */
1005         if (python_interpreter_init(inst, conf) < 0) return -1;
1006
1007         /*
1008          *      Switch to our module specific main thread
1009          */
1010         PyEval_RestoreThread(inst->sub_interpreter);
1011
1012         /*
1013          *      Process the various sections
1014          */
1015 #define PYTHON_FUNC_LOAD(_x) if (python_function_load(&inst->_x) < 0) goto error
1016         PYTHON_FUNC_LOAD(instantiate);
1017         PYTHON_FUNC_LOAD(authenticate);
1018         PYTHON_FUNC_LOAD(authorize);
1019         PYTHON_FUNC_LOAD(preacct);
1020         PYTHON_FUNC_LOAD(accounting);
1021         PYTHON_FUNC_LOAD(checksimul);
1022         PYTHON_FUNC_LOAD(pre_proxy);
1023         PYTHON_FUNC_LOAD(post_proxy);
1024         PYTHON_FUNC_LOAD(post_auth);
1025 #ifdef WITH_COA
1026         PYTHON_FUNC_LOAD(recv_coa);
1027         PYTHON_FUNC_LOAD(send_coa);
1028 #endif
1029         PYTHON_FUNC_LOAD(detach);
1030
1031         /*
1032          *      Call the instantiate function.
1033          */
1034         code = do_python_single(NULL, inst->instantiate.function, "instantiate");
1035         if (code < 0) {
1036         error:
1037                 python_error_log();     /* Needs valid thread with GIL */
1038                 PyEval_SaveThread();
1039                 return -1;
1040         }
1041         PyEval_SaveThread();
1042
1043         return 0;
1044 }
1045
1046 static int mod_detach(void *instance)
1047 {
1048         rlm_python_t *inst = instance;
1049         int          ret;
1050
1051         /*
1052          *      Call module destructor
1053          */
1054         PyEval_RestoreThread(inst->sub_interpreter);
1055
1056         ret = do_python_single(NULL, inst->detach.function, "detach");
1057
1058 #define PYTHON_FUNC_DESTROY(_x) python_function_destroy(&inst->_x)
1059         PYTHON_FUNC_DESTROY(instantiate);
1060         PYTHON_FUNC_DESTROY(authorize);
1061         PYTHON_FUNC_DESTROY(authenticate);
1062         PYTHON_FUNC_DESTROY(preacct);
1063         PYTHON_FUNC_DESTROY(accounting);
1064         PYTHON_FUNC_DESTROY(checksimul);
1065         PYTHON_FUNC_DESTROY(detach);
1066
1067         Py_DecRef(inst->pythonconf_dict);
1068         Py_DecRef(inst->module);
1069
1070         PyEval_SaveThread();
1071
1072         /*
1073          *      Force cleaning up of threads if this is *NOT* a worker
1074          *      thread, which happens if this is being called from
1075          *      unittest framework, and probably with the server running
1076          *      in debug mode.
1077          */
1078         rbtree_free(local_thread_state);
1079         local_thread_state = NULL;
1080
1081         /*
1082          *      Only destroy if it's a subinterpreter
1083          */
1084         if (!inst->cext_compat) python_interpreter_free(inst->sub_interpreter);
1085
1086         if ((--python_instances) == 0) {
1087                 PyThreadState_Swap(main_interpreter); /* Swap to the main thread */
1088                 Py_Finalize();
1089                 dlclose(python_dlhandle);
1090         }
1091
1092         return ret;
1093 }
1094
1095 /*
1096  *      The module name should be the only globally exported symbol.
1097  *      That is, everything else should be 'static'.
1098  *
1099  *      If the module needs to temporarily modify it's instantiation
1100  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
1101  *      The server will then take care of ensuring that the module
1102  *      is single-threaded.
1103  */
1104 extern module_t rlm_python;
1105 module_t rlm_python = {
1106         .magic          = RLM_MODULE_INIT,
1107         .name           = "python",
1108         .type           = RLM_TYPE_THREAD_SAFE,
1109         .inst_size      = sizeof(rlm_python_t),
1110         .config         = module_config,
1111         .instantiate    = mod_instantiate,
1112         .detach         = mod_detach,
1113         .methods = {
1114                 [MOD_AUTHENTICATE]      = mod_authenticate,
1115                 [MOD_AUTHORIZE]         = mod_authorize,
1116                 [MOD_PREACCT]           = mod_preacct,
1117                 [MOD_ACCOUNTING]        = mod_accounting,
1118                 [MOD_SESSION]           = mod_checksimul,
1119                 [MOD_PRE_PROXY]         = mod_pre_proxy,
1120                 [MOD_POST_PROXY]        = mod_post_proxy,
1121                 [MOD_POST_AUTH]         = mod_post_auth,
1122 #ifdef WITH_COA
1123                 [MOD_RECV_COA]          = mod_recv_coa,
1124                 [MOD_SEND_COA]          = mod_send_coa
1125 #endif
1126         }
1127 };