The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_python.c
Go to the documentation of this file.
1/*
2 * This program 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: 0317d920f4d8faf6e1e5f67b8f8961252b6b672b $
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: 0317d920f4d8faf6e1e5f67b8f8961252b6b672b $")
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/util/debug.h>
34#include <freeradius-devel/util/lsan.h>
35
36#include <Python.h>
37#include <structmember.h>
38#include <frameobject.h> /* Python header not pulled in by default. */
39#include <libgen.h>
40#include <dlfcn.h>
41
42/** Specifies the module.function to load for processing a section
43 *
44 */
45typedef struct {
46 PyObject *module; //!< Python reference to module.
47 PyObject *function; //!< Python reference to function in module.
48
49 char const *module_name; //!< String name of module.
50 char const *function_name; //!< String name of function in module.
51 char *name1; //!< Section name1 where this is called.
52 char *name2; //!< Section name2 where this is called.
53 fr_rb_node_t node; //!< Entry in tree of Python functions.
55
56/** An instance of the rlm_python module
57 *
58 */
59typedef struct {
60 char const *name; //!< Name of the module instance
61 PyThreadState *interpreter; //!< The interpreter used for this instance of rlm_python.
62 PyObject *module; //!< Local, interpreter specific module.
63 char const *def_module_name; //!< Default module for Python functions
64 fr_rb_tree_t funcs; //!< Tree of function calls found by call_env parser
65 bool funcs_init; //!< Has the tree been initialised.
66
70
71 PyObject *pythonconf_dict; //!< Configuration parameters defined in the module
72 //!< made available to the python script.
74
75/** Global config for python library
76 *
77 */
78typedef struct {
79 char const *path; //!< Path to search for python files in.
80 bool path_include_default; //!< Include the default python path in `path`
81 bool verbose; //!< Enable libpython verbose logging
83
87
88/** Tracks a python module inst/thread state pair
89 *
90 * Multiple instances of python create multiple interpreters and each
91 * thread must have a PyThreadState per interpreter, to track execution.
92 */
93typedef struct {
94 rlm_python_t const *inst; //!< Current module instance data.
95 PyThreadState *state; //!< Module instance/thread specific state.
97
98/** Additional fields for pairs
99 *
100 */
101typedef struct {
102 PyObject_HEAD //!< Common fields needed for every python object.
103 fr_dict_attr_t const *da; //!< dictionary attribute for this pair.
104 fr_pair_t *vp; //!< Real FreeRADIUS pair for this Python pair.
105 unsigned int idx; //!< Instance index.
106 PyObject *parent; //!< Parent object of this pair.
108
109typedef struct {
110 PyObject_HEAD //!< Common fields needed for every python object.
111 PyObject *request; //!< Request list.
112 PyObject *reply; //!< Reply list.
113 PyObject *control; //!< Control list.
114 PyObject *state; //!< Session state list.
116
117/** Wrapper around a python instance
118 *
119 * This is added to the FreeRADIUS module to allow us to
120 * get at the global and thread local instance data.
121 */
122typedef struct {
123 PyObject_HEAD //!< Common fields needed for every python object.
124 rlm_python_t const *inst; //!< Module instance.
125 rlm_python_thread_t *t; //!< Thread-specific python instance.
126 request_t *request; //!< Current request.
128
129static void *python_dlhandle;
130static PyThreadState *global_interpreter; //!< Our first interpreter.
131
132static rlm_python_t const *current_inst = NULL; //!< Used for communication with inittab functions.
133static CONF_SECTION *current_conf; //!< Used for communication with inittab functions.
134static rlm_python_thread_t *current_t; //!< Used for communicating with object init function.
135
136static PyObject *py_freeradius_log(UNUSED PyObject *self, PyObject *args, PyObject *kwds);
137
138static int py_freeradius_state_init(PyObject *self, UNUSED PyObject *args, UNUSED PyObject *kwds);
139
140static PyObject *py_freeradius_pair_map_subscript(PyObject *self, PyObject *attr);
141static PyObject *py_freeradius_attribute_instance(PyObject *self, PyObject *attr);
142static int py_freeradius_pair_map_set(PyObject* self, PyObject* attr, PyObject* value);
143static PyObject *py_freeradius_pair_getvalue(PyObject *self, void *closure);
144static int py_freeradius_pair_setvalue(PyObject *self, PyObject *value, void *closure);
145static PyObject *py_freeradius_pair_str(PyObject *self);
146
148 .path = NULL,
149 .path_include_default = true
150};
151
153 { FR_CONF_OFFSET("path", libpython_global_config_t, path) },
154 { FR_CONF_OFFSET("path_include_default", libpython_global_config_t, path_include_default) },
155 { FR_CONF_OFFSET("verbose", libpython_global_config_t, verbose) },
157};
158
159static int libpython_init(void);
160static void libpython_free(void);
161
163 .name = "python",
164 .config = python_global_config,
165 .init = libpython_init,
166 .free = libpython_free,
168};
169
170extern global_lib_autoinst_t const * const rlm_python_lib[];
175
176/*
177 * As of Python 3.8 the GIL will be per-interpreter
178 * If there are still issues with CEXTs,
179 * multiple interpreters and the GIL at that point
180 * users can build rlm_python against Python 3.8
181 * and the horrible hack of using a single interpreter
182 * for all instances of rlm_python will no longer be
183 * required.
184 */
185
186/*
187 * A mapping of configuration file names to internal variables.
188 */
190
191#define A(x) { FR_CONF_OFFSET("mod_" #x, rlm_python_t, x.module_name), .dflt = "${.module}" }, \
192 { FR_CONF_OFFSET("func_" #x, rlm_python_t, x.function_name) },
193
194 A(instantiate)
195 A(detach)
196
197#undef A
198
199 { FR_CONF_OFFSET("module", rlm_python_t, def_module_name) },
200
202};
203
204static struct {
205 char const *name;
206 int value;
208
209#define A(x) { #x, x },
210
211 A(L_DBG)
212 A(L_WARN)
213 A(L_INFO)
214 A(L_ERR)
215 A(L_WARN)
217 A(L_DBG_ERR)
236#undef A
237
238 { NULL, 0 },
240
241/** The class which all pair types inherit from
242 *
243 */
244static PyTypeObject py_freeradius_pair_def = {
245 PyVarObject_HEAD_INIT(NULL, 0)
246 .tp_name = "freeradius.Pair",
247 .tp_doc = "An attribute value pair",
248 .tp_basicsize = sizeof(py_freeradius_pair_t),
249 .tp_itemsize = 0,
250 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
251 .tp_new = PyType_GenericNew,
252};
253
254/** How to access "value" attribute of a pair
255 *
256 */
257static PyGetSetDef py_freeradius_pair_getset[] = {
258 {
259 .name = "value",
262 .doc = "Pair value",
263 .closure = NULL
264 },
265 { .name = NULL } /* Terminator */
266};
267
268/** Contains a value pair of a specific type
269 *
270 */
271static PyTypeObject py_freeradius_value_pair_def = {
272 PyVarObject_HEAD_INIT(NULL, 0)
273 .tp_name = "freeradius.ValuePair",
274 .tp_doc = "A value pair, i.e. one of the type string, integer, ipaddr etc...)",
275 .tp_flags = Py_TPFLAGS_DEFAULT,
276 .tp_base = &py_freeradius_pair_def,
277 .tp_getset = py_freeradius_pair_getset,
278 .tp_str = py_freeradius_pair_str,
279 .tp_as_mapping = &(PyMappingMethods) {
280 .mp_subscript = py_freeradius_attribute_instance,
281 .mp_ass_subscript = py_freeradius_pair_map_set,
282 }
283};
284
285/** Contains group attribute of a specific type
286 *
287 * Children of this attribute may be accessed using the map protocol
288 * i.e. foo['child-of-foo'].
289 *
290 */
291static PyTypeObject py_freeradius_grouping_pair_def = {
292 PyVarObject_HEAD_INIT(NULL, 0)
293 .tp_name = "freeradius.GroupingPair",
294 .tp_doc = "A grouping pair, i.e. one of the type group, tlv, vsa or vendor. "
295 "Children are accessible via the mapping protocol i.e. foo['child-of-foo]",
296 .tp_flags = Py_TPFLAGS_DEFAULT,
297 .tp_base = &py_freeradius_pair_def,
298 .tp_as_mapping = &(PyMappingMethods){
299 .mp_subscript = py_freeradius_pair_map_subscript,
300 .mp_ass_subscript = py_freeradius_pair_map_set,
301 }
302};
303
304/** Each instance contains a top level list (i.e. request, reply, control, session-state)
305 */
306static PyTypeObject py_freeradius_pair_list_def = {
307 PyVarObject_HEAD_INIT(NULL, 0)
308 .tp_name = "freeradius.PairList",
309 .tp_doc = "A list of objects of freeradius.GroupingPairList and freeradius.ValuePair",
310 .tp_flags = Py_TPFLAGS_DEFAULT,
311 .tp_base = &py_freeradius_pair_def,
312 .tp_as_mapping = &(PyMappingMethods){
313 .mp_subscript = py_freeradius_pair_map_subscript,
314 .mp_ass_subscript = py_freeradius_pair_map_set,
315 }
316};
317
318static PyMemberDef py_freeradius_request_attrs[] = {
319 {
320 .name = "request",
321 .type = T_OBJECT,
322 .offset = offsetof(py_freeradius_request_t, request),
323 .flags = READONLY,
324 .doc = "Pairs in the request list - received from the network"
325 },
326 {
327 .name = "reply",
328 .type = T_OBJECT,
329 .offset = offsetof(py_freeradius_request_t, reply),
330 .flags = READONLY,
331 .doc = "Pairs in the reply list - sent to the network"
332 },
333 {
334 .name = "control",
335 .type = T_OBJECT,
336 .offset = offsetof(py_freeradius_request_t, control),
337 .flags = READONLY,
338 .doc = "Pairs in the control list - control the behaviour of subsequently called modules"
339 },
340 {
341 .name = "session-state",
342 .type = T_OBJECT,
343 .offset = offsetof(py_freeradius_request_t, state),
344 .flags = READONLY,
345 .doc = "Pairs in the session-state list - persists for the length of the session"
346 },
347 { .name = NULL } /* Terminator */
348};
349
350static PyTypeObject py_freeradius_request_def = {
351 PyVarObject_HEAD_INIT(NULL, 0)
352 .tp_name = "freeradius.Request",
353 .tp_doc = "freeradius request handle",
354 .tp_basicsize = sizeof(py_freeradius_request_t),
355 .tp_itemsize = 0,
356 .tp_flags = Py_TPFLAGS_DEFAULT,
357 .tp_new = PyType_GenericNew,
358 .tp_members = py_freeradius_request_attrs
359};
360
361static PyTypeObject py_freeradius_state_def = {
362 PyVarObject_HEAD_INIT(NULL, 0)
363 .tp_name = "freeradius.State",
364 .tp_doc = "Private state data",
365 .tp_basicsize = sizeof(py_freeradius_state_t),
366 .tp_itemsize = 0,
367 .tp_flags = Py_TPFLAGS_DEFAULT,
368 .tp_new = PyType_GenericNew,
369 .tp_init = py_freeradius_state_init
370};
371
372#ifndef _PyCFunction_CAST
373#define _PyCFunction_CAST(func) (PyCFunction)((void(*)(void))(func))
374#endif
375
376/*
377 * freeradius Python functions
378 */
379static PyMethodDef py_freeradius_methods[] = {
380 { "log", _PyCFunction_CAST(py_freeradius_log), METH_VARARGS | METH_KEYWORDS,
381 "freeradius.log(msg[, type, lvl])\n\n"
382 "Print a message using the freeradius daemon's logging system.\n"
383 "type should be one of the following constants:\n"
384 " freeradius.L_DBG\n"
385 " freeradius.L_INFO\n"
386 " freeradius.L_WARN\n"
387 " freeradius.L_ERR\n"
388 "lvl should be one of the following constants:\n"
389 " freeradius.L_DBG_LVL_OFF\n"
390 " freeradius.L_DBG_LVL_1\n"
391 " freeradius.L_DBG_LVL_2\n"
392 " freeradius.L_DBG_LVL_3\n"
393 " freeradius.L_DBG_LVL_4\n"
394 " freeradius.L_DBG_LVL_MAX\n"
395 },
396 { NULL, NULL, 0, NULL },
397};
398
399static PyModuleDef py_freeradius_def = {
400 PyModuleDef_HEAD_INIT,
401 .m_name = "freeradius",
402 .m_doc = "FreeRADIUS python module",
403 .m_size = 0,
404 .m_methods = py_freeradius_methods
405};
406
407/** How to compare two Python calls
408 *
409 */
410static int8_t python_func_def_cmp(void const *one, void const *two)
411{
412 python_func_def_t const *a = one, *b = two;
413 int ret;
414
415 ret = strcmp(a->name1, b->name1);
416 if (ret != 0) return CMP(ret, 0);
417 if (!a->name2 && !b->name2) return 0;
418 if (!a->name2 || !b->name2) return a->name2 ? 1 : -1;
419 ret = strcmp(a->name2, b->name2);
420 return CMP(ret, 0);
421}
422
423/** Return the module instance object associated with the thread state or interpreter state
424 *
425 */
426static inline CC_HINT(always_inline) py_freeradius_state_t *rlm_python_state_obj(void)
427{
428 PyObject *dict;
429
430 dict = PyThreadState_GetDict(); /* If this is NULL, we're dealing with the main interpreter */
431 if (!dict) {
432 PyObject *module;
433
434 module = PyState_FindModule(&py_freeradius_def);
435 if (unlikely(!module)) return NULL;
436
437 dict = PyModule_GetDict(module);
438 if (unlikely(!dict)) return NULL;
439 }
440
441 return (py_freeradius_state_t *)PyDict_GetItemString(dict, "__State");
442}
443
444/** Return the rlm_python instance associated with the current interpreter
445 *
446 */
448{
449 py_freeradius_state_t const *p_state;
450
451 p_state = rlm_python_state_obj();
452 if (unlikely(!p_state)) return NULL;
453 return p_state->inst;
454}
455
456/** Return the request associated with the current thread state
457 *
458 */
460{
461 py_freeradius_state_t const *p_state;
462
463 p_state = rlm_python_state_obj();
464 if (unlikely(!p_state)) return NULL;
465
466 return p_state->request;
467}
468
469/** Set the request associated with the current thread state
470 *
471 */
473{
474 py_freeradius_state_t *p_state;
475
476 p_state = rlm_python_state_obj();
477 if (unlikely(!p_state)) return;
478
479 p_state->request = request;
480}
481
482/** Allow fr_log to be called from python
483 *
484 */
485static PyObject *py_freeradius_log(UNUSED PyObject *self, PyObject *args, PyObject *kwds)
486{
487 static char const *kwlist[] = { "msg", "type", "lvl", NULL };
488 char *msg;
489 int type = L_DBG;
490 int lvl = L_DBG_LVL_2;
492
493 if (fr_debug_lvl < lvl) Py_RETURN_NONE; /* Don't bother parsing args */
494
495 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ii", (char **)((uintptr_t)kwlist),
496 &msg, &type, &lvl)) Py_RETURN_NONE;
497
498 fr_log(&default_log, type, __FILE__, __LINE__, "rlm_python (%s) - %s", inst->name, msg);
499
500 Py_RETURN_NONE;
501}
502
503static int py_freeradius_state_init(PyObject *self, UNUSED PyObject *args, UNUSED PyObject *kwds)
504{
506 rlm_python_t const *inst;
507
509 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 */
510 our_self->inst = inst = current_t ? current_t->inst : current_inst;
511 DEBUG3("Populating __State data with %p/%p", our_self->inst, our_self->t);
512
513 return 0;
514}
515
516/** Returns a specific instance of freeradius.Pair
517 *
518 * Called when a numeric index is used on a PairGroup or PairValue as part of an object getter
519 */
520static PyObject *py_freeradius_attribute_instance(PyObject *self, PyObject *attr)
521{
522 long idx;
523 py_freeradius_pair_t *pair, *init_pair = (py_freeradius_pair_t *)self;
524
525 if (!PyLong_CheckExact(attr)) Py_RETURN_NONE;
526 idx = PyLong_AsLong(attr);
527
528 if (idx < 0) {
529 PyErr_SetString(PyExc_AttributeError, "Cannot use negative attribute instance values");
530 return NULL;
531 }
532 if (idx == 0) return self;
533
534 if (fr_type_is_leaf(init_pair->da->type)) {
535 pair = PyObject_New(py_freeradius_pair_t, (PyTypeObject *)&py_freeradius_value_pair_def);
536
537 } else if (fr_type_is_struct(init_pair->da->type)) {
538 pair = PyObject_New(py_freeradius_pair_t, (PyTypeObject *)&py_freeradius_grouping_pair_def);
539 } else {
540 PyErr_SetString(PyExc_AttributeError, "Unsupported data type");
541 return NULL;
542 }
543 if (!pair) {
544 PyErr_SetString(PyExc_MemoryError, "Failed to allocate PyObject");
545 return NULL;
546 };
547 pair->parent = init_pair->parent;
548 Py_INCREF(init_pair->parent);
549 pair->da = init_pair->da;
550 pair->idx = idx;
551 if (init_pair->vp) pair->vp = fr_pair_find_by_da_idx(fr_pair_parent_list(init_pair->vp), pair->da, (unsigned int)idx);
552 return (PyObject *)pair;
553}
554
555/** Returns a freeradius.Pair
556 *
557 * Called when pair["attr"] or pair["attr"][n] is accessed.
558 * When pair["attr"] is accessed, `self` is `pair` - which is the list or a pair group object
559 * When pair["attr"][n] is accessed, `self` is pair["attr"]
560 *
561 * Either a group object or pair object will be returned.
562 */
563static PyObject *py_freeradius_pair_map_subscript(PyObject *self, PyObject *attr)
564{
565 py_freeradius_pair_t *our_self = (py_freeradius_pair_t *)self;
566 char const *attr_name;
567 ssize_t len;
570 fr_dict_attr_t const *da;
571 fr_pair_list_t *list = NULL;
572
573 /*
574 * If we have a numeric subscript, find the nth instance of the pair.
575 */
576 if (PyLong_CheckExact(attr)) return py_freeradius_attribute_instance(self, attr);
577
578 if (!PyUnicode_CheckExact(attr)) {
579 PyErr_Format(PyExc_AttributeError, "Invalid type '%s' for map attribute",
580 ((PyTypeObject *)PyObject_Type(attr))->tp_name);
581 return NULL;
582 }
583 attr_name = PyUnicode_AsUTF8AndSize(attr, &len);
584
585 if (PyObject_IsInstance(self, (PyObject *)&py_freeradius_pair_list_def)) {
586 fr_dict_attr_search_by_name_substr(NULL, &da, request->proto_dict, &FR_SBUFF_IN(attr_name, len),
587 NULL, true, false);
588 } else {
589 fr_dict_attr_by_name_substr(NULL, &da, our_self->da, &FR_SBUFF_IN(attr_name, len), NULL);
590 }
591 if (our_self->vp) list = &our_self->vp->vp_group;
592
593 if (!da) {
594 PyErr_Format(PyExc_AttributeError, "Invalid attribute name '%.*s'", (int)len, attr_name);
595 return NULL;
596 }
597
598 if (fr_type_is_leaf(da->type)) {
599 pair = PyObject_New(py_freeradius_pair_t, (PyTypeObject *)&py_freeradius_value_pair_def);
600 } else if (fr_type_is_structural(da->type)) {
601 pair = PyObject_New(py_freeradius_pair_t, (PyTypeObject *)&py_freeradius_grouping_pair_def);
602 } else {
603 PyErr_SetString(PyExc_AttributeError, "Unsupported data type");
604 return NULL;
605 }
606 if (!pair) {
607 PyErr_SetString(PyExc_MemoryError, "Failed to allocate PyObject");
608 return NULL;
609 };
610
611 pair->parent = self;
612 Py_INCREF(self);
613 pair->da = da;
614 pair->vp = list ? fr_pair_find_by_da(list, NULL, da) : NULL;
615 pair->idx = 0;
616
617 return (PyObject *)pair;
618}
619
620/** Build out missing parent pairs when a leaf node is assigned a value.
621 *
622 */
624{
626 fr_pair_t *parent = ((py_freeradius_pair_t *)obj_pair->parent)->vp;
627
628 if (!parent) {
630 if (!parent) return NULL;
631 }
632
633 /*
634 * Asked to populate foo[n] - check that we have n instances already
635 */
636 if (obj_pair->idx > 0) {
637 unsigned int count = fr_pair_count_by_da(&parent->vp_group, obj_pair->da);
638 if (count < obj_pair->idx) {
639 PyErr_Format(PyExc_AttributeError, "Attempt to set instance %d when only %d exist", obj_pair->idx, count);
640 return NULL;
641 }
642 }
643 fr_pair_append_by_da(parent, &obj_pair->vp, &parent->vp_group, obj_pair->da);
644
645 return obj_pair->vp;
646}
647
648/** Set the value of a pair
649 *
650 * Called with two Python syntaxes
651 *
652 * - request['foo'] = 'baa'
653 * `self` will be the parent object - either the list or parent structural object.
654 * `attr` is the value in [].
655 * `value` is what we want to set the pair to.
656 *
657 * - request['foo'][n] = 'baa'
658 * `self` will be the first instance of the attribute `foo`
659 * `attr` will be the instance number
660 * `value` is what we want to set the pair to.
661 *
662 * We expect `value` to be a UTF8 string object.
663 *
664 * Due to Python "magic" this is also called when `del request['foo']` happens - only with
665 * value as NULL.
666 */
667static int py_freeradius_pair_map_set(PyObject* self, PyObject* attr, PyObject* value)
668{
669 fr_pair_list_t *list = NULL;
671 py_freeradius_pair_t *our_self = (py_freeradius_pair_t *)self;
672 fr_pair_t *vp = NULL;
673 char const *vstr;
674 ssize_t vlen;
675 bool del = (value ? false : true);
676
677 if (value && !PyUnicode_CheckExact(value)) {
678 PyErr_Format(PyExc_AttributeError, "Invalid value type '%s'", ((PyTypeObject *)PyObject_Type(value))->tp_name);
679 return -1;
680 }
681
682 /*
683 * list['attr'] = 'value'
684 * Look up DA represented by 'attr' and find pair or create pair to update
685 */
686 if (PyUnicode_CheckExact(attr)) {
687 char const *attr_name;
688 ssize_t len;
689 fr_dict_attr_t const *da = NULL;
690
691 attr_name = PyUnicode_AsUTF8AndSize(attr, &len);
692
693 if (PyObject_IsInstance(self, (PyObject *)&py_freeradius_pair_list_def)) {
694 fr_dict_attr_search_by_name_substr(NULL, &da, request->proto_dict, &FR_SBUFF_IN(attr_name, len),
695 NULL, true, false);
696 } else {
697 fr_dict_attr_by_name_substr(NULL, &da, our_self->da, &FR_SBUFF_IN(attr_name, len), NULL);
698 }
699
700 if (!da) {
701 PyErr_Format(PyExc_AttributeError, "Invalid attribute name %.*s", (int)len, attr_name);
702 return -1;
703 }
704
705 // `self` is the parent of the pair we're building - so we build parents to that point.
706 if (!our_self->vp) {
707 if (del) return 0; // If we're deleting then no need to build parents.
708 our_self->vp = py_freeradius_build_parents(self);
709 }
710 if (!our_self->vp) return -1;
711
712 list = &our_self->vp->vp_group;
713
714 vp = fr_pair_find_by_da(list, NULL, da);
715 if (del) goto del;
716
717 if (!vp) {
718 if (fr_pair_append_by_da(fr_pair_list_parent(list), &vp, list, da) < 0) {
719 PyErr_Format(PyExc_MemoryError, "Failed to add attribute %s", da->name);
720 return -1;
721 }
722 } else {
724 }
725
726 /*
727 * list['attr'][n] = 'value'
728 * Look for instance n, creating if necessary
729 */
730 } else if (PyLong_CheckExact(attr)) {
731 long idx = PyLong_AsLong(attr);
733
734 if (idx < 0) {
735 PyErr_SetString(PyExc_AttributeError, "Cannot use negative attribute instance values");
736 return -1;
737 }
738
739 if (!parent->vp) {
740 if (del) return 0;
742 }
743 if (!parent->vp) return -1;
744
745 list = &parent->vp->vp_group;
746
747 if (idx == 0) {
748 if (!our_self->vp) {
749 if (fr_pair_append_by_da(fr_pair_list_parent(list), &our_self->vp, list, our_self->da) < 0) {
750 PyErr_Format(PyExc_MemoryError, "Failed to add attribute %s", our_self->da->name);
751 return -1;
752 }
753 } else {
754 fr_value_box_clear_value(&our_self->vp->data);
755 }
756 vp = our_self->vp;
757 if (del) goto del;
758 } else {
759 vp = fr_pair_find_by_da_idx(list, our_self->da, idx);
760 if (del) goto del;
761 if (!vp) {
762 unsigned int count = fr_pair_count_by_da(list, our_self->da);
763 if (count < idx) {
764 PyErr_Format(PyExc_AttributeError, "Attempt to set instance %ld when only %d exist", idx, count);
765 return -1;
766 }
767 if (fr_pair_append_by_da(fr_pair_list_parent(list), &vp, list, our_self->da) < 0) {
768 PyErr_Format(PyExc_MemoryError, "Failed to add attribute %s", our_self->da->name);
769 return -1;
770 }
771 }
772 }
773 } else {
774 PyErr_Format(PyExc_AttributeError, "Invalid object type '%s'", ((PyTypeObject *)PyObject_Type(attr))->tp_name);
775 return -1;
776 }
777
779
780 vstr = PyUnicode_AsUTF8AndSize(value, &vlen);
781
782 if (fr_pair_value_from_str(vp, vstr, vlen, NULL, false) < 0) {
783 PyErr_Format(PyExc_AttributeError, "Failed setting '%s' = '%.*s", vp->da->name, (int)vlen, vstr);
784 fr_pair_delete(list, vp);
785 return -1;
786 }
787
788 RDEBUG2("set %pP", vp);
789 return 0;
790
791del:
792 if (vp) {
793 RDEBUG2("delete %pP", vp);
794 fr_pair_delete(list, vp);
795 }
796 return 0;
797}
798
799/** Return a native Python object of the appropriate type for leaf pair objects
800 *
801 * Accessed as `request['attr'].value`
802 *
803 * `self` is the Python leaf pair object
804 */
805static PyObject *py_freeradius_pair_getvalue(PyObject *self, UNUSED void *closure)
806{
807 py_freeradius_pair_t *own_self = (py_freeradius_pair_t *)self;
808 PyObject *value = NULL;
809 fr_pair_t *vp = own_self->vp;
810
811 if (!vp) Py_RETURN_NONE;
812
813 switch(vp->vp_type) {
814 case FR_TYPE_STRING:
815 value = PyUnicode_FromStringAndSize(vp->vp_strvalue, vp->vp_length);
816 break;
817
818 case FR_TYPE_OCTETS:
819 value = PyBytes_FromStringAndSize((char const *)vp->vp_octets, vp->vp_length);
820 break;
821
822 case FR_TYPE_BOOL:
823 value = PyBool_FromLong(vp->vp_bool);
824 break;
825
826 case FR_TYPE_UINT8:
827 value = PyLong_FromUnsignedLong(vp->vp_uint8);
828 break;
829
830 case FR_TYPE_UINT16:
831 value = PyLong_FromUnsignedLong(vp->vp_uint16);
832 break;
833
834 case FR_TYPE_UINT32:
835 value = PyLong_FromUnsignedLong(vp->vp_uint32);
836 break;
837
838 case FR_TYPE_UINT64:
839 value = PyLong_FromUnsignedLongLong(vp->vp_uint64);
840 break;
841
842 case FR_TYPE_INT8:
843 value = PyLong_FromLong(vp->vp_int8);
844 break;
845
846 case FR_TYPE_INT16:
847 value = PyLong_FromLong(vp->vp_int16);
848 break;
849
850 case FR_TYPE_INT32:
851 value = PyLong_FromLong(vp->vp_int32);
852 break;
853
854 case FR_TYPE_INT64:
855 value = PyLong_FromLongLong(vp->vp_int64);
856 break;
857
858 case FR_TYPE_FLOAT32:
859 value = PyFloat_FromDouble((double) vp->vp_float32);
860 break;
861
862 case FR_TYPE_FLOAT64:
863 value = PyFloat_FromDouble(vp->vp_float64);
864 break;
865
866 case FR_TYPE_SIZE:
867 value = PyLong_FromSize_t(vp->vp_size);
868 break;
869
871 case FR_TYPE_DATE:
872 case FR_TYPE_IFID:
879 case FR_TYPE_ETHERNET:
880 case FR_TYPE_ATTR:
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 case FR_TYPE_ATTR:
1044 {
1045 char const *val;
1046 ssize_t len;
1047
1048 if (!PyUnicode_CheckExact(value)) goto wrong_type;
1049
1050 val = PyUnicode_AsUTF8AndSize(value, &len);
1051 if (fr_pair_value_from_str(vp, val, len, NULL, false) < 0) {
1052 PyErr_Format(PyExc_AttributeError, "Failed parsing %.*s as %s", (int)len, val, fr_type_to_str(vp->da->type));
1053 return -1;
1054 }
1055 }
1056 break;
1057
1058 case FR_TYPE_NON_LEAF:
1059 fr_assert(0);
1060 break;
1061 }
1062
1063 return 0;
1064}
1065
1066/** Return the string representation of a leaf pair node
1067 *
1068 * Called when the attribute is accessed in print() or str() or selective other
1069 * magical Python contexts.
1070 */
1071static PyObject *py_freeradius_pair_str(PyObject *self) {
1072 py_freeradius_pair_t *own_self = (py_freeradius_pair_t *)self;
1073 PyObject *value = NULL;
1074 fr_pair_t *vp = own_self->vp;
1075
1076 if (!vp) return PyObject_Str(Py_None);
1077
1078 switch(vp->vp_type) {
1079 case FR_TYPE_STRING:
1080 value = PyUnicode_FromStringAndSize(vp->vp_strvalue, vp->vp_length);
1081 break;
1082
1083 case FR_TYPE_NON_LEAF:
1084 fr_assert(0);
1085 break;
1086
1087 default:
1088 {
1089 ssize_t slen;
1090 char buffer[1024];
1091
1092 slen = fr_value_box_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), &vp->data, NULL);
1093 if (slen < 0) {
1094 error:
1095 PyErr_Format(PyExc_MemoryError, "Failed casting %s to Python string", vp->da->name);
1096 return NULL;
1097 }
1098 value = PyUnicode_FromStringAndSize(buffer, (size_t)slen);
1099 }
1100 break;
1101 }
1102
1103 if (value == NULL) goto error;
1104
1105 return value;
1106}
1107
1108/** Print out the current error
1109 *
1110 * Must be called with a valid thread state set
1111 */
1112static void python_error_log(rlm_python_t const *inst, request_t *request)
1113{
1114 PyObject *p_type = NULL, *p_value = NULL, *p_traceback = NULL, *p_str_1 = NULL, *p_str_2 = NULL;
1115
1116 PyErr_Fetch(&p_type, &p_value, &p_traceback);
1117 PyErr_NormalizeException(&p_type, &p_value, &p_traceback);
1118 if (!p_type || !p_value) goto failed;
1119
1120 if (((p_str_1 = PyObject_Str(p_type)) == NULL) || ((p_str_2 = PyObject_Str(p_value)) == NULL)) goto failed;
1121
1122 ROPTIONAL(RERROR, ERROR, "%s (%s)", PyUnicode_AsUTF8(p_str_1), PyUnicode_AsUTF8(p_str_2));
1123
1124 if (p_traceback != Py_None) {
1125 PyTracebackObject *ptb = (PyTracebackObject*)p_traceback;
1126 size_t fnum = 0;
1127
1128 while (ptb != NULL) {
1129 PyFrameObject *cur_frame = ptb->tb_frame;
1130#if PY_VERSION_HEX >= 0x030A0000
1131 PyCodeObject *code = PyFrame_GetCode(cur_frame);
1132
1133 ROPTIONAL(RERROR, ERROR, "[%ld] %s:%d at %s()",
1134 fnum,
1135 PyUnicode_AsUTF8(code->co_filename),
1136 PyFrame_GetLineNumber(cur_frame),
1137 PyUnicode_AsUTF8(code->co_name)
1138 );
1139 Py_XDECREF(code);
1140#else
1141 ROPTIONAL(RERROR, ERROR, "[%ld] %s:%d at %s()",
1142 fnum,
1143 PyUnicode_AsUTF8(cur_frame->f_code->co_filename),
1144 PyFrame_GetLineNumber(cur_frame),
1145 PyUnicode_AsUTF8(cur_frame->f_code->co_name)
1146 );
1147#endif
1148
1149 ptb = ptb->tb_next;
1150 fnum++;
1151 }
1152 }
1153
1154failed:
1155 Py_XDECREF(p_str_1);
1156 Py_XDECREF(p_str_2);
1157 Py_XDECREF(p_type);
1158 Py_XDECREF(p_value);
1159 Py_XDECREF(p_traceback);
1160}
1161
1162/** Create the Python object representing a pair list
1163 *
1164 */
1165static inline CC_HINT(always_inline) PyObject *pair_list_alloc(request_t *request, fr_dict_attr_t const *list)
1166{
1167 PyObject *py_list;
1168 py_freeradius_pair_t *our_list;
1169
1170 /*
1171 * When handing instantiate and detach, there is no request
1172 */
1173 if (!request) {
1174 Py_INCREF(Py_None);
1175 return Py_None;
1176 }
1177
1178 py_list = PyObject_CallObject((PyObject *)&py_freeradius_pair_list_def, NULL);
1179 if (unlikely(!py_list)) return NULL;
1180
1181 our_list = (py_freeradius_pair_t *)py_list;
1182 our_list->da = list;
1183 our_list->vp = fr_pair_list_parent(tmpl_list_head(request, list));
1184 our_list->parent = NULL;
1185 our_list->idx = 0;
1186 return py_list;
1187}
1188
1190 request_t *request, PyObject *p_func, char const *funcname)
1191{
1192 rlm_rcode_t rcode = RLM_MODULE_OK;
1193 rlm_python_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1194
1195 PyObject *p_ret = NULL;
1196 PyObject *py_request;
1197 py_freeradius_request_t *our_request;
1198
1199 /*
1200 * Instantiate the request
1201 */
1202 py_request = PyObject_CallObject((PyObject *)&py_freeradius_request_def, NULL);
1203 if (unlikely(!py_request)) {
1204 python_error_log(inst, request);
1206 }
1207
1208 our_request = (py_freeradius_request_t *)py_request;
1209 rlm_python_set_request(request);
1210
1211 /*
1212 * Create the list roots
1213 */
1214 our_request->request = pair_list_alloc(request, request_attr_request);
1215 if (unlikely(!our_request->request)) {
1216 req_error:
1217 Py_DECREF(py_request);
1218 python_error_log(inst, request);
1220 }
1221
1222 our_request->reply = pair_list_alloc(request, request_attr_reply);
1223 if (unlikely(!our_request->reply)) goto req_error;
1224
1225 our_request->control = pair_list_alloc(request, request_attr_control);
1226 if (unlikely(!our_request->control)) goto req_error;
1227
1228 our_request->state = pair_list_alloc(request, request_attr_state);
1229 if (unlikely(!our_request->state)) goto req_error;
1230
1231 /* Call Python function. */
1232 p_ret = PyObject_CallFunctionObjArgs(p_func, py_request, NULL);
1233 if (!p_ret) {
1234 RERROR("Python function returned no value");
1235 rcode = RLM_MODULE_FAIL;
1236 goto finish;
1237 }
1238
1239 if (!request) {
1240 // check return code at module instantiation time
1241 if (PyNumber_Check(p_ret)) rcode = PyLong_AsLong(p_ret);
1242 goto finish;
1243 }
1244
1245 /*
1246 * The function is expected to either return a return value
1247 * or None, which results in the default return value.
1248 */
1249 if (PyNumber_Check(p_ret)) {
1250 /* Just an integer */
1251 rcode = PyLong_AsLong(p_ret);
1252
1253 } else if (p_ret == Py_None) {
1254 /* returned 'None', return value defaults to "OK, continue." */
1255 rcode = RLM_MODULE_OK;
1256 } else {
1257 /* Not tuple or None */
1258 ERROR("%s - Function did not return a tuple or None", funcname);
1259 rcode = RLM_MODULE_FAIL;
1260 goto finish;
1261 }
1262
1263finish:
1265
1266 if (rcode == RLM_MODULE_FAIL) python_error_log(inst, request);
1267 Py_XDECREF(p_ret);
1268 Py_XDECREF(py_request);
1269
1270 RETURN_UNLANG_RCODE(rcode);
1271}
1272
1273/** Thread safe call to a python function
1274 *
1275 * Will swap in thread state specific to module/thread.
1276 */
1277static unlang_action_t mod_python(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1278{
1279 rlm_python_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_python_thread_t);
1280 python_call_env_t *func = talloc_get_type_abort(mctx->env_data, python_call_env_t);
1281
1282 /*
1283 * It's a NOOP if the function wasn't defined
1284 */
1285 if (!func->func->function) RETURN_UNLANG_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(p_result, mctx, request, func->func->function, func->func->function_name);
1291 (void)fr_cond_assert(PyEval_SaveThread() == t->state);
1292
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
1692 PYTHON_FUNC_LOAD(instantiate);
1693 PYTHON_FUNC_LOAD(detach);
1694
1695 /*
1696 * Load all the Python functions found by the call_env parser.
1697 */
1698 for (func = fr_rb_iter_init_inorder(&inst->funcs, &iter);
1699 func != NULL;
1700 func = fr_rb_iter_next_inorder(&inst->funcs, &iter)) {
1701 /*
1702 * Check for mod_<name1>_<name2> or mod_<name1> config pairs.
1703 * If neither exist, fall back to default Python module.
1704 */
1705 if (func->name2) {
1706 pair_name = talloc_asprintf(func, "mod_%s_%s", func->name1, func->name2);
1707 cp = cf_pair_find(mctx->mi->conf, pair_name);
1708 talloc_free(pair_name);
1709 if (cp) goto found_mod;
1710 }
1711 pair_name = talloc_asprintf(func, "mod_%s", func->name1);
1712 cp = cf_pair_find(mctx->mi->conf, pair_name);
1713 talloc_free(pair_name);
1714 found_mod:
1715 func->module_name = cp ? cf_pair_value(cp) : inst->def_module_name;
1716
1717 /*
1718 * Check for func_<name1>_<name2> or func_<name1> function overrides.
1719 * Checks for Python functions <name1>_<name2> and <name1> are done
1720 * in python_function_load.
1721 */
1722 if (func->name2) {
1723 pair_name = talloc_asprintf(func, "func_%s_%s", func->name1, func->name2);
1724 cp = cf_pair_find(mctx->mi->conf, pair_name);
1725 talloc_free(pair_name);
1726 if (cp) goto found_func;
1727 }
1728 pair_name = talloc_asprintf(func, "func_%s", func->name1);
1729 cp = cf_pair_find(mctx->mi->conf, pair_name);
1730 talloc_free(pair_name);
1731 found_func:
1732 if (cp) func->function_name = cf_pair_value(cp);
1733
1734 if (python_function_load(mctx, func) < 0) goto error;
1735 }
1736
1737 /*
1738 * Call the instantiate function.
1739 */
1740 if (inst->instantiate.function) {
1741 unlang_result_t result;
1742
1743 do_python_single(&result, MODULE_CTX_FROM_INST(mctx), NULL, inst->instantiate.function, "instantiate");
1744 switch (result.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 unlang_result_t result;
1789
1790 (void)do_python_single(&result, MODULE_CTX_FROM_INST(mctx), NULL, inst->detach.function, "detach");
1791 }
1792
1793#define PYTHON_FUNC_DESTROY(_x) python_function_destroy(&inst->_x)
1794 PYTHON_FUNC_DESTROY(instantiate);
1795 PYTHON_FUNC_DESTROY(detach);
1796
1797 for (func = fr_rb_iter_init_inorder(&inst->funcs, &iter);
1798 func != NULL;
1799 func = fr_rb_iter_next_inorder(&inst->funcs, &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
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
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:506
#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:113
#define unlikely(_x)
Definition build.h:402
#define UNUSED
Definition build.h:336
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:690
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:747
#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:657
#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:280
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:594
Common header for all CONF_* types.
Definition cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:72
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 first 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:1419
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:1574
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1558
#define cf_item_next(_parent, _curr)
Definition cf_util.h:89
#define CF_IDENT_ANY
Definition cf_util.h:75
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:141
#define MEM(x)
Definition debug.h:46
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:40
#define DEBUG(fmt,...)
Definition dhcpclient.c:38
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:3269
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
talloc_free(hp)
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:134
#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:540
#define RDEBUG3(fmt,...)
Definition log.h:355
#define RERROR(fmt,...)
Definition log.h:310
int fr_debug_lvl
Definition log.c:40
fr_log_t default_log
Definition log.c:288
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:577
@ L_DBG_LVL_3
3rd highest priority debug messages (-xxx | -Xx).
Definition log.h:69
@ L_DBG_LVL_1
Highest priority debug messages (-x).
Definition log.h:67
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
Definition log.h:68
@ L_DBG_LVL_OFF
No debug messages.
Definition log.h:66
@ L_DBG_LVL_4
4th highest priority debug messages (-xxxx | -Xxx).
Definition log.h:70
@ L_DBG_LVL_MAX
Lowest priority debug messages (-xxxxx | -Xxxx).
Definition log.h:71
@ L_DBG_WARN_REQ
Less severe warning only displayed when debugging is enabled.
Definition log.h:60
@ L_WARN
Warning.
Definition log.h:54
@ L_ERR
Error message.
Definition log.h:53
@ L_DBG_ERR
Error only displayed when debugging is enabled.
Definition log.h:59
@ L_DBG_ERR_REQ
Less severe error only displayed when debugging is enabled.
Definition log.h:61
@ L_DBG_WARN
Warning only displayed when debugging is enabled.
Definition log.h:58
@ L_INFO
Informational message.
Definition log.h:52
@ L_DBG
Only displayed when debugging is enabled.
Definition log.h:56
#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:970
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:684
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:1471
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:2617
fr_pair_list_t * fr_pair_parent_list(fr_pair_t const *vp)
Return a pointer to the parent pair list.
Definition pair.c:941
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:707
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:755
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:1833
static const conf_parser_t config[]
Definition base.c:163
#define fr_assert(_expr)
Definition rad_assert.h:37
#define RDEBUG2(fmt,...)
#define DEBUG2(fmt,...)
#define WARN(fmt,...)
static rs_t * conf
Definition radsniff.c:52
void * fr_rb_iter_init_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
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(UNUSED fr_rb_tree_t *tree, 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:178
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:319
The main red black tree structure.
Definition rb.h:71
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:61
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:51
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:52
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:47
@ RLM_MODULE_TIMEOUT
Module (or section) timed out.
Definition rcode.h:56
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:53
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:55
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:54
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:50
#define RETURN_UNLANG_NOOP
Definition rcode.h:69
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:426
static int libpython_init(void)
static PyMethodDef py_freeradius_methods[]
Definition rlm_python.c:379
PyObject * pythonconf_dict
Configuration parameters defined in the module made available to the python script.
Definition rlm_python.c:71
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:112
static unlang_action_t do_python_single(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request, PyObject *p_func, char const *funcname)
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:111
static PyTypeObject py_freeradius_value_pair_def
Contains a value pair of a specific type.
Definition rlm_python.c:271
char * name2
Section name2 where this is called.
Definition rlm_python.c:52
fr_rb_node_t node
Entry in tree of Python functions.
Definition rlm_python.c:53
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:126
rlm_python_thread_t * t
Thread-specific python instance.
Definition rlm_python.c:125
PyObject_HEAD rlm_python_t const * inst
< Common fields needed for every python object.
Definition rlm_python.c:124
python_func_def_t detach
Definition rlm_python.c:69
static rlm_python_thread_t * current_t
Used for communicating with object init function.
Definition rlm_python.c:134
static int py_freeradius_pair_map_set(PyObject *self, PyObject *attr, PyObject *value)
Set the value of a pair.
Definition rlm_python.c:667
#define A(x)
static PyGetSetDef py_freeradius_pair_getset[]
How to access "value" attribute of a pair.
Definition rlm_python.c:257
PyObject *char const * def_module_name
< Local, interpreter specific module.
Definition rlm_python.c:63
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:162
static void rlm_python_set_request(request_t *request)
Set the request associated with the current thread state.
Definition rlm_python.c:472
static rlm_python_t const * rlm_python_get_inst(void)
Return the rlm_python instance associated with the current interpreter.
Definition rlm_python.c:447
static rlm_python_t const * current_inst
Used for communication with inittab functions.
Definition rlm_python.c:132
static void libpython_free(void)
PyThreadState * interpreter
The interpreter used for this instance of rlm_python.
Definition rlm_python.c:61
python_func_def_t * func
Definition rlm_python.c:85
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:113
module_rlm_t rlm_python
static int py_freeradius_state_init(PyObject *self, UNUSED PyObject *args, UNUSED PyObject *kwds)
Definition rlm_python.c:503
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:459
static void python_func_name_safe(char *name)
rlm_python_t const * inst
Current module instance data.
Definition rlm_python.c:94
char const * module_name
String name of module.
Definition rlm_python.c:49
PyThreadState * state
Module instance/thread specific state.
Definition rlm_python.c:95
PyObject * parent
Parent object of this pair.
Definition rlm_python.c:106
char const * name
Name of the module instance.
Definition rlm_python.c:60
static PyTypeObject py_freeradius_request_def
Definition rlm_python.c:350
static PyThreadState * global_interpreter
Our first interpreter.
Definition rlm_python.c:130
static PyObject * py_freeradius_pair_map_subscript(PyObject *self, PyObject *attr)
Returns a freeradius.Pair.
Definition rlm_python.c:563
bool path_include_default
Include the default python path in path
Definition rlm_python.c:80
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:410
static PyObject * python_module_init(void)
PyObject *PyObject * function
< Python reference to module.
Definition rlm_python.c:47
char * name1
Section name1 where this is called.
Definition rlm_python.c:51
static PyObject * py_freeradius_attribute_instance(PyObject *self, PyObject *attr)
Returns a specific instance of freeradius.Pair.
Definition rlm_python.c:520
static conf_parser_t const python_global_config[]
Definition rlm_python.c:152
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:104
static PyTypeObject py_freeradius_grouping_pair_def
Contains group attribute of a specific type.
Definition rlm_python.c:291
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:485
#define _PyCFunction_CAST(func)
Definition rlm_python.c:373
static libpython_global_config_t libpython_global_config
Definition rlm_python.c:147
char const * function_name
String name of function in module.
Definition rlm_python.c:50
bool verbose
Enable libpython verbose logging.
Definition rlm_python.c:81
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:623
bool funcs_init
Has the tree been initialised.
Definition rlm_python.c:65
static CONF_SECTION * current_conf
Used for communication with inittab functions.
Definition rlm_python.c:133
static PyObject * py_freeradius_pair_getvalue(PyObject *self, void *closure)
static PyMemberDef py_freeradius_request_attrs[]
Definition rlm_python.c:318
unsigned int idx
Instance index.
Definition rlm_python.c:105
PyObject * state
Session state list.
Definition rlm_python.c:114
#define LOAD_INFO(_fmt,...)
#define PYTHON_FUNC_LOAD(_x)
static PyModuleDef py_freeradius_def
Definition rlm_python.c:399
static void * python_dlhandle
Definition rlm_python.c:129
static PyTypeObject py_freeradius_pair_def
The class which all pair types inherit from.
Definition rlm_python.c:244
static int python_interpreter_init(module_inst_ctx_t const *mctx)
static struct @184 freeradius_constants[]
static const call_env_method_t python_method_env
char const * path
Path to search for python files in.
Definition rlm_python.c:79
#define PYTHON_FUNC_DESTROY(_x)
static PyTypeObject py_freeradius_state_def
Definition rlm_python.c:361
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:171
static conf_parser_t module_config[]
Definition rlm_python.c:189
fr_rb_tree_t funcs
Tree of function calls found by call_env parser.
Definition rlm_python.c:64
static unlang_action_t mod_python(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Thread safe call to a python function.
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:68
static PyTypeObject py_freeradius_pair_list_def
Each instance contains a top level list (i.e.
Definition rlm_python.c:306
PyObject_HEAD fr_dict_attr_t const * da
< Common fields needed for every python object.
Definition rlm_python.c:103
Global config for python library.
Definition rlm_python.c:78
Additional fields for pairs.
Definition rlm_python.c:101
Wrapper around a python instance.
Definition rlm_python.c:122
Specifies the module.function to load for processing a section.
Definition rlm_python.c:45
An instance of the rlm_python module.
Definition rlm_python.c:59
Tracks a python module inst/thread state pair.
Definition rlm_python.c:93
static char const * name
#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:39
char const * name2
Second section name. Usually a packet type like 'access-request', 'access-accept',...
Definition section.h:45
char const * name1
First section name. Usually a verb like 'recv', 'send', etc...
Definition section.h:44
char const * name
Instance name e.g. user_database.
Definition module.h:357
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:351
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:293
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Named methods exported by a module.
Definition module.h:174
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:336
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:253
#define talloc_asprintf
Definition talloc.h:144
#define talloc_strdup(_ctx, _str)
Definition talloc.h:142
static fr_slen_t parent
Definition pair.h:858
#define fr_type_is_structural(_x)
Definition types.h:392
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:83
#define FR_TYPE_NON_LEAF
Definition types.h:318
#define fr_type_is_leaf(_x)
Definition types.h:393
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
#define fr_type_is_struct(_x)
Definition types.h:373
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:6086
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:4316
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4362
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:4823
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:5064
static size_t char ** out
Definition value.h:1030