The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_python.c
Go to the documentation of this file.
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: ac02e4a4c01fe935d5af752c20faf6045f024c8f $
19 * @file rlm_python.c
20 * @brief Translates requests between the server an a python interpreter.
21 *
22 * @note Rewritten by Nick Porter for FreeRADIUS v4
23 *
24 * @copyright 2000,2006,2015-2016 The FreeRADIUS server project
25 * @copyright 2025 Network RADIUS SAS
26 */
27RCSID("$Id: ac02e4a4c01fe935d5af752c20faf6045f024c8f $")
28
29#define LOG_PREFIX inst->name
30
31#include <freeradius-devel/server/base.h>
32#include <freeradius-devel/server/module_rlm.h>
33#include <freeradius-devel/server/pairmove.h>
34#include <freeradius-devel/util/debug.h>
35#include <freeradius-devel/util/lsan.h>
36
37#include <Python.h>
38#include <structmember.h>
39#include <frameobject.h> /* Python header not pulled in by default. */
40#include <libgen.h>
41#include <dlfcn.h>
42
43/** Specifies the module.function to load for processing a section
44 *
45 */
46typedef struct {
47 PyObject *module; //!< Python reference to module.
48 PyObject *function; //!< Python reference to function in module.
49
50 char const *module_name; //!< String name of module.
51 char const *function_name; //!< String name of function in module.
52 char *name1; //!< Section name1 where this is called.
53 char *name2; //!< Section name2 where this is called.
54 fr_rb_node_t node; //!< Entry in tree of Python functions.
56
57/** An instance of the rlm_python module
58 *
59 */
60typedef struct {
61 char const *name; //!< Name of the module instance
62 PyThreadState *interpreter; //!< The interpreter used for this instance of rlm_python.
63 PyObject *module; //!< Local, interpreter specific module.
64 char const *def_module_name; //!< Default module for Python functions
65 fr_rb_tree_t funcs; //!< Tree of function calls found by call_env parser
66 bool funcs_init; //!< Has the tree been initialised.
67
71
72 PyObject *pythonconf_dict; //!< Configuration parameters defined in the module
73 //!< made available to the python script.
75
76/** Global config for python library
77 *
78 */
79typedef struct {
80 char const *path; //!< Path to search for python files in.
81 bool path_include_default; //!< Include the default python path in `path`
82 bool verbose; //!< Enable libpython verbose logging
84
88
89/** Tracks a python module inst/thread state pair
90 *
91 * Multiple instances of python create multiple interpreters and each
92 * thread must have a PyThreadState per interpreter, to track execution.
93 */
94typedef struct {
95 rlm_python_t const *inst; //!< Current module instance data.
96 PyThreadState *state; //!< Module instance/thread specific state.
98
99/** Additional fields for pairs
100 *
101 */
102typedef struct {
103 PyObject_HEAD //!< Common fields needed for every python object.
104 fr_dict_attr_t const *da; //!< dictionary attribute for this pair.
105 fr_pair_t *vp; //!< Real FreeRADIUS pair for this Python pair.
106 unsigned int idx; //!< Instance index.
107 PyObject *parent; //!< Parent object of this pair.
109
110typedef struct {
111 PyObject_HEAD //!< Common fields needed for every python object.
112 PyObject *request; //!< Request list.
113 PyObject *reply; //!< Reply list.
114 PyObject *control; //!< Control list.
115 PyObject *state; //!< Session state list.
117
118/** Wrapper around a python instance
119 *
120 * This is added to the FreeRADIUS module to allow us to
121 * get at the global and thread local instance data.
122 */
123typedef struct {
124 PyObject_HEAD //!< Common fields needed for every python object.
125 rlm_python_t const *inst; //!< Module instance.
126 rlm_python_thread_t *t; //!< Thread-specific python instance.
127 request_t *request; //!< Current request.
129
130static void *python_dlhandle;
131static PyThreadState *global_interpreter; //!< Our first interpreter.
132
133static rlm_python_t const *current_inst = NULL; //!< Used for communication with inittab functions.
134static CONF_SECTION *current_conf; //!< Used for communication with inittab functions.
135static rlm_python_thread_t *current_t; //!< Used for communicating with object init function.
136
137static PyObject *py_freeradius_log(UNUSED PyObject *self, PyObject *args, PyObject *kwds);
138
139static int py_freeradius_state_init(PyObject *self, UNUSED PyObject *args, UNUSED PyObject *kwds);
140
141static PyObject *py_freeradius_pair_map_subscript(PyObject *self, PyObject *attr);
142static PyObject *py_freeradius_attribute_instance(PyObject *self, PyObject *attr);
143static int py_freeradius_pair_map_set(PyObject* self, PyObject* attr, PyObject* value);
144static PyObject *py_freeradius_pair_getvalue(PyObject *self, void *closure);
145static int py_freeradius_pair_setvalue(PyObject *self, PyObject *value, void *closure);
146static PyObject *py_freeradius_pair_str(PyObject *self);
147
149 .path = NULL,
150 .path_include_default = true
151};
152
154 { FR_CONF_OFFSET("path", libpython_global_config_t, path) },
155 { FR_CONF_OFFSET("path_include_default", libpython_global_config_t, path_include_default) },
156 { FR_CONF_OFFSET("verbose", libpython_global_config_t, verbose) },
158};
159
160static int libpython_init(void);
161static void libpython_free(void);
162
164 .name = "python",
165 .config = python_global_config,
166 .init = libpython_init,
167 .free = libpython_free,
169};
170
171extern global_lib_autoinst_t const * const rlm_python_lib[];
176
177/*
178 * As of Python 3.8 the GIL will be per-interpreter
179 * If there are still issues with CEXTs,
180 * multiple interpreters and the GIL at that point
181 * users can build rlm_python against Python 3.8
182 * and the horrible hack of using a single interpreter
183 * for all instances of rlm_python will no longer be
184 * required.
185 */
186
187/*
188 * A mapping of configuration file names to internal variables.
189 */
191
192#define A(x) { FR_CONF_OFFSET("mod_" #x, rlm_python_t, x.module_name), .dflt = "${.module}" }, \
193 { FR_CONF_OFFSET("func_" #x, rlm_python_t, x.function_name) },
194
196 A(detach)
197
198#undef A
199
200 { FR_CONF_OFFSET("module", rlm_python_t, def_module_name) },
201
203};
204
205static struct {
206 char const *name;
207 int value;
209
210#define A(x) { #x, x },
211
212 A(L_DBG)
213 A(L_WARN)
214 A(L_INFO)
215 A(L_ERR)
216 A(L_WARN)
218 A(L_DBG_ERR)
237#undef A
238
239 { NULL, 0 },
241
242/** The class which all pair types inherit from
243 *
244 */
245static PyTypeObject py_freeradius_pair_def = {
246 PyVarObject_HEAD_INIT(NULL, 0)
247 .tp_name = "freeradius.Pair",
248 .tp_doc = "An attribute value pair",
249 .tp_basicsize = sizeof(py_freeradius_pair_t),
250 .tp_itemsize = 0,
251 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
252 .tp_new = PyType_GenericNew,
253};
254
255/** How to access "value" attribute of a pair
256 *
257 */
258static PyGetSetDef py_freeradius_pair_getset[] = {
259 {
260 .name = "value",
263 .doc = "Pair value",
264 .closure = NULL
265 },
266 { NULL } /* Terminator */
267};
268
269/** Contains a value pair of a specific type
270 *
271 */
272static PyTypeObject py_freeradius_value_pair_def = {
273 PyVarObject_HEAD_INIT(NULL, 0)
274 .tp_name = "freeradius.ValuePair",
275 .tp_doc = "A value pair, i.e. one of the type string, integer, ipaddr etc...)",
276 .tp_flags = Py_TPFLAGS_DEFAULT,
277 .tp_base = &py_freeradius_pair_def,
278 .tp_getset = py_freeradius_pair_getset,
279 .tp_str = py_freeradius_pair_str,
280 .tp_as_mapping = &(PyMappingMethods) {
281 .mp_subscript = py_freeradius_attribute_instance,
282 .mp_ass_subscript = py_freeradius_pair_map_set,
283 }
284};
285
286/** Contains group attribute of a specific type
287 *
288 * Children of this attribute may be accessed using the map protocol
289 * i.e. foo['child-of-foo'].
290 *
291 */
292static PyTypeObject py_freeradius_grouping_pair_def = {
293 PyVarObject_HEAD_INIT(NULL, 0)
294 .tp_name = "freeradius.GroupingPair",
295 .tp_doc = "A grouping pair, i.e. one of the type group, tlv, vsa or vendor. "
296 "Children are accessible via the mapping protocol i.e. foo['child-of-foo]",
297 .tp_flags = Py_TPFLAGS_DEFAULT,
298 .tp_base = &py_freeradius_pair_def,
299 .tp_as_mapping = &(PyMappingMethods){
300 .mp_subscript = py_freeradius_pair_map_subscript,
301 .mp_ass_subscript = py_freeradius_pair_map_set,
302 }
303};
304
305/** Each instance contains a top level list (i.e. request, reply, control, session-state)
306 */
307static PyTypeObject py_freeradius_pair_list_def = {
308 PyVarObject_HEAD_INIT(NULL, 0)
309 .tp_name = "freeradius.PairList",
310 .tp_doc = "A list of objects of freeradius.GroupingPairList and freeradius.ValuePair",
311 .tp_flags = Py_TPFLAGS_DEFAULT,
312 .tp_base = &py_freeradius_pair_def,
313 .tp_as_mapping = &(PyMappingMethods){
314 .mp_subscript = py_freeradius_pair_map_subscript,
315 .mp_ass_subscript = py_freeradius_pair_map_set,
316 }
317};
318
319static PyMemberDef py_freeradius_request_attrs[] = {
320 {
321 .name = "request",
322 .type = T_OBJECT,
323 .offset = offsetof(py_freeradius_request_t, request),
324 .flags = READONLY,
325 .doc = "Pairs in the request list - received from the network"
326 },
327 {
328 .name = "reply",
329 .type = T_OBJECT,
330 .offset = offsetof(py_freeradius_request_t, reply),
331 .flags = READONLY,
332 .doc = "Pairs in the reply list - sent to the network"
333 },
334 {
335 .name = "control",
336 .type = T_OBJECT,
337 .offset = offsetof(py_freeradius_request_t, control),
338 .flags = READONLY,
339 .doc = "Pairs in the control list - control the behaviour of subsequently called modules"
340 },
341 {
342 .name = "session-state",
343 .type = T_OBJECT,
344 .offset = offsetof(py_freeradius_request_t, state),
345 .flags = READONLY,
346 .doc = "Pairs in the session-state list - persists for the length of the session"
347 },
348 { NULL } /* Terminator */
349};
350
351static PyTypeObject py_freeradius_request_def = {
352 PyVarObject_HEAD_INIT(NULL, 0)
353 .tp_name = "freeradius.Request",
354 .tp_doc = "freeradius request handle",
355 .tp_basicsize = sizeof(py_freeradius_request_t),
356 .tp_itemsize = 0,
357 .tp_flags = Py_TPFLAGS_DEFAULT,
358 .tp_new = PyType_GenericNew,
359 .tp_members = py_freeradius_request_attrs
360};
361
362static PyTypeObject py_freeradius_state_def = {
363 PyVarObject_HEAD_INIT(NULL, 0)
364 .tp_name = "freeradius.State",
365 .tp_doc = "Private state data",
366 .tp_basicsize = sizeof(py_freeradius_state_t),
367 .tp_itemsize = 0,
368 .tp_flags = Py_TPFLAGS_DEFAULT,
369 .tp_new = PyType_GenericNew,
370 .tp_init = py_freeradius_state_init
371};
372
373#ifndef _PyCFunction_CAST
374#define _PyCFunction_CAST(func) (PyCFunction)((void(*)(void))(func))
375#endif
376
377/*
378 * freeradius Python functions
379 */
380static PyMethodDef py_freeradius_methods[] = {
381 { "log", _PyCFunction_CAST(py_freeradius_log), METH_VARARGS | METH_KEYWORDS,
382 "freeradius.log(msg[, type, lvl])\n\n"
383 "Print a message using the freeradius daemon's logging system.\n"
384 "type should be one of the following constants:\n"
385 " freeradius.L_DBG\n"
386 " freeradius.L_INFO\n"
387 " freeradius.L_WARN\n"
388 " freeradius.L_ERR\n"
389 "lvl should be one of the following constants:\n"
390 " freeradius.L_DBG_LVL_OFF\n"
391 " freeradius.L_DBG_LVL_1\n"
392 " freeradius.L_DBG_LVL_2\n"
393 " freeradius.L_DBG_LVL_3\n"
394 " freeradius.L_DBG_LVL_4\n"
395 " freeradius.L_DBG_LVL_MAX\n"
396 },
397 { NULL, NULL, 0, NULL },
398};
399
400static PyModuleDef py_freeradius_def = {
401 PyModuleDef_HEAD_INIT,
402 .m_name = "freeradius",
403 .m_doc = "FreeRADIUS python module",
404 .m_size = 0,
405 .m_methods = py_freeradius_methods
406};
407
408/** How to compare two Python calls
409 *
410 */
411static int8_t python_func_def_cmp(void const *one, void const *two)
412{
413 python_func_def_t const *a = one, *b = two;
414 int ret;
415
416 ret = strcmp(a->name1, b->name1);
417 if (ret != 0) return CMP(ret, 0);
418 if (!a->name2 && !b->name2) return 0;
419 if (!a->name2 || !b->name2) return a->name2 ? 1 : -1;
420 ret = strcmp(a->name2, b->name2);
421 return CMP(ret, 0);
422}
423
424/** Return the module instance object associated with the thread state or interpreter state
425 *
426 */
427static inline CC_HINT(always_inline) py_freeradius_state_t *rlm_python_state_obj(void)
428{
429 PyObject *dict;
430
431 dict = PyThreadState_GetDict(); /* If this is NULL, we're dealing with the main interpreter */
432 if (!dict) {
433 PyObject *module;
434
435 module = PyState_FindModule(&py_freeradius_def);
436 if (unlikely(!module)) return NULL;
437
438 dict = PyModule_GetDict(module);
439 if (unlikely(!dict)) return NULL;
440 }
441
442 return (py_freeradius_state_t *)PyDict_GetItemString(dict, "__State");
443}
444
445/** Return the rlm_python instance associated with the current interpreter
446 *
447 */
449{
450 py_freeradius_state_t const *p_state;
451
452 p_state = rlm_python_state_obj();
453 if (unlikely(!p_state)) return NULL;
454 return p_state->inst;
455}
456
457/** Return the request associated with the current thread state
458 *
459 */
461{
462 py_freeradius_state_t const *p_state;
463
464 p_state = rlm_python_state_obj();
465 if (unlikely(!p_state)) return NULL;
466
467 return p_state->request;
468}
469
470/** Set the request associated with the current thread state
471 *
472 */
474{
475 py_freeradius_state_t *p_state;
476
477 p_state = rlm_python_state_obj();
478 if (unlikely(!p_state)) return;
479
480 p_state->request = request;
481}
482
483/** Allow fr_log to be called from python
484 *
485 */
486static PyObject *py_freeradius_log(UNUSED PyObject *self, PyObject *args, PyObject *kwds)
487{
488 static char const *kwlist[] = { "msg", "type", "lvl", NULL };
489 char *msg;
490 int type = L_DBG;
491 int lvl = L_DBG_LVL_2;
493
494 if (fr_debug_lvl < lvl) Py_RETURN_NONE; /* Don't bother parsing args */
495
496 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ii", (char **)((uintptr_t)kwlist),
497 &msg, &type, &lvl)) Py_RETURN_NONE;
498
499 fr_log(&default_log, type, __FILE__, __LINE__, "rlm_python (%s) - %s", inst->name, msg);
500
501 Py_RETURN_NONE;
502}
503
504static int py_freeradius_state_init(PyObject *self, UNUSED PyObject *args, UNUSED PyObject *kwds)
505{
507 rlm_python_t const *inst;
508
510 our_self->t = current_t ? talloc_get_type_abort(current_t, rlm_python_thread_t) : NULL; /* May be NULL if this is the first interpreter */
511 our_self->inst = inst = current_t ? current_t->inst : current_inst;
512 DEBUG3("Populating __State data with %p/%p", our_self->inst, our_self->t);
513
514 return 0;
515}
516
517/** Returns a specific instance of freeradius.Pair
518 *
519 * Called when a numeric index is used on a PairGroup or PairValue as part of an object getter
520 */
521static PyObject *py_freeradius_attribute_instance(PyObject *self, PyObject *attr)
522{
523 long index;
524 py_freeradius_pair_t *pair, *init_pair = (py_freeradius_pair_t *)self;
525
526 if (!PyLong_CheckExact(attr)) Py_RETURN_NONE;
527 index = PyLong_AsLong(attr);
528
529 if (index < 0) {
530 PyErr_SetString(PyExc_AttributeError, "Cannot use negative attribute instance values");
531 return NULL;
532 }
533 if (index == 0) return self;
534
535 if (fr_type_is_leaf(init_pair->da->type)) {
536 pair = PyObject_New(py_freeradius_pair_t, (PyTypeObject *)&py_freeradius_value_pair_def);
537
538 } else if (fr_type_is_struct(init_pair->da->type)) {
539 pair = PyObject_New(py_freeradius_pair_t, (PyTypeObject *)&py_freeradius_grouping_pair_def);
540 } else {
541 PyErr_SetString(PyExc_AttributeError, "Unsupported data type");
542 return NULL;
543 }
544 if (!pair) {
545 PyErr_SetString(PyExc_MemoryError, "Failed to allocate PyObject");
546 return NULL;
547 };
548 pair->parent = init_pair->parent;
549 Py_INCREF(init_pair->parent);
550 pair->da = init_pair->da;
551 pair->idx = index;
552 if (init_pair->vp) pair->vp = fr_pair_find_by_da_idx(fr_pair_parent_list(init_pair->vp), pair->da, (unsigned int)index);
553 return (PyObject *)pair;
554}
555
556/** Returns a freeradius.Pair
557 *
558 * Called when pair["attr"] or pair["attr"][n] is accessed.
559 * When pair["attr"] is accessed, `self` is `pair` - which is the list or a pair group object
560 * When pair["attr"][n] is accessed, `self` is pair["attr"]
561 *
562 * Either a group object or pair object will be returned.
563 */
564static PyObject *py_freeradius_pair_map_subscript(PyObject *self, PyObject *attr)
565{
566 py_freeradius_pair_t *our_self = (py_freeradius_pair_t *)self;
567 char const *attr_name;
568 ssize_t len;
571 fr_dict_attr_t const *da;
572 fr_pair_list_t *list = NULL;
573
574 /*
575 * If we have a numeric subscript, find the nth instance of the pair.
576 */
577 if (PyLong_CheckExact(attr)) return py_freeradius_attribute_instance(self, attr);
578
579 if (!PyUnicode_CheckExact(attr)) {
580 PyErr_Format(PyExc_AttributeError, "Invalid type '%s' for map attribute",
581 ((PyTypeObject *)PyObject_Type(attr))->tp_name);
582 return NULL;
583 }
584 attr_name = PyUnicode_AsUTF8AndSize(attr, &len);
585
586 if (PyObject_IsInstance(self, (PyObject *)&py_freeradius_pair_list_def)) {
587 fr_dict_attr_search_by_name_substr(NULL, &da, request->proto_dict, &FR_SBUFF_IN(attr_name, len),
588 NULL, true, false);
589 } else {
590 fr_dict_attr_by_name_substr(NULL, &da, our_self->da, &FR_SBUFF_IN(attr_name, len), NULL);
591 }
592 if (our_self->vp) list = &our_self->vp->vp_group;
593
594 if (!da) {
595 PyErr_Format(PyExc_AttributeError, "Invalid attribute name '%.*s'", (int)len, attr_name);
596 return NULL;
597 }
598
599 if (fr_type_is_leaf(da->type)) {
600 pair = PyObject_New(py_freeradius_pair_t, (PyTypeObject *)&py_freeradius_value_pair_def);
601 } else if (fr_type_is_structural(da->type)) {
602 pair = PyObject_New(py_freeradius_pair_t, (PyTypeObject *)&py_freeradius_grouping_pair_def);
603 } else {
604 PyErr_SetString(PyExc_AttributeError, "Unsupported data type");
605 return NULL;
606 }
607 if (!pair) {
608 PyErr_SetString(PyExc_MemoryError, "Failed to allocate PyObject");
609 return NULL;
610 };
611
612 pair->parent = self;
613 Py_INCREF(self);
614 pair->da = da;
615 pair->vp = list ? fr_pair_find_by_da(list, NULL, da) : NULL;
616 pair->idx = 0;
617
618 return (PyObject *)pair;
619}
620
621/** Build out missing parent pairs when a leaf node is assigned a value.
622 *
623 */
625{
627 fr_pair_t *parent = ((py_freeradius_pair_t *)obj_pair->parent)->vp;
628
629 if (!parent) {
631 if (!parent) return NULL;
632 }
633
634 /*
635 * Asked to populate foo[n] - check that we have n instances already
636 */
637 if (obj_pair->idx > 0) {
638 unsigned int count = fr_pair_count_by_da(&parent->vp_group, obj_pair->da);
639 if (count < obj_pair->idx) {
640 PyErr_Format(PyExc_AttributeError, "Attempt to set instance %d when only %d exist", index, count);
641 return NULL;
642 }
643 }
644 fr_pair_append_by_da(parent, &obj_pair->vp, &parent->vp_group, obj_pair->da);
645
646 return obj_pair->vp;
647}
648
649/** Set the value of a pair
650 *
651 * Called with two Python syntaxes
652 *
653 * - request['foo'] = 'baa'
654 * `self` will be the parent object - either the list or parent structural object.
655 * `attr` is the value in [].
656 * `value` is what we want to set the pair to.
657 *
658 * - request['foo'][n] = 'baa'
659 * `self` will be the first instance of the attribute `foo`
660 * `attr` will be the instance number
661 * `value` is what we want to set the pair to.
662 *
663 * We expect `value` to be a UTF8 string object.
664 *
665 * Due to Python "magic" this is also called when `del request['foo']` happens - only with
666 * value as NULL.
667 */
668static int py_freeradius_pair_map_set(PyObject* self, PyObject* attr, PyObject* value)
669{
670 fr_pair_list_t *list = NULL;
672 py_freeradius_pair_t *our_self = (py_freeradius_pair_t *)self;
673 fr_pair_t *vp = NULL;
674 char const *vstr;
675 ssize_t vlen;
676 bool del = (value ? false : true);
677
678 if (value && !PyUnicode_CheckExact(value)) {
679 PyErr_Format(PyExc_AttributeError, "Invalid value type '%s'", ((PyTypeObject *)PyObject_Type(value))->tp_name);
680 return -1;
681 }
682
683 /*
684 * list['attr'] = 'value'
685 * Look up DA represented by 'attr' and find pair or create pair to update
686 */
687 if (PyUnicode_CheckExact(attr)) {
688 char const *attr_name;
689 ssize_t len;
690 fr_dict_attr_t const *da = NULL;
691
692 attr_name = PyUnicode_AsUTF8AndSize(attr, &len);
693
694 if (PyObject_IsInstance(self, (PyObject *)&py_freeradius_pair_list_def)) {
695 fr_dict_attr_search_by_name_substr(NULL, &da, request->proto_dict, &FR_SBUFF_IN(attr_name, len),
696 NULL, true, false);
697 } else {
698 fr_dict_attr_by_name_substr(NULL, &da, our_self->da, &FR_SBUFF_IN(attr_name, len), NULL);
699 }
700
701 if (!da) {
702 PyErr_Format(PyExc_AttributeError, "Invalid attribute name %.*s", (int)len, attr_name);
703 return -1;
704 }
705
706 // `self` is the parent of the pair we're building - so we build parents to that point.
707 if (!our_self->vp) {
708 if (del) return 0; // If we're deleting then no need to build parents.
709 our_self->vp = py_freeradius_build_parents(self);
710 }
711 if (!our_self->vp) return -1;
712
713 list = &our_self->vp->vp_group;
714
715 vp = fr_pair_find_by_da(list, NULL, da);
716 if (del) goto del;
717
718 if (!vp) {
719 if (fr_pair_append_by_da(fr_pair_list_parent(list), &vp, list, da) < 0) {
720 PyErr_Format(PyExc_MemoryError, "Failed to add attribute %s", da->name);
721 return -1;
722 }
723 } else {
725 }
726
727 /*
728 * list['attr'][n] = 'value'
729 * Look for instance n, creating if necessary
730 */
731 } else if (PyLong_CheckExact(attr)) {
732 long index = PyLong_AsLong(attr);
734
735 if (index < 0) {
736 PyErr_SetString(PyExc_AttributeError, "Cannot use negative attribute instance values");
737 return -1;
738 }
739
740 if (!parent->vp) {
741 if (del) return 0;
743 }
744 if (!parent->vp) return -1;
745
746 list = &parent->vp->vp_group;
747
748 if (index == 0) {
749 if (!our_self->vp) {
750 if (fr_pair_append_by_da(fr_pair_list_parent(list), &our_self->vp, list, our_self->da) < 0) {
751 PyErr_Format(PyExc_MemoryError, "Failed to add attribute %s", our_self->da->name);
752 return -1;
753 }
754 } else {
755 fr_value_box_clear_value(&our_self->vp->data);
756 }
757 vp = our_self->vp;
758 if (del) goto del;
759 } else {
760 vp = fr_pair_find_by_da_idx(list, our_self->da, index);
761 if (del) goto del;
762 if (!vp) {
763 unsigned int count = fr_pair_count_by_da(list, our_self->da);
764 if (count < index) {
765 PyErr_Format(PyExc_AttributeError, "Attempt to set instance %ld when only %d exist", index, count);
766 return -1;
767 }
768 if (fr_pair_append_by_da(fr_pair_list_parent(list), &vp, list, our_self->da) < 0) {
769 PyErr_Format(PyExc_MemoryError, "Failed to add attribute %s", our_self->da->name);
770 return -1;
771 }
772 }
773 }
774 } else {
775 PyErr_Format(PyExc_AttributeError, "Invalid object type '%s'", ((PyTypeObject *)PyObject_Type(attr))->tp_name);
776 return -1;
777 }
778
780
781 vstr = PyUnicode_AsUTF8AndSize(value, &vlen);
782
783 if (fr_pair_value_from_str(vp, vstr, vlen, NULL, false) < 0) {
784 PyErr_Format(PyExc_AttributeError, "Failed setting '%s' = '%.*s", vp->da->name, (int)vlen, vstr);
785 fr_pair_delete(list, vp);
786 return -1;
787 }
788
789 RDEBUG2("set %pP", vp);
790 return 0;
791
792del:
793 if (vp) {
794 RDEBUG2("delete %pP", vp);
795 fr_pair_delete(list, vp);
796 }
797 return 0;
798}
799
800/** Return a native Python object of the appropriate type for leaf pair objects
801 *
802 * Accessed as `request['attr'].value`
803 *
804 * `self` is the Python leaf pair object
805 */
806static PyObject *py_freeradius_pair_getvalue(PyObject *self, UNUSED void *closure)
807{
808 py_freeradius_pair_t *own_self = (py_freeradius_pair_t *)self;
809 PyObject *value = NULL;
810 fr_pair_t *vp = own_self->vp;
811
812 if (!vp) Py_RETURN_NONE;
813
814 switch(vp->vp_type) {
815 case FR_TYPE_STRING:
816 value = PyUnicode_FromStringAndSize(vp->vp_strvalue, vp->vp_length);
817 break;
818
819 case FR_TYPE_OCTETS:
820 value = PyBytes_FromStringAndSize((char const *)vp->vp_octets, vp->vp_length);
821 break;
822
823 case FR_TYPE_BOOL:
824 value = PyBool_FromLong(vp->vp_bool);
825 break;
826
827 case FR_TYPE_UINT8:
828 value = PyLong_FromUnsignedLong(vp->vp_uint8);
829 break;
830
831 case FR_TYPE_UINT16:
832 value = PyLong_FromUnsignedLong(vp->vp_uint16);
833 break;
834
835 case FR_TYPE_UINT32:
836 value = PyLong_FromUnsignedLong(vp->vp_uint32);
837 break;
838
839 case FR_TYPE_UINT64:
840 value = PyLong_FromUnsignedLongLong(vp->vp_uint64);
841 break;
842
843 case FR_TYPE_INT8:
844 value = PyLong_FromLong(vp->vp_int8);
845 break;
846
847 case FR_TYPE_INT16:
848 value = PyLong_FromLong(vp->vp_int16);
849 break;
850
851 case FR_TYPE_INT32:
852 value = PyLong_FromLong(vp->vp_int32);
853 break;
854
855 case FR_TYPE_INT64:
856 value = PyLong_FromLongLong(vp->vp_int64);
857 break;
858
859 case FR_TYPE_FLOAT32:
860 value = PyFloat_FromDouble((double) vp->vp_float32);
861 break;
862
863 case FR_TYPE_FLOAT64:
864 value = PyFloat_FromDouble(vp->vp_float64);
865 break;
866
867 case FR_TYPE_SIZE:
868 value = PyLong_FromSize_t(vp->vp_size);
869 break;
870
872 case FR_TYPE_DATE:
873 case FR_TYPE_IFID:
880 case FR_TYPE_ETHERNET:
881 {
882 ssize_t slen;
883 char buffer[1024];
884
885 slen = fr_value_box_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), &vp->data, NULL);
886 if (slen < 0) {
887 error:
888 PyErr_Format(PyExc_MemoryError, "Failed marshalling %s to Python value", vp->da->name);
889 return NULL;
890 }
891 value = PyUnicode_FromStringAndSize(buffer, (size_t)slen);
892 }
893 break;
894
895 case FR_TYPE_NON_LEAF:
896 fr_assert(0);
897 break;
898 }
899
900 if (value == NULL) goto error;
901
902 Py_INCREF(value);
903 return value;
904}
905
906/** Use a native Python object of the appropriate type to set a leaf pair
907 *
908 * Using Python syntax `request['attr'].value = object` or `request['attr'][n].value`
909 *
910 * `self` is the Python object representing the leaf node pair
911 */
912static int py_freeradius_pair_setvalue(PyObject *self, PyObject *value, UNUSED void *closure)
913{
914 py_freeradius_pair_t *own_self = (py_freeradius_pair_t *)self;
915 fr_pair_t *vp = own_self->vp;
916
917 /*
918 * The pair doesn't exist yet - so create it
919 */
920 if (!vp) {
922 fr_pair_list_t *list;
923 unsigned int count;
924
925 if (!parent->vp) parent->vp = py_freeradius_build_parents(own_self->parent);
926 if (!parent->vp) return -1;
927
928 list = &parent->vp->vp_group;
929 count = fr_pair_count_by_da(list, own_self->da);
930 if (count < own_self->idx) {
931 PyErr_Format(PyExc_AttributeError, "Attempt to set instance %d when only %d exist", own_self->idx, count);
932 return -1;
933 }
934 fr_pair_append_by_da(fr_pair_list_parent(list), &vp, list, own_self->da);
935 own_self->vp = vp;
936 }
937
938 switch (vp->da->type) {
939 case FR_TYPE_STRING:
940 {
941 char const *val;
942 ssize_t len;
943 if (!PyUnicode_CheckExact(value)){
944 wrong_type:
945 PyErr_Format(PyExc_AttributeError, "Incorrect Python type '%s' for attribute type '%s'",
946 ((PyTypeObject *)PyObject_Type(value))->tp_name, fr_type_to_str(vp->da->type));
947 return -1;
948 }
949 val = PyUnicode_AsUTF8AndSize(value, &len);
951 fr_value_box_bstrndup(vp, &vp->data, NULL, val, len, false);
952 }
953 break;
954
955 case FR_TYPE_OCTETS:
956 {
957 uint8_t *val;
958 ssize_t len;
959 if (!PyObject_IsInstance(value, (PyObject *)&PyBytes_Type)) goto wrong_type;
960 PyBytes_AsStringAndSize(value, (char **)&val, &len);
961 fr_value_box_clear(&vp->data);
962 fr_value_box_memdup(vp, &vp->data, NULL, val, len, false);
963 }
964 break;
965
966 case FR_TYPE_BOOL:
967 case FR_TYPE_UINT8:
968 case FR_TYPE_UINT16:
969 case FR_TYPE_UINT32:
970 case FR_TYPE_UINT64:
971 case FR_TYPE_INT8:
972 case FR_TYPE_INT16:
973 case FR_TYPE_INT32:
974 case FR_TYPE_INT64:
975 case FR_TYPE_SIZE:
976 {
977 long long val;
978 if (!PyLong_CheckExact(value)) goto wrong_type;
979 val = PyLong_AsLongLong(value);
980
981 switch (vp->da->type) {
982 case FR_TYPE_BOOL:
983 vp->vp_bool = (bool)val;
984 break;
985 case FR_TYPE_UINT8:
986 vp->vp_uint8 = (uint8_t)val;
987 break;
988 case FR_TYPE_UINT16:
989 vp->vp_uint16 = (uint16_t)val;
990 break;
991 case FR_TYPE_UINT32:
992 vp->vp_uint32 = (uint32_t)val;
993 break;
994 case FR_TYPE_UINT64:
995 vp->vp_uint64 = (uint64_t)val;
996 break;
997 case FR_TYPE_INT8:
998 vp->vp_int8 = (int8_t)val;
999 break;
1000 case FR_TYPE_INT16:
1001 vp->vp_int16 = (int16_t)val;
1002 break;
1003 case FR_TYPE_INT32:
1004 vp->vp_int32 = (int32_t)val;
1005 break;
1006 case FR_TYPE_INT64:
1007 vp->vp_int64 = (int64_t)val;
1008 break;
1009 case FR_TYPE_SIZE:
1010 vp->vp_size = (size_t)val;
1011 break;
1012 default:
1013 fr_assert(0);
1014 }
1015 }
1016 break;
1017
1018 case FR_TYPE_FLOAT32:
1019 case FR_TYPE_FLOAT64:
1020 {
1021 double val;
1022 if (!PyFloat_CheckExact(value)) goto wrong_type;
1023 val = PyFloat_AsDouble(value);
1024
1025 if (vp->da->type == FR_TYPE_FLOAT32) {
1026 vp->vp_float32 = (float)val;
1027 } else {
1028 vp->vp_float64 = val;
1029 }
1030 }
1031 break;
1032
1033 case FR_TYPE_TIME_DELTA:
1034 case FR_TYPE_DATE:
1035 case FR_TYPE_IFID:
1036 case FR_TYPE_IPV6_ADDR:
1038 case FR_TYPE_IPV4_ADDR:
1042 case FR_TYPE_ETHERNET:
1043 {
1044 char const *val;
1045 ssize_t len;
1046
1047 if (!PyUnicode_CheckExact(value)) goto wrong_type;
1048
1049 val = PyUnicode_AsUTF8AndSize(value, &len);
1050 if (fr_pair_value_from_str(vp, val, len, NULL, false) < 0) {
1051 PyErr_Format(PyExc_AttributeError, "Failed parsing %.*s as %s", (int)len, val, fr_type_to_str(vp->da->type));
1052 return -1;
1053 }
1054 }
1055 break;
1056
1057 case FR_TYPE_NON_LEAF:
1058 fr_assert(0);
1059 break;
1060 }
1061
1062 return 0;
1063}
1064
1065/** Return the string representation of a leaf pair node
1066 *
1067 * Called when the attribute is accessed in print() or str() or selective other
1068 * magical Python contexts.
1069 */
1070static PyObject *py_freeradius_pair_str(PyObject *self) {
1071 py_freeradius_pair_t *own_self = (py_freeradius_pair_t *)self;
1072 PyObject *value = NULL;
1073 fr_pair_t *vp = own_self->vp;
1074
1075 if (!vp) return PyObject_Str(Py_None);
1076
1077 switch(vp->vp_type) {
1078 case FR_TYPE_STRING:
1079 value = PyUnicode_FromStringAndSize(vp->vp_strvalue, vp->vp_length);
1080 break;
1081
1082 case FR_TYPE_NON_LEAF:
1083 fr_assert(0);
1084 break;
1085
1086 default:
1087 {
1088 ssize_t slen;
1089 char buffer[1024];
1090
1091 slen = fr_value_box_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), &vp->data, NULL);
1092 if (slen < 0) {
1093 error:
1094 PyErr_Format(PyExc_MemoryError, "Failed casting %s to Python string", vp->da->name);
1095 return NULL;
1096 }
1097 value = PyUnicode_FromStringAndSize(buffer, (size_t)slen);
1098 }
1099 break;
1100 }
1101
1102 if (value == NULL) goto error;
1103
1104 return value;
1105}
1106
1107/** Print out the current error
1108 *
1109 * Must be called with a valid thread state set
1110 */
1111static void python_error_log(rlm_python_t const *inst, request_t *request)
1112{
1113 PyObject *p_type = NULL, *p_value = NULL, *p_traceback = NULL, *p_str_1 = NULL, *p_str_2 = NULL;
1114
1115 PyErr_Fetch(&p_type, &p_value, &p_traceback);
1116 PyErr_NormalizeException(&p_type, &p_value, &p_traceback);
1117 if (!p_type || !p_value) goto failed;
1118
1119 if (((p_str_1 = PyObject_Str(p_type)) == NULL) || ((p_str_2 = PyObject_Str(p_value)) == NULL)) goto failed;
1120
1121 ROPTIONAL(RERROR, ERROR, "%s (%s)", PyUnicode_AsUTF8(p_str_1), PyUnicode_AsUTF8(p_str_2));
1122
1123 if (p_traceback != Py_None) {
1124 PyTracebackObject *ptb = (PyTracebackObject*)p_traceback;
1125 size_t fnum = 0;
1126
1127 while (ptb != NULL) {
1128 PyFrameObject *cur_frame = ptb->tb_frame;
1129#if PY_VERSION_HEX >= 0x030A0000
1130 PyCodeObject *code = PyFrame_GetCode(cur_frame);
1131
1132 ROPTIONAL(RERROR, ERROR, "[%ld] %s:%d at %s()",
1133 fnum,
1134 PyUnicode_AsUTF8(code->co_filename),
1135 PyFrame_GetLineNumber(cur_frame),
1136 PyUnicode_AsUTF8(code->co_name)
1137 );
1138 Py_XDECREF(code);
1139#else
1140 ROPTIONAL(RERROR, ERROR, "[%ld] %s:%d at %s()",
1141 fnum,
1142 PyUnicode_AsUTF8(cur_frame->f_code->co_filename),
1143 PyFrame_GetLineNumber(cur_frame),
1144 PyUnicode_AsUTF8(cur_frame->f_code->co_name)
1145 );
1146#endif
1147
1148 ptb = ptb->tb_next;
1149 fnum++;
1150 }
1151 }
1152
1153failed:
1154 Py_XDECREF(p_str_1);
1155 Py_XDECREF(p_str_2);
1156 Py_XDECREF(p_type);
1157 Py_XDECREF(p_value);
1158 Py_XDECREF(p_traceback);
1159}
1160
1161/** Create the Python object representing a pair list
1162 *
1163 */
1164static inline CC_HINT(always_inline) PyObject *pair_list_alloc(request_t *request, fr_dict_attr_t const *list)
1165{
1166 PyObject *py_list;
1167 py_freeradius_pair_t *our_list;
1168
1169 /*
1170 * When handing instantiate and detach, there is no request
1171 */
1172 if (!request) {
1173 Py_INCREF(Py_None);
1174 return Py_None;
1175 }
1176
1177 py_list = PyObject_CallObject((PyObject *)&py_freeradius_pair_list_def, NULL);
1178 if (unlikely(!py_list)) return NULL;
1179
1180 our_list = (py_freeradius_pair_t *)py_list;
1181 our_list->da = list;
1182 our_list->vp = fr_pair_list_parent(tmpl_list_head(request, list));
1183 our_list->parent = NULL;
1184 our_list->idx = 0;
1185 return py_list;
1186}
1187
1189 request_t *request, PyObject *p_func, char const *funcname)
1190{
1191 rlm_rcode_t rcode = RLM_MODULE_OK;
1192 rlm_python_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1193
1194 PyObject *p_ret = NULL;
1195 PyObject *py_request;
1196 py_freeradius_request_t *our_request;
1197
1198 /*
1199 * Instantiate the request
1200 */
1201 py_request = PyObject_CallObject((PyObject *)&py_freeradius_request_def, NULL);
1202 if (unlikely(!py_request)) {
1203 python_error_log(inst, request);
1205 }
1206
1207 our_request = (py_freeradius_request_t *)py_request;
1208 rlm_python_set_request(request);
1209
1210 /*
1211 * Create the list roots
1212 */
1213 our_request->request = pair_list_alloc(request, request_attr_request);
1214 if (unlikely(!our_request->request)) {
1215 req_error:
1216 Py_DECREF(py_request);
1217 python_error_log(inst, request);
1219 }
1220
1221 our_request->reply = pair_list_alloc(request, request_attr_reply);
1222 if (unlikely(!our_request->reply)) goto req_error;
1223
1224 our_request->control = pair_list_alloc(request, request_attr_control);
1225 if (unlikely(!our_request->control)) goto req_error;
1226
1227 our_request->state = pair_list_alloc(request, request_attr_state);
1228 if (unlikely(!our_request->state)) goto req_error;
1229
1230 /* Call Python function. */
1231 p_ret = PyObject_CallFunctionObjArgs(p_func, py_request, NULL);
1232 if (!p_ret) {
1233 RERROR("Python function returned no value");
1234 rcode = RLM_MODULE_FAIL;
1235 goto finish;
1236 }
1237
1238 if (!request) {
1239 // check return code at module instantiation time
1240 if (PyNumber_Check(p_ret)) rcode = PyLong_AsLong(p_ret);
1241 goto finish;
1242 }
1243
1244 /*
1245 * The function is expected to either return a return value
1246 * or None, which results in the default return value.
1247 */
1248 if (PyNumber_Check(p_ret)) {
1249 /* Just an integer */
1250 rcode = PyLong_AsLong(p_ret);
1251
1252 } else if (p_ret == Py_None) {
1253 /* returned 'None', return value defaults to "OK, continue." */
1254 rcode = RLM_MODULE_OK;
1255 } else {
1256 /* Not tuple or None */
1257 ERROR("%s - Function did not return a tuple or None", funcname);
1258 rcode = RLM_MODULE_FAIL;
1259 goto finish;
1260 }
1261
1262finish:
1264
1265 if (rcode == RLM_MODULE_FAIL) python_error_log(inst, request);
1266 Py_XDECREF(p_ret);
1267 Py_XDECREF(py_request);
1268
1269 RETURN_MODULE_RCODE(rcode);
1270}
1271
1272/** Thread safe call to a python function
1273 *
1274 * Will swap in thread state specific to module/thread.
1275 */
1276static unlang_action_t mod_python(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1277{
1278 rlm_python_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_python_thread_t);
1279 python_call_env_t *func = talloc_get_type_abort(mctx->env_data, python_call_env_t);
1280 rlm_rcode_t rcode;
1281
1282 /*
1283 * It's a NOOP if the function wasn't defined
1284 */
1285 if (!func->func->function) RETURN_MODULE_NOOP;
1286
1287 RDEBUG3("Using thread state %p/%p", mctx->mi->data, t->state);
1288
1289 PyEval_RestoreThread(t->state); /* Swap in our local thread state */
1290 do_python_single(&rcode, mctx, request, func->func->function, func->func->function_name);
1291 (void)fr_cond_assert(PyEval_SaveThread() == t->state);
1292
1293 RETURN_MODULE_RCODE(rcode);
1294}
1295
1296static void python_obj_destroy(PyObject **ob)
1297{
1298 if (*ob != NULL) {
1299 Py_DECREF(*ob);
1300 *ob = NULL;
1301 }
1302}
1303
1305{
1307 python_obj_destroy(&def->module);
1308}
1309
1310/** Import a user module and load a function from it
1311 *
1312 */
1314{
1315 rlm_python_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1316 char const *funcname = "python_function_load";
1317
1318 if (def->module_name == NULL || (def->function_name == NULL && def->name1 == NULL)) return 0;
1319
1320 LSAN_DISABLE(def->module = PyImport_ImportModule(def->module_name));
1321 if (!def->module) {
1322 ERROR("%s - Module '%s' load failed", funcname, def->module_name);
1323 error:
1324 python_error_log(inst, NULL);
1325 Py_XDECREF(def->function);
1326 def->function = NULL;
1327 Py_XDECREF(def->module);
1328 def->module = NULL;
1329
1330 return -1;
1331 }
1332
1333 /*
1334 * Calls found by call_env parsing will have name1 set
1335 * If name2 is set first look for <name1>_<name2> and fall back to <name1>
1336 */
1337 if (!def->function_name) def->function_name = def->name2 ? talloc_asprintf(def, "%s_%s", def->name1, def->name2) : def->name1;
1338
1339 def->function = PyObject_GetAttrString(def->module, def->function_name);
1340 if (!def->function && def->name2) {
1341 PyErr_Clear(); // Since we're checking for another function, clear any errors.
1343 def->function_name = def->name1;
1344 def->function = PyObject_GetAttrString(def->module, def->function_name);
1345 }
1346 if (!def->function) {
1347 ERROR("%s - Function '%s.%s' is not found", funcname, def->module_name, def->function_name);
1348 goto error;
1349 }
1350
1351 if (!PyCallable_Check(def->function)) {
1352 ERROR("%s - Function '%s.%s' is not callable", funcname, def->module_name, def->function_name);
1353 goto error;
1354 }
1355
1356 DEBUG2("Loaded function '%s.%s'", def->module_name, def->function_name);
1357 return 0;
1358}
1359
1360/*
1361 * Parse a configuration section, and populate a dict.
1362 * This function is recursively called (allows to have nested dicts.)
1363 */
1364static int python_parse_config(rlm_python_t const *inst, CONF_SECTION *cs, int lvl, PyObject *dict)
1365{
1366 int indent_section = (lvl * 4);
1367 int indent_item = (lvl + 1) * 4;
1368 int ret = 0;
1369 CONF_ITEM *ci = NULL;
1370
1371 if (!cs || !dict) return -1;
1372
1373 DEBUG("%*s%s {", indent_section, " ", cf_section_name1(cs));
1374
1375 while ((ci = cf_item_next(cs, ci))) {
1376 /*
1377 * This is a section.
1378 * Create a new dict, store it in current dict,
1379 * Then recursively call python_parse_config with this section and the new dict.
1380 */
1381 if (cf_item_is_section(ci)) {
1382 CONF_SECTION *sub_cs = cf_item_to_section(ci);
1383 char const *key = cf_section_name1(sub_cs); /* dict key */
1384 PyObject *sub_dict, *p_key;
1385
1386 p_key = PyUnicode_FromString(key);
1387 if (!p_key) {
1388 ERROR("Failed converting config key \"%s\" to python string", key);
1389 return -1;
1390 }
1391
1392 if (PyDict_Contains(dict, p_key)) {
1393 WARN("Ignoring duplicate config section '%s'", key);
1394 continue;
1395 }
1396
1397 MEM(sub_dict = PyDict_New());
1398 (void)PyDict_SetItem(dict, p_key, sub_dict);
1399
1400 ret = python_parse_config(inst, sub_cs, lvl + 1, sub_dict);
1401 if (ret < 0) break;
1402 } else if (cf_item_is_pair(ci)) {
1403 CONF_PAIR *cp = cf_item_to_pair(ci);
1404 char const *key = cf_pair_attr(cp); /* dict key */
1405 char const *value = cf_pair_value(cp); /* dict value */
1406 PyObject *p_key, *p_value;
1407
1408 if (!value) {
1409 WARN("Skipping \"%s\" as it has no value", key);
1410 continue;
1411 }
1412
1413 p_key = PyUnicode_FromString(key);
1414 p_value = PyUnicode_FromString(value);
1415 if (!p_key) {
1416 ERROR("Failed converting config key \"%s\" to python string", key);
1417 return -1;
1418 }
1419 if (!p_value) {
1420 ERROR("Failed converting config value \"%s\" to python string", value);
1421 return -1;
1422 }
1423
1424 /*
1425 * This is an item.
1426 * Store item attr / value in current dict.
1427 */
1428 if (PyDict_Contains(dict, p_key)) {
1429 WARN("Ignoring duplicate config item '%s'", key);
1430 continue;
1431 }
1432
1433 (void)PyDict_SetItem(dict, p_key, p_value);
1434
1435 DEBUG("%*s%s = \"%s\"", indent_item, " ", key, value);
1436 }
1437 }
1438
1439 DEBUG("%*s}", indent_section, " ");
1440
1441 return ret;
1442}
1443
1444/** Make the current instance's config available within the module we're initialising
1445 *
1446 */
1448{
1449 CONF_SECTION *cs;
1450
1451 /*
1452 * Convert a FreeRADIUS config structure into a python
1453 * dictionary.
1454 */
1455 inst->pythonconf_dict = PyDict_New();
1456 if (!inst->pythonconf_dict) {
1457 ERROR("Unable to create python dict for config");
1458 error:
1459 Py_XDECREF(inst->pythonconf_dict);
1460 inst->pythonconf_dict = NULL;
1461 python_error_log(inst, NULL);
1462 return -1;
1463 }
1464
1465 cs = cf_section_find(conf, "config", NULL);
1466 if (cs) {
1467 DEBUG("Inserting \"config\" section into python environment as radiusd.config");
1468 if (python_parse_config(inst, cs, 0, inst->pythonconf_dict) < 0) goto error;
1469 }
1470
1471 /*
1472 * Add module configuration as a dict
1473 */
1474 if (PyModule_AddObject(module, "config", inst->pythonconf_dict) < 0) goto error;
1475
1476 return 0;
1477}
1478
1479/** Import integer constants into the module we're initialising
1480 *
1481 */
1482static int python_module_import_constants(rlm_python_t const *inst, PyObject *module)
1483{
1484 size_t i;
1485
1486 for (i = 0; freeradius_constants[i].name; i++) {
1487 if ((PyModule_AddIntConstant(module, freeradius_constants[i].name, freeradius_constants[i].value)) < 0) {
1488 ERROR("Failed adding constant to module");
1489 python_error_log(inst, NULL);
1490 return -1;
1491 }
1492 }
1493
1494 return 0;
1495}
1496
1497/*
1498 * Python 3 interpreter initialisation and destruction
1499 */
1500static PyObject *python_module_init(void)
1501{
1502 PyObject *module;
1503 PyObject *p_state;
1504 static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
1505 static bool type_ready = false;
1506
1508
1509 /*
1510 * Only allow one thread at a time to do the module init. This is
1511 * out of an abundance of caution as it's unclear whether the
1512 * reference counts on the various objects are thread safe.
1513 */
1514 pthread_mutex_lock(&init_lock);
1515
1516 /*
1517 * The type definitions are global, so we only need to call the
1518 * init functions the first pass through.
1519 */
1520
1521 if (!type_ready) {
1522 /*
1523 * We need to initialise the definitions first
1524 * this fills in any fields we didn't explicitly
1525 * specify, and gets the structures ready for
1526 * use by the python interpreter.
1527 */
1528 if (PyType_Ready(&py_freeradius_pair_def) < 0) {
1529 error:
1530 pthread_mutex_unlock(&init_lock);
1532 Py_RETURN_NONE;
1533 }
1534
1535 if (PyType_Ready(&py_freeradius_value_pair_def) < 0) goto error;
1536
1537 if (PyType_Ready(&py_freeradius_grouping_pair_def) < 0) goto error;
1538
1539 if (PyType_Ready(&py_freeradius_pair_list_def) < 0) goto error;
1540
1541 if (PyType_Ready(&py_freeradius_request_def) < 0) goto error;
1542
1543 if (PyType_Ready(&py_freeradius_state_def) < 0) goto error;
1544
1545 type_ready = true;
1546 }
1547
1548 /*
1549 * The module is per-interpreter
1550 */
1551 module = PyModule_Create(&py_freeradius_def);
1552 if (!module) {
1554 goto error;
1555 }
1556
1557 /*
1558 * PyModule_AddObject steals ref on success, we we
1559 * INCREF here to give it something to steal, else
1560 * on free the refcount would go negative.
1561 *
1562 * Note here we're creating a new instance of an
1563 * object, not adding the object definition itself
1564 * as there's no reason that a python script would
1565 * ever need to create an instance object.
1566 *
1567 * The instantiation function associated with the
1568 * the __State object takes care of populating the
1569 * instance data from globals and thread-specific
1570 * variables.
1571 */
1572 p_state = PyObject_CallObject((PyObject *)&py_freeradius_state_def, NULL);
1573 Py_INCREF(&py_freeradius_state_def);
1574
1575 if (PyModule_AddObject(module, "__State", p_state) < 0) {
1576 Py_DECREF(&py_freeradius_state_def);
1577 Py_DECREF(module);
1578 goto error;
1579 }
1580
1581 /*
1582 * For "Pair" we're inserting an object definition
1583 * as opposed to the object instance we inserted
1584 * for inst.
1585 */
1586 Py_INCREF(&py_freeradius_pair_def);
1587 if (PyModule_AddObject(module, "Pair", (PyObject *)&py_freeradius_pair_def) < 0) {
1588 Py_DECREF(&py_freeradius_pair_def);
1589 Py_DECREF(module);
1590 goto error;
1591 }
1592 pthread_mutex_unlock(&init_lock);
1593
1594 return module;
1595}
1596
1598{
1599 rlm_python_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1600 CONF_SECTION *conf = mctx->mi->conf;
1601 PyObject *module;
1602
1603 /*
1604 * python_module_init takes no args, so we need
1605 * to set these globals so that when it's
1606 * called during interpreter initialisation
1607 * it can get at the current instance config.
1608 */
1611
1612 PyEval_RestoreThread(global_interpreter);
1613 LSAN_DISABLE(inst->interpreter = Py_NewInterpreter());
1614 if (!inst->interpreter) {
1615 ERROR("Failed creating new interpreter");
1616 return -1;
1617 }
1618 DEBUG3("Created new interpreter %p", inst->interpreter);
1619 PyEval_SaveThread(); /* Unlock GIL */
1620
1621 PyEval_RestoreThread(inst->interpreter);
1622
1623 /*
1624 * Import the radiusd module into this python
1625 * environment. Each interpreter gets its
1626 * own copy which it can mutate as much as
1627 * it wants.
1628 */
1629 module = PyImport_ImportModule("freeradius");
1630 if (!module) {
1631 ERROR("Failed importing \"freeradius\" module into interpreter %p", inst->interpreter);
1632 return -1;
1633 }
1634 if ((python_module_import_config(inst, conf, module) < 0) ||
1635 (python_module_import_constants(inst, module) < 0)) {
1636 Py_DECREF(module);
1637 return -1;
1638 }
1639 inst->module = module;
1640 PyEval_SaveThread();
1641
1642 return 0;
1643}
1644
1645static void python_interpreter_free(rlm_python_t *inst, PyThreadState *interp)
1646{
1647 PyEval_RestoreThread(interp); /* Switches thread state and locks GIL */
1648
1649 /*
1650 * We incremented the reference count earlier
1651 * during module initialisation.
1652 */
1653 Py_XDECREF(inst->module);
1654
1655 Py_EndInterpreter(interp); /* Destroys interpreter (GIL still locked) - sets thread state to NULL */
1656 PyThreadState_Swap(global_interpreter); /* Get a none-null thread state */
1657 PyEval_SaveThread(); /* Unlock GIL */
1658}
1659
1660/*
1661 * Do any per-module initialization that is separate to each
1662 * configured instance of the module. e.g. set up connections
1663 * to external databases, read configuration files, set up
1664 * dictionary entries, etc.
1665 *
1666 * If configuration information is given in the config section
1667 * that must be referenced in later calls, store a handle to it
1668 * in *instance otherwise put a null pointer there.
1669 *
1670 */
1671static int mod_instantiate(module_inst_ctx_t const *mctx)
1672{
1673 rlm_python_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1674 python_func_def_t *func = NULL;
1676 CONF_PAIR *cp;
1677 char *pair_name;
1678
1679 if (inst->interpreter) return 0;
1680 if (python_interpreter_init(mctx) < 0) return -1;
1681 inst->name = mctx->mi->name;
1682 if (!inst->funcs_init) fr_rb_inline_init(&inst->funcs, python_func_def_t, node, python_func_def_cmp, NULL);
1683 /*
1684 * Switch to our module specific interpreter
1685 */
1686 PyEval_RestoreThread(inst->interpreter);
1687
1688 /*
1689 * Process the various sections
1690 */
1691#define PYTHON_FUNC_LOAD(_x) if (python_function_load(mctx, &inst->_x) < 0) goto error
1693 PYTHON_FUNC_LOAD(detach);
1694
1695 /*
1696 * Load all the Python functions found by the call_env parser.
1697 */
1698 func = fr_rb_iter_init_inorder(&iter, &inst->funcs);
1699 while (func) {
1700 /*
1701 * Check for mod_<name1>_<name2> or mod_<name1> config pairs.
1702 * If neither exist, fall back to default Python module.
1703 */
1704 if (func->name2) {
1705 pair_name = talloc_asprintf(func, "mod_%s_%s", func->name1, func->name2);
1706 cp = cf_pair_find(mctx->mi->conf, pair_name);
1707 talloc_free(pair_name);
1708 if (cp) goto found_mod;
1709 }
1710 pair_name = talloc_asprintf(func, "mod_%s", func->name1);
1711 cp = cf_pair_find(mctx->mi->conf, pair_name);
1712 talloc_free(pair_name);
1713 found_mod:
1714 func->module_name = cp ? cf_pair_value(cp) : inst->def_module_name;
1715
1716 /*
1717 * Check for func_<name1>_<name2> or func_<name1> function overrides.
1718 * Checks for Python functions <name1>_<name2> and <name1> are done
1719 * in python_function_load.
1720 */
1721 if (func->name2) {
1722 pair_name = talloc_asprintf(func, "func_%s_%s", func->name1, func->name2);
1723 cp = cf_pair_find(mctx->mi->conf, pair_name);
1724 talloc_free(pair_name);
1725 if (cp) goto found_func;
1726 }
1727 pair_name = talloc_asprintf(func, "func_%s", func->name1);
1728 cp = cf_pair_find(mctx->mi->conf, pair_name);
1729 talloc_free(pair_name);
1730 found_func:
1731 if (cp) func->function_name = cf_pair_value(cp);
1732
1733 if (python_function_load(mctx, func) < 0) goto error;
1734 func = fr_rb_iter_next_inorder(&iter);
1735 }
1736
1737 /*
1738 * Call the instantiate function.
1739 */
1740 if (inst->instantiate.function) {
1741 rlm_rcode_t rcode;
1742
1743 do_python_single(&rcode, MODULE_CTX_FROM_INST(mctx), NULL, inst->instantiate.function, "instantiate");
1744 switch (rcode) {
1745 case RLM_MODULE_FAIL:
1746 case RLM_MODULE_REJECT:
1747 error:
1748 fr_cond_assert(PyEval_SaveThread() == inst->interpreter);
1749 python_interpreter_free(inst, inst->interpreter);
1750 return -1;
1751
1752 default:
1753 break;
1754 }
1755 }
1756
1757 /*
1758 * Switch back to the global interpreter
1759 */
1760 if (!fr_cond_assert(PyEval_SaveThread() == inst->interpreter)) goto error;
1761
1762 return 0;
1763}
1764
1765static int mod_detach(module_detach_ctx_t const *mctx)
1766{
1767 rlm_python_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1768 python_func_def_t *func = NULL;
1770
1771 /*
1772 * If we don't have a interpreter
1773 * we didn't get far enough into
1774 * instantiation to generate things
1775 * we need to clean up...
1776 */
1777 if (!inst->interpreter) return 0;
1778
1779 /*
1780 * Call module destructor
1781 */
1782 PyEval_RestoreThread(inst->interpreter);
1783
1784 /*
1785 * We don't care if this fails.
1786 */
1787 if (inst->detach.function) {
1788 rlm_rcode_t rcode;
1789
1790 (void)do_python_single(&rcode, MODULE_CTX_FROM_INST(mctx), NULL, inst->detach.function, "detach");
1791 }
1792
1793#define PYTHON_FUNC_DESTROY(_x) python_function_destroy(&inst->_x)
1795 PYTHON_FUNC_DESTROY(detach);
1796
1797 func = fr_rb_iter_init_inorder(&iter, &inst->funcs);
1798 while (func) {
1800 func = fr_rb_iter_next_inorder(&iter);
1801 }
1802
1803 PyEval_SaveThread();
1804
1805 /*
1806 * Free the module specific interpreter
1807 */
1808 python_interpreter_free(inst, inst->interpreter);
1809
1810 return 0;
1811}
1812
1814{
1815 rlm_python_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1816 rlm_python_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_python_thread_t);
1817
1818 PyThreadState *t_state;
1819 PyObject *t_dict;
1820 PyObject *p_state;
1821
1822 current_t = t;
1823
1824 t->inst = inst;
1825 t_state = PyThreadState_New(inst->interpreter->interp);
1826 if (!t_state) {
1827 ERROR("Failed initialising local PyThreadState");
1828 return -1;
1829 }
1830
1831 PyEval_RestoreThread(t_state); /* Switches thread state and locks GIL */
1832 t_dict = PyThreadState_GetDict();
1833 if (unlikely(!t_dict)) {
1834 ERROR("Failed getting PyThreadState dictionary");
1835 error:
1836 PyEval_SaveThread(); /* Unlock GIL */
1837 PyThreadState_Delete(t_state);
1838
1839 return -1;
1840 }
1841
1842 /*
1843 * Instantiate a new instance object which captures
1844 * the global and thread instances, and associates
1845 * them with the thread.
1846 */
1847 p_state = PyObject_CallObject((PyObject *)&py_freeradius_state_def, NULL);
1848 if (unlikely(!p_state)) {
1849 ERROR("Failed instantiating module instance information object");
1850 goto error;
1851 }
1852
1853 if (unlikely(PyDict_SetItemString(t_dict, "__State", p_state) < 0)) {
1854 ERROR("Failed setting module instance information in thread dict");
1855 goto error;
1856 }
1857
1858 DEBUG3("Initialised PyThreadState %p", t_state);
1859 t->state = t_state;
1860 PyEval_SaveThread(); /* Unlock GIL */
1861
1862 return 0;
1863}
1864
1866{
1867 rlm_python_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_python_thread_t);
1868
1869 PyEval_RestoreThread(t->state); /* Swap in our local thread state */
1870 PyThreadState_Clear(t->state);
1871 PyEval_SaveThread();
1872
1873 PyThreadState_Delete(t->state); /* Don't need to hold lock for this */
1874
1875 return 0;
1876}
1877
1878static int libpython_init(void)
1879{
1880#define LOAD_INFO(_fmt, ...) fr_log(LOG_DST, L_INFO, __FILE__, __LINE__, "rlm_python - " _fmt, ## __VA_ARGS__)
1881#define LOAD_WARN(_fmt, ...) fr_log_perror(LOG_DST, L_WARN, __FILE__, __LINE__, \
1882 &(fr_log_perror_format_t){ \
1883 .first_prefix = "rlm_python - ", \
1884 .subsq_prefix = "rlm_python - ", \
1885 }, \
1886 _fmt, ## __VA_ARGS__)
1887 PyConfig config;
1888 PyStatus status;
1889 wchar_t *wide_name;
1890
1891 fr_assert(!Py_IsInitialized());
1892
1893 LOAD_INFO("Python version: %s", Py_GetVersion());
1894 dependency_version_number_add(NULL, "python", Py_GetVersion());
1895
1896 /*
1897 * Load python using RTLD_GLOBAL and dlopen.
1898 * This fixes issues where python C extensions
1899 * can't find the symbols they need.
1900 */
1901 python_dlhandle = dl_open_by_sym("Py_IsInitialized", RTLD_NOW | RTLD_GLOBAL);
1902 if (!python_dlhandle) LOAD_WARN("Failed loading libpython symbols into global symbol table");
1903
1904 PyConfig_InitPythonConfig(&config);
1905
1906 /*
1907 * Set program name (i.e. the software calling the interpreter)
1908 * The value of argv[0] as a wide char string
1909 */
1910 wide_name = Py_DecodeLocale(main_config->name, NULL);
1911 status = PyConfig_SetString(&config, &config.program_name, wide_name);
1912 PyMem_RawFree(wide_name);
1913
1914 if (PyStatus_Exception(status)) {
1915 fail:
1916 LOAD_WARN("%s", status.err_msg);
1917 PyConfig_Clear(&config);
1918 return -1;
1919 }
1920
1921 /*
1922 * Python 3 introduces the concept of a
1923 * "inittab", i.e. a list of modules which
1924 * are automatically created when the first
1925 * interpreter is spawned.
1926 */
1927 PyImport_AppendInittab("freeradius", python_module_init);
1928
1930 wchar_t *wide_path = Py_DecodeLocale(libpython_global_config.path, NULL);
1931
1933 /*
1934 * The path from config is to be used in addition to the default.
1935 * Set it in the pythonpath_env.
1936 */
1937 status = PyConfig_SetString(&config, &config.pythonpath_env, wide_path);
1938 } else {
1939 /*
1940 * Only the path from config is to be used.
1941 * Setting module_search_paths_set to 1 disables any automatic paths.
1942 */
1943 config.module_search_paths_set = 1;
1944 status = PyWideStringList_Append(&config.module_search_paths, wide_path);
1945 }
1946 PyMem_RawFree(wide_path);
1947 if (PyStatus_Exception(status)) goto fail;
1948 }
1949
1950 config.install_signal_handlers = 0; /* Don't override signal handlers - noop on subs calls */
1951
1952 if (libpython_global_config.verbose) config.verbose = 1; /* Enable libpython logging*/
1953
1954 LSAN_DISABLE(status = Py_InitializeFromConfig(&config));
1955 if (PyStatus_Exception(status)) goto fail;
1956
1957 PyConfig_Clear(&config);
1958
1959 global_interpreter = PyEval_SaveThread(); /* Store reference to the main interpreter and release the GIL */
1960
1961 return 0;
1962}
1963
1964static void libpython_free(void)
1965{
1966 PyThreadState_Swap(global_interpreter); /* Swap to the main thread */
1967
1968 /*
1969 * PyImport_Cleanup - Leaks memory in python 3.6
1970 * should check once we require 3.8 that this is
1971 * still needed.
1972 */
1973 LSAN_DISABLE(Py_Finalize()); /* Ignore leaks on exit, we don't reload modules so we don't care */
1974 if (python_dlhandle) dlclose(python_dlhandle); /* dlclose will SEGV on null handle */
1975}
1976
1977/*
1978 * Restrict automatic Python function names to lowercase characters, numbers and underscore
1979 * meaning that a module call in `recv Access-Request` will look for `recv_access_request`
1980 */
1981static void python_func_name_safe(char *name) {
1982 char *p;
1983 size_t i;
1984
1985 p = name;
1986 for (i = 0; i < talloc_array_length(name); i++) {
1987 *p = tolower(*p);
1988 if (!strchr("abcdefghijklmnopqrstuvwxyz1234567890", *p)) *p = '_';
1989 p++;
1990 }
1991}
1992
1993static int python_func_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, UNUSED tmpl_rules_t const *t_rules,
1994 UNUSED CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
1995{
1996 rlm_python_t *inst = talloc_get_type_abort(cec->mi->data, rlm_python_t);
1997 call_env_parsed_t *parsed;
1998 python_func_def_t *func;
1999 void *found;
2000
2001 if (!inst->funcs_init) {
2003 inst->funcs_init = true;
2004 }
2005
2006 MEM(parsed = call_env_parsed_add(ctx, out,
2008 .name = "func",
2009 .flags = CALL_ENV_FLAG_PARSE_ONLY,
2010 .pair = {
2011 .parsed = {
2012 .offset = rule->pair.offset,
2014 }
2015 }
2016 }));
2017
2018 MEM(func = talloc_zero(inst, python_func_def_t));
2019 func->name1 = talloc_strdup(func, cec->asked->name1);
2021 if (cec->asked->name2) {
2022 func->name2 = talloc_strdup(func, cec->asked->name2);
2024 }
2025
2026 if (fr_rb_find_or_insert(&found, &inst->funcs, func) < 0) {
2027 talloc_free(func);
2028 return -1;
2029 }
2030
2031 /*
2032 * If the function call is already in the tree, use that entry.
2033 */
2034 if (found) {
2035 talloc_free(func);
2036 call_env_parsed_set_data(parsed, found);
2037 } else {
2038 call_env_parsed_set_data(parsed, func);
2039 }
2040 return 0;
2041}
2042
2050
2051/*
2052 * The module name should be the only globally exported symbol.
2053 * That is, everything else should be 'static'.
2054 *
2055 * If the module needs to temporarily modify it's instantiation
2056 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
2057 * The server will then take care of ensuring that the module
2058 * is single-threaded.
2059 */
2062 .common = {
2063 .magic = MODULE_MAGIC_INIT,
2064 .name = "python",
2065
2066 .inst_size = sizeof(rlm_python_t),
2067 .thread_inst_size = sizeof(rlm_python_thread_t),
2068
2069 .config = module_config,
2070
2071 .instantiate = mod_instantiate,
2072 .detach = mod_detach,
2073
2074 .thread_instantiate = mod_thread_instantiate,
2075 .thread_detach = mod_thread_detach
2076 },
2077 .method_group = {
2078 .bindings = (module_method_binding_t[]){
2079 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_python, .method_env = &python_method_env },
2081 }
2082 }
2083};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
static int const char char buffer[256]
Definition acutest.h:576
log_entry msg
Definition acutest.h:794
va_list args
Definition acutest.h:770
#define RCSID(id)
Definition build.h:485
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:112
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
call_env_parsed_t * call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule)
Allocate a new call_env_parsed_t structure and add it to the list of parsed call envs.
Definition call_env.c:646
void call_env_parsed_set_data(call_env_parsed_t *parsed, void const *data)
Assign data to a call_env_parsed_t.
Definition call_env.c:703
#define CALL_ENV_TERMINATOR
Definition call_env.h:236
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition call_env.h:240
call_env_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:247
section_name_t const * asked
The actual name1/name2 that resolved to a module_method_binding_t.
Definition call_env.h:232
@ CALL_ENV_FLAG_PARSE_ONLY
The result of parsing will not be evaluated at runtime.
Definition call_env.h:85
@ CALL_ENV_FLAG_PARSE_MISSING
If this subsection is missing, still parse it.
Definition call_env.h:88
@ CALL_ENV_PARSE_TYPE_VOID
Output of the parsing phase is undefined (a custom structure).
Definition call_env.h:62
module_instance_t const * mi
Module instance that the callenv is registered to.
Definition call_env.h:229
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _name2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
Definition call_env.h:412
Per method call config.
Definition call_env.h:180
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:658
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:284
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:595
Common header for all CONF_* types.
Definition cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
Definition cf_util.c:631
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1170
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1027
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
Definition cf_util.c:1438
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
Definition cf_util.c:617
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:663
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1593
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1577
#define cf_item_next(_parent, _curr)
Definition cf_util.h:92
#define CF_IDENT_ANY
Definition cf_util.h:78
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#define MEM(x)
Definition debug.h:36
int dependency_version_number_add(CONF_SECTION *cs, char const *name, char const *version)
Add a library/server version pair to the main configuration.
Definition dependency.c:152
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
fr_slen_t fr_dict_attr_by_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, fr_dict_attr_t const *parent, fr_sbuff_t *name, fr_sbuff_term_t const *tt))
fr_slen_t fr_dict_attr_search_by_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, fr_dict_t const *dict_def, fr_sbuff_t *name, fr_sbuff_term_t const *tt, bool internal, bool foreign))
Locate a fr_dict_attr_t by its name in the top level namespace of a dictionary.
Definition dict_util.c:3007
Test enumeration values.
Definition dict_test.h:92
void * dl_open_by_sym(char const *sym_name, int flags)
Utility function to dlopen the library containing a particular symbol.
Definition dl.c:186
#define RTLD_NOW
Definition dl.c:44
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
char const * name
Name of library and section within global config.
Definition global_lib.h:39
#define GLOBAL_LIB_TERMINATOR
Definition global_lib.h:51
Structure to define how to initialise libraries with global configuration.
Definition global_lib.h:38
#define DEBUG3(_fmt,...)
Definition log.h:266
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:528
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RERROR(fmt,...)
Definition log.h:298
talloc_free(reap)
int fr_debug_lvl
Definition log.c:40
fr_log_t default_log
Definition log.c:292
void fr_log(fr_log_t const *log, fr_log_type_t type, char const *file, int line, char const *fmt,...)
Send a server log message to its destination.
Definition log.c:581
@ L_DBG_LVL_3
3rd highest priority debug messages (-xxx | -Xx).
Definition log.h:72
@ L_DBG_LVL_1
Highest priority debug messages (-x).
Definition log.h:70
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
Definition log.h:71
@ L_DBG_LVL_OFF
No debug messages.
Definition log.h:69
@ L_DBG_LVL_4
4th highest priority debug messages (-xxxx | -Xxx).
Definition log.h:73
@ L_DBG_LVL_MAX
Lowest priority debug messages (-xxxxx | -Xxxx).
Definition log.h:74
@ L_DBG_WARN_REQ
Less severe warning only displayed when debugging is enabled.
Definition log.h:63
@ L_WARN
Warning.
Definition log.h:57
@ L_ERR
Error message.
Definition log.h:56
@ L_DBG_ERR
Error only displayed when debugging is enabled.
Definition log.h:62
@ L_DBG_ERR_REQ
Less severe error only displayed when debugging is enabled.
Definition log.h:64
@ L_DBG_WARN
Warning only displayed when debugging is enabled.
Definition log.h:61
@ L_INFO
Informational message.
Definition log.h:55
@ L_DBG
Only displayed when debugging is enabled.
Definition log.h:59
#define LSAN_DISABLE(_x)
Definition lsan.h:41
main_config_t const * main_config
Main server configuration.
Definition main_config.c:58
char const * name
Name of the daemon, usually 'radiusd'.
Definition main_config.h:52
unsigned short uint16_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_FLOAT32
Single precision floating point.
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_INT8
8 Bit signed integer.
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
@ FR_TYPE_IPV6_PREFIX
IPv6 Prefix.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_INT64
64 Bit signed integer.
@ FR_TYPE_INT16
16 Bit signed integer.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_COMBO_IP_PREFIX
IPv4 or IPv6 address prefix depending on length.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
@ FR_TYPE_COMBO_IP_ADDR
IPv4 or IPv6 address depending on length.
@ FR_TYPE_IFID
Interface ID.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_FLOAT64
Double precision floating point.
unsigned int uint32_t
long int ssize_t
unsigned char bool
unsigned char uint8_t
unsigned long int size_t
void * env_data
Per call environment data.
Definition module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * thread
Thread specific instance data.
Definition module_ctx.h:43
module_instance_t * mi
Module instance to detach.
Definition module_ctx.h:57
void * thread
Thread instance data.
Definition module_ctx.h:67
#define MODULE_CTX_FROM_INST(_mctx)
Wrapper to create a module_ctx_t as a compound literal from a module_inst_ctx_t.
Definition module_ctx.h:138
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:64
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for detach calls.
Definition module_ctx.h:56
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
Temporary structure to hold arguments for thread_instantiation calls.
Definition module_ctx.h:63
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
fr_pair_t * fr_pair_list_parent(fr_pair_list_t const *list)
Return a pointer to the parent pair which contains this list.
Definition pair.c:960
unsigned int fr_pair_count_by_da(fr_pair_list_t const *list, fr_dict_attr_t const *da)
Return the number of instances of a given da in the specified list.
Definition pair.c:674
int fr_pair_append_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t (and append)
Definition pair.c:1472
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, UNUSED bool tainted)
Convert string value to native attribute value.
Definition pair.c:2599
fr_pair_list_t * fr_pair_parent_list(fr_pair_t const *vp)
Return a pointer to the parent pair list.
Definition pair.c:931
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:697
fr_pair_t * fr_pair_find_by_da_idx(fr_pair_list_t const *list, fr_dict_attr_t const *da, unsigned int idx)
Find a pair with a matching da at a given index.
Definition pair.c:745
int fr_pair_delete(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list and free.
Definition pair.c:1832
static const conf_parser_t config[]
Definition base.c:183
#define fr_assert(_expr)
Definition rad_assert.h:38
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define WARN(fmt,...)
Definition radclient.h:47
static rs_t * conf
Definition radsniff.c:53
void * fr_rb_iter_init_inorder(fr_rb_iter_inorder_t *iter, fr_rb_tree_t *tree)
Initialise an in-order iterator.
Definition rb.c:824
int fr_rb_find_or_insert(void **found, fr_rb_tree_t *tree, void const *data)
Attempt to find current data in the tree, if it does not exist insert it.
Definition rb.c:598
void * fr_rb_iter_next_inorder(fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:850
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
Definition rb.h:180
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:321
The main red black tree structure.
Definition rb.h:73
#define RETURN_MODULE_NOOP
Definition rcode.h:63
#define RETURN_MODULE_RCODE(_rcode)
Definition rcode.h:66
#define RETURN_MODULE_FAIL
Definition rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:45
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:46
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:41
@ RLM_MODULE_TIMEOUT
Module (or section) timed out.
Definition rcode.h:50
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:47
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:49
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:48
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:44
fr_dict_attr_t const * request_attr_request
Definition request.c:43
fr_dict_attr_t const * request_attr_control
Definition request.c:45
fr_dict_attr_t const * request_attr_state
Definition request.c:46
fr_dict_attr_t const * request_attr_reply
Definition request.c:44
static py_freeradius_state_t * rlm_python_state_obj(void)
Return the module instance object associated with the thread state or interpreter state.
Definition rlm_python.c:427
static int libpython_init(void)
static PyMethodDef py_freeradius_methods[]
Definition rlm_python.c:380
PyObject * pythonconf_dict
Configuration parameters defined in the module made available to the python script.
Definition rlm_python.c:72
static int python_parse_config(rlm_python_t const *inst, CONF_SECTION *cs, int lvl, PyObject *dict)
static int mod_detach(module_detach_ctx_t const *mctx)
static void python_function_destroy(python_func_def_t *def)
PyObject * reply
Reply list.
Definition rlm_python.c:113
static int python_module_import_constants(rlm_python_t const *inst, PyObject *module)
Import integer constants into the module we're initialising.
PyObject_HEAD PyObject * request
< Common fields needed for every python object.
Definition rlm_python.c:112
static PyTypeObject py_freeradius_value_pair_def
Contains a value pair of a specific type.
Definition rlm_python.c:272
char * name2
Section name2 where this is called.
Definition rlm_python.c:53
fr_rb_node_t node
Entry in tree of Python functions.
Definition rlm_python.c:54
static int python_func_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, UNUSED tmpl_rules_t const *t_rules, UNUSED CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
request_t * request
Current request.
Definition rlm_python.c:127
rlm_python_thread_t * t
Thread-specific python instance.
Definition rlm_python.c:126
PyObject_HEAD rlm_python_t const * inst
< Common fields needed for every python object.
Definition rlm_python.c:125
python_func_def_t detach
Definition rlm_python.c:70
static rlm_python_thread_t * current_t
Used for communicating with object init function.
Definition rlm_python.c:135
static int py_freeradius_pair_map_set(PyObject *self, PyObject *attr, PyObject *value)
Set the value of a pair.
Definition rlm_python.c:668
#define A(x)
static unlang_action_t mod_python(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Thread safe call to a python function.
static PyGetSetDef py_freeradius_pair_getset[]
How to access "value" attribute of a pair.
Definition rlm_python.c:258
PyObject *char const * def_module_name
< Local, interpreter specific module.
Definition rlm_python.c:64
static PyObject * pair_list_alloc(request_t *request, fr_dict_attr_t const *list)
Create the Python object representing a pair list.
static global_lib_autoinst_t rlm_python_autoinst
Definition rlm_python.c:163
static void rlm_python_set_request(request_t *request)
Set the request associated with the current thread state.
Definition rlm_python.c:473
static rlm_python_t const * rlm_python_get_inst(void)
Return the rlm_python instance associated with the current interpreter.
Definition rlm_python.c:448
static rlm_python_t const * current_inst
Used for communication with inittab functions.
Definition rlm_python.c:133
static void libpython_free(void)
PyThreadState * interpreter
The interpreter used for this instance of rlm_python.
Definition rlm_python.c:62
python_func_def_t * func
Definition rlm_python.c:86
static PyObject * py_freeradius_pair_str(PyObject *self)
Return the string representation of a leaf pair node.
PyObject * control
Control list.
Definition rlm_python.c:114
module_rlm_t rlm_python
static int py_freeradius_state_init(PyObject *self, UNUSED PyObject *args, UNUSED PyObject *kwds)
Definition rlm_python.c:504
static struct @180 freeradius_constants[]
static void python_obj_destroy(PyObject **ob)
static int python_module_import_config(rlm_python_t *inst, CONF_SECTION *conf, PyObject *module)
Make the current instance's config available within the module we're initialising.
static request_t * rlm_python_get_request(void)
Return the request associated with the current thread state.
Definition rlm_python.c:460
static void python_func_name_safe(char *name)
rlm_python_t const * inst
Current module instance data.
Definition rlm_python.c:95
char const * module_name
String name of module.
Definition rlm_python.c:50
PyThreadState * state
Module instance/thread specific state.
Definition rlm_python.c:96
PyObject * parent
Parent object of this pair.
Definition rlm_python.c:107
char const * name
Name of the module instance.
Definition rlm_python.c:61
static PyTypeObject py_freeradius_request_def
Definition rlm_python.c:351
static PyThreadState * global_interpreter
Our first interpreter.
Definition rlm_python.c:131
static PyObject * py_freeradius_pair_map_subscript(PyObject *self, PyObject *attr)
Returns a freeradius.Pair.
Definition rlm_python.c:564
bool path_include_default
Include the default python path in path
Definition rlm_python.c:81
static int py_freeradius_pair_setvalue(PyObject *self, PyObject *value, void *closure)
#define LOAD_WARN(_fmt,...)
static int8_t python_func_def_cmp(void const *one, void const *two)
How to compare two Python calls.
Definition rlm_python.c:411
static PyObject * python_module_init(void)
PyObject *PyObject * function
< Python reference to module.
Definition rlm_python.c:48
char * name1
Section name1 where this is called.
Definition rlm_python.c:52
static PyObject * py_freeradius_attribute_instance(PyObject *self, PyObject *attr)
Returns a specific instance of freeradius.Pair.
Definition rlm_python.c:521
static conf_parser_t const python_global_config[]
Definition rlm_python.c:153
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
static void python_interpreter_free(rlm_python_t *inst, PyThreadState *interp)
fr_pair_t * vp
Real FreeRADIUS pair for this Python pair.
Definition rlm_python.c:105
static PyTypeObject py_freeradius_grouping_pair_def
Contains group attribute of a specific type.
Definition rlm_python.c:292
static int python_function_load(module_inst_ctx_t const *mctx, python_func_def_t *def)
Import a user module and load a function from it.
static PyObject * py_freeradius_log(UNUSED PyObject *self, PyObject *args, PyObject *kwds)
Allow fr_log to be called from python.
Definition rlm_python.c:486
#define _PyCFunction_CAST(func)
Definition rlm_python.c:374
static libpython_global_config_t libpython_global_config
Definition rlm_python.c:148
char const * function_name
String name of function in module.
Definition rlm_python.c:51
bool verbose
Enable libpython verbose logging.
Definition rlm_python.c:82
static fr_pair_t * py_freeradius_build_parents(PyObject *obj)
Build out missing parent pairs when a leaf node is assigned a value.
Definition rlm_python.c:624
bool funcs_init
Has the tree been initialised.
Definition rlm_python.c:66
static CONF_SECTION * current_conf
Used for communication with inittab functions.
Definition rlm_python.c:134
static PyObject * py_freeradius_pair_getvalue(PyObject *self, void *closure)
static PyMemberDef py_freeradius_request_attrs[]
Definition rlm_python.c:319
static unlang_action_t do_python_single(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request, PyObject *p_func, char const *funcname)
unsigned int idx
Instance index.
Definition rlm_python.c:106
PyObject * state
Session state list.
Definition rlm_python.c:115
#define LOAD_INFO(_fmt,...)
#define PYTHON_FUNC_LOAD(_x)
static PyModuleDef py_freeradius_def
Definition rlm_python.c:400
static void * python_dlhandle
Definition rlm_python.c:130
static PyTypeObject py_freeradius_pair_def
The class which all pair types inherit from.
Definition rlm_python.c:245
static int python_interpreter_init(module_inst_ctx_t const *mctx)
static const call_env_method_t python_method_env
char const * path
Path to search for python files in.
Definition rlm_python.c:80
#define PYTHON_FUNC_DESTROY(_x)
static PyTypeObject py_freeradius_state_def
Definition rlm_python.c:362
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
static int mod_instantiate(module_inst_ctx_t const *mctx)
global_lib_autoinst_t const *const rlm_python_lib[]
Definition rlm_python.c:172
static conf_parser_t module_config[]
Definition rlm_python.c:190
fr_rb_tree_t funcs
Tree of function calls found by call_env parser.
Definition rlm_python.c:65
static void python_error_log(rlm_python_t const *inst, request_t *request)
Print out the current error.
python_func_def_t instantiate
Definition rlm_python.c:69
static PyTypeObject py_freeradius_pair_list_def
Each instance contains a top level list (i.e.
Definition rlm_python.c:307
PyObject_HEAD fr_dict_attr_t const * da
< Common fields needed for every python object.
Definition rlm_python.c:104
Global config for python library.
Definition rlm_python.c:79
Additional fields for pairs.
Definition rlm_python.c:102
Wrapper around a python instance.
Definition rlm_python.c:123
Specifies the module.function to load for processing a section.
Definition rlm_python.c:46
An instance of the rlm_python module.
Definition rlm_python.c:60
Tracks a python module inst/thread state pair.
Definition rlm_python.c:94
static char const * name
static int instantiate(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1313
#define FR_SBUFF_IN(_start, _len_or_end)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:40
char const * name2
Second section name. Usually a packet type like 'access-request', 'access-accept',...
Definition section.h:46
char const * name1
First section name. Usually a verb like 'recv', 'send', etc...
Definition section.h:45
char const * name
Instance name e.g. user_database.
Definition module.h:336
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:330
size_t inst_size
Size of the module's instance data.
Definition module.h:204
void * data
Module's instance data.
Definition module.h:272
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:151
Named methods exported by a module.
Definition module.h:173
fr_pair_list_t * tmpl_list_head(request_t *request, fr_dict_attr_t const *list)
Resolve attribute fr_pair_list_t value to an attribute list.
Definition tmpl_eval.c:70
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:332
return count
Definition module.c:155
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:229
static fr_slen_t parent
Definition pair.h:845
#define fr_type_is_structural(_x)
Definition types.h:388
#define FR_TYPE_NON_LEAF
Definition types.h:314
#define fr_type_is_leaf(_x)
Definition types.h:389
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:450
#define fr_type_is_struct(_x)
Definition types.h:369
ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules)
Print one boxed value to a string.
Definition value.c:5496
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:3899
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:3945
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition value.c:4379
int fr_value_box_memdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Copy a buffer to a fr_value_box_t.
Definition value.c:4620
static size_t char ** out
Definition value.h:1020