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: f08823aa4a80e0598dc9f0c4239f458e9f10c9e3 $
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: f08823aa4a80e0598dc9f0c4239f458e9f10c9e3 $")
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
238#undef A
239
240 { NULL, 0 },
242
243/** The class which all pair types inherit from
244 *
245 */
246static PyTypeObject py_freeradius_pair_def = {
247 PyVarObject_HEAD_INIT(NULL, 0)
248 .tp_name = "freeradius.Pair",
249 .tp_doc = "An attribute value pair",
250 .tp_basicsize = sizeof(py_freeradius_pair_t),
251 .tp_itemsize = 0,
252 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
253 .tp_new = PyType_GenericNew,
254};
255
256/** How to access "value" attribute of a pair
257 *
258 */
259static PyGetSetDef py_freeradius_pair_getset[] = {
260 {
261 .name = "value",
264 .doc = "Pair value",
265 .closure = NULL
266 },
267 { NULL } /* Terminator */
268};
269
270/** Contains a value pair of a specific type
271 *
272 */
273static PyTypeObject py_freeradius_value_pair_def = {
274 PyVarObject_HEAD_INIT(NULL, 0)
275 .tp_name = "freeradius.ValuePair",
276 .tp_doc = "A value pair, i.e. one of the type string, integer, ipaddr etc...)",
277 .tp_flags = Py_TPFLAGS_DEFAULT,
278 .tp_base = &py_freeradius_pair_def,
279 .tp_getset = py_freeradius_pair_getset,
280 .tp_str = py_freeradius_pair_str,
281 .tp_as_mapping = &(PyMappingMethods) {
282 .mp_subscript = py_freeradius_attribute_instance,
283 .mp_ass_subscript = py_freeradius_pair_map_set,
284 }
285};
286
287/** Contains group attribute of a specific type
288 *
289 * Children of this attribute may be accessed using the map protocol
290 * i.e. foo['child-of-foo'].
291 *
292 */
293static PyTypeObject py_freeradius_grouping_pair_def = {
294 PyVarObject_HEAD_INIT(NULL, 0)
295 .tp_name = "freeradius.GroupingPair",
296 .tp_doc = "A grouping pair, i.e. one of the type group, tlv, vsa or vendor. "
297 "Children are accessible via the mapping protocol i.e. foo['child-of-foo]",
298 .tp_flags = Py_TPFLAGS_DEFAULT,
299 .tp_base = &py_freeradius_pair_def,
300 .tp_as_mapping = &(PyMappingMethods){
301 .mp_subscript = py_freeradius_pair_map_subscript,
302 .mp_ass_subscript = py_freeradius_pair_map_set,
303 }
304};
305
306/** Each instance contains a top level list (i.e. request, reply, control, session-state)
307 */
308static PyTypeObject py_freeradius_pair_list_def = {
309 PyVarObject_HEAD_INIT(NULL, 0)
310 .tp_name = "freeradius.PairList",
311 .tp_doc = "A list of objects of freeradius.GroupingPairList and freeradius.ValuePair",
312 .tp_flags = Py_TPFLAGS_DEFAULT,
313 .tp_base = &py_freeradius_pair_def,
314 .tp_as_mapping = &(PyMappingMethods){
315 .mp_subscript = py_freeradius_pair_map_subscript,
316 .mp_ass_subscript = py_freeradius_pair_map_set,
317 }
318};
319
320static PyMemberDef py_freeradius_request_attrs[] = {
321 {
322 .name = "request",
323 .type = T_OBJECT,
324 .offset = offsetof(py_freeradius_request_t, request),
325 .flags = READONLY,
326 .doc = "Pairs in the request list - received from the network"
327 },
328 {
329 .name = "reply",
330 .type = T_OBJECT,
331 .offset = offsetof(py_freeradius_request_t, reply),
332 .flags = READONLY,
333 .doc = "Pairs in the reply list - sent to the network"
334 },
335 {
336 .name = "control",
337 .type = T_OBJECT,
338 .offset = offsetof(py_freeradius_request_t, control),
339 .flags = READONLY,
340 .doc = "Pairs in the control list - control the behaviour of subsequently called modules"
341 },
342 {
343 .name = "session-state",
344 .type = T_OBJECT,
345 .offset = offsetof(py_freeradius_request_t, state),
346 .flags = READONLY,
347 .doc = "Pairs in the session-state list - persists for the length of the session"
348 },
349 { NULL } /* Terminator */
350};
351
352static PyTypeObject py_freeradius_request_def = {
353 PyVarObject_HEAD_INIT(NULL, 0)
354 .tp_name = "freeradius.Request",
355 .tp_doc = "freeradius request handle",
356 .tp_basicsize = sizeof(py_freeradius_request_t),
357 .tp_itemsize = 0,
358 .tp_flags = Py_TPFLAGS_DEFAULT,
359 .tp_new = PyType_GenericNew,
360 .tp_members = py_freeradius_request_attrs
361};
362
363static PyTypeObject py_freeradius_state_def = {
364 PyVarObject_HEAD_INIT(NULL, 0)
365 .tp_name = "freeradius.State",
366 .tp_doc = "Private state data",
367 .tp_basicsize = sizeof(py_freeradius_state_t),
368 .tp_itemsize = 0,
369 .tp_flags = Py_TPFLAGS_DEFAULT,
370 .tp_new = PyType_GenericNew,
371 .tp_init = py_freeradius_state_init
372};
373
374#ifndef _PyCFunction_CAST
375#define _PyCFunction_CAST(func) (PyCFunction)((void(*)(void))(func))
376#endif
377
378/*
379 * freeradius Python functions
380 */
381static PyMethodDef py_freeradius_methods[] = {
382 { "log", _PyCFunction_CAST(py_freeradius_log), METH_VARARGS | METH_KEYWORDS,
383 "freeradius.log(msg[, type, lvl])\n\n"
384 "Print a message using the freeradius daemon's logging system.\n"
385 "type should be one of the following constants:\n"
386 " freeradius.L_DBG\n"
387 " freeradius.L_INFO\n"
388 " freeradius.L_WARN\n"
389 " freeradius.L_ERR\n"
390 "lvl should be one of the following constants:\n"
391 " freeradius.L_DBG_LVL_OFF\n"
392 " freeradius.L_DBG_LVL_1\n"
393 " freeradius.L_DBG_LVL_2\n"
394 " freeradius.L_DBG_LVL_3\n"
395 " freeradius.L_DBG_LVL_4\n"
396 " freeradius.L_DBG_LVL_MAX\n"
397 },
398 { NULL, NULL, 0, NULL },
399};
400
401static PyModuleDef py_freeradius_def = {
402 PyModuleDef_HEAD_INIT,
403 .m_name = "freeradius",
404 .m_doc = "FreeRADIUS python module",
405 .m_size = 0,
406 .m_methods = py_freeradius_methods
407};
408
409/** How to compare two Python calls
410 *
411 */
412static int8_t python_func_def_cmp(void const *one, void const *two)
413{
414 python_func_def_t const *a = one, *b = two;
415 int ret;
416
417 ret = strcmp(a->name1, b->name1);
418 if (ret != 0) return CMP(ret, 0);
419 if (!a->name2 && !b->name2) return 0;
420 if (!a->name2 || !b->name2) return a->name2 ? 1 : -1;
421 ret = strcmp(a->name2, b->name2);
422 return CMP(ret, 0);
423}
424
425/** Return the module instance object associated with the thread state or interpreter state
426 *
427 */
428static inline CC_HINT(always_inline) py_freeradius_state_t *rlm_python_state_obj(void)
429{
430 PyObject *dict;
431
432 dict = PyThreadState_GetDict(); /* If this is NULL, we're dealing with the main interpreter */
433 if (!dict) {
434 PyObject *module;
435
436 module = PyState_FindModule(&py_freeradius_def);
437 if (unlikely(!module)) return NULL;
438
439 dict = PyModule_GetDict(module);
440 if (unlikely(!dict)) return NULL;
441 }
442
443 return (py_freeradius_state_t *)PyDict_GetItemString(dict, "__State");
444}
445
446/** Return the rlm_python instance associated with the current interpreter
447 *
448 */
450{
451 py_freeradius_state_t const *p_state;
452
453 p_state = rlm_python_state_obj();
454 if (unlikely(!p_state)) return NULL;
455 return p_state->inst;
456}
457
458/** Return the request associated with the current thread state
459 *
460 */
462{
463 py_freeradius_state_t const *p_state;
464
465 p_state = rlm_python_state_obj();
466 if (unlikely(!p_state)) return NULL;
467
468 return p_state->request;
469}
470
471/** Set the request associated with the current thread state
472 *
473 */
475{
476 py_freeradius_state_t *p_state;
477
478 p_state = rlm_python_state_obj();
479 if (unlikely(!p_state)) return;
480
481 p_state->request = request;
482}
483
484/** Allow fr_log to be called from python
485 *
486 */
487static PyObject *py_freeradius_log(UNUSED PyObject *self, PyObject *args, PyObject *kwds)
488{
489 static char const *kwlist[] = { "msg", "type", "lvl", NULL };
490 char *msg;
491 int type = L_DBG;
492 int lvl = L_DBG_LVL_2;
494
495 if (fr_debug_lvl < lvl) Py_RETURN_NONE; /* Don't bother parsing args */
496
497 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ii", (char **)((uintptr_t)kwlist),
498 &msg, &type, &lvl)) Py_RETURN_NONE;
499
500 fr_log(&default_log, type, __FILE__, __LINE__, "rlm_python (%s) - %s", inst->name, msg);
501
502 Py_RETURN_NONE;
503}
504
505static int py_freeradius_state_init(PyObject *self, UNUSED PyObject *args, UNUSED PyObject *kwds)
506{
508 rlm_python_t const *inst;
509
511 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 */
512 our_self->inst = inst = current_t ? current_t->inst : current_inst;
513 DEBUG3("Populating __State data with %p/%p", our_self->inst, our_self->t);
514
515 return 0;
516}
517
518/** Returns a specific instance of freeradius.Pair
519 *
520 * Called when a numeric index is used on a PairGroup or PairValue as part of an object getter
521 */
522static PyObject *py_freeradius_attribute_instance(PyObject *self, PyObject *attr)
523{
524 long index;
525 py_freeradius_pair_t *pair, *init_pair = (py_freeradius_pair_t *)self;
526
527 if (!PyLong_CheckExact(attr)) Py_RETURN_NONE;
528 index = PyLong_AsLong(attr);
529
530 if (index < 0) {
531 PyErr_SetString(PyExc_AttributeError, "Cannot use negative attribute instance values");
532 return NULL;
533 }
534 if (index == 0) return self;
535
536 if (fr_type_is_leaf(init_pair->da->type)) {
537 pair = PyObject_New(py_freeradius_pair_t, (PyTypeObject *)&py_freeradius_value_pair_def);
538 } else {
539 pair = PyObject_New(py_freeradius_pair_t, (PyTypeObject *)&py_freeradius_grouping_pair_def);
540 }
541 if (!pair) {
542 PyErr_SetString(PyExc_MemoryError, "Failed to allocate PyObject");
543 return NULL;
544 };
545 pair->parent = init_pair->parent;
546 Py_INCREF(init_pair->parent);
547 pair->da = init_pair->da;
548 pair->idx = index;
549 if (init_pair->vp) pair->vp = fr_pair_find_by_da_idx(fr_pair_parent_list(init_pair->vp), pair->da, (unsigned int)index);
550 return (PyObject *)pair;
551}
552
553/** Returns a freeradius.Pair
554 *
555 * Called when pair["attr"] or pair["attr"][n] is accessed.
556 * When pair["attr"] is accessed, `self` is `pair` - which is the list or a pair group object
557 * When pair["attr"][n] is accessed, `self` is pair["attr"]
558 *
559 * Either a group object or pair object will be returned.
560 */
561static PyObject *py_freeradius_pair_map_subscript(PyObject *self, PyObject *attr)
562{
563 py_freeradius_pair_t *our_self = (py_freeradius_pair_t *)self;
564 char const *attr_name;
565 ssize_t len;
568 fr_dict_attr_t const *da;
569 fr_pair_list_t *list = NULL;
570
571 /*
572 * If we have a numeric subscript, find the nth instance of the pair.
573 */
574 if (PyLong_CheckExact(attr)) return py_freeradius_attribute_instance(self, attr);
575
576 if (!PyUnicode_CheckExact(attr)) {
577 PyErr_Format(PyExc_AttributeError, "Invalid type '%s' for map attribute",
578 ((PyTypeObject *)PyObject_Type(attr))->tp_name);
579 return NULL;
580 }
581 attr_name = PyUnicode_AsUTF8AndSize(attr, &len);
582
583 if (PyObject_IsInstance(self, (PyObject *)&py_freeradius_pair_list_def)) {
584 fr_dict_attr_search_by_name_substr(NULL, &da, request->proto_dict, &FR_SBUFF_IN(attr_name, len),
585 NULL, true, false);
586 } else {
587 fr_dict_attr_by_name_substr(NULL, &da, our_self->da, &FR_SBUFF_IN(attr_name, len), NULL);
588 }
589 if (our_self->vp) list = &our_self->vp->vp_group;
590
591 if (!da) {
592 PyErr_Format(PyExc_AttributeError, "Invalid attribute name '%.*s'", (int)len, attr_name);
593 return NULL;
594 }
595
596 if (fr_type_is_leaf(da->type)) {
597 pair = PyObject_New(py_freeradius_pair_t, (PyTypeObject *)&py_freeradius_value_pair_def);
598 } else {
599 pair = PyObject_New(py_freeradius_pair_t, (PyTypeObject *)&py_freeradius_grouping_pair_def);
600 }
601 if (!pair) {
602 PyErr_SetString(PyExc_MemoryError, "Failed to allocate PyObject");
603 return NULL;
604 };
605
606 pair->parent = self;
607 Py_INCREF(self);
608 pair->da = da;
609 pair->vp = list ? fr_pair_find_by_da(list, NULL, da) : NULL;
610 pair->idx = 0;
611
612 return (PyObject *)pair;
613}
614
615/** Build out missing parent pairs when a leaf node is assigned a value.
616 *
617 */
619{
621 fr_pair_t *parent = ((py_freeradius_pair_t *)obj_pair->parent)->vp;
622
623 if (!parent) {
625 if (!parent) return NULL;
626 }
627
628 /*
629 * Asked to populate foo[n] - check that we have n instances already
630 */
631 if (obj_pair->idx > 0) {
632 unsigned int count = fr_pair_count_by_da(&parent->vp_group, obj_pair->da);
633 if (count < obj_pair->idx) {
634 PyErr_Format(PyExc_AttributeError, "Attempt to set instance %d when only %d exist", index, count);
635 return NULL;
636 }
637 }
638 fr_pair_append_by_da(parent, &obj_pair->vp, &parent->vp_group, obj_pair->da);
639
640 return obj_pair->vp;
641}
642
643/** Set the value of a pair
644 *
645 * Called with two Python syntaxes
646 *
647 * - request['foo'] = 'baa'
648 * `self` will be the parent object - either the list or parent structural object.
649 * `attr` is the value in [].
650 * `value` is what we want to set the pair to.
651 *
652 * - request['foo'][n] = 'baa'
653 * `self` will be the first instance of the attribute `foo`
654 * `attr` will be the instance number
655 * `value` is what we want to set the pair to.
656 *
657 * We expect `value` to be a UTF8 string object.
658 *
659 * Due to Python "magic" this is also called when `del request['foo']` happens - only with
660 * value as NULL.
661 */
662static int py_freeradius_pair_map_set(PyObject* self, PyObject* attr, PyObject* value)
663{
664 fr_pair_list_t *list = NULL;
666 py_freeradius_pair_t *our_self = (py_freeradius_pair_t *)self;
667 fr_pair_t *vp = NULL;
668 char const *vstr;
669 ssize_t vlen;
670 bool del = (value ? false : true);
671
672 if (value && !PyUnicode_CheckExact(value)) {
673 PyErr_Format(PyExc_AttributeError, "Invalid value type '%s'", ((PyTypeObject *)PyObject_Type(value))->tp_name);
674 return -1;
675 }
676
677 /*
678 * list['attr'] = 'value'
679 * Look up DA represented by 'attr' and find pair or create pair to update
680 */
681 if (PyUnicode_CheckExact(attr)) {
682 char const *attr_name;
683 ssize_t len;
684 fr_dict_attr_t const *da = NULL;
685
686 attr_name = PyUnicode_AsUTF8AndSize(attr, &len);
687
688 if (PyObject_IsInstance(self, (PyObject *)&py_freeradius_pair_list_def)) {
689 fr_dict_attr_search_by_name_substr(NULL, &da, request->proto_dict, &FR_SBUFF_IN(attr_name, len),
690 NULL, true, false);
691 } else {
692 fr_dict_attr_by_name_substr(NULL, &da, our_self->da, &FR_SBUFF_IN(attr_name, len), NULL);
693 }
694
695 if (!da) {
696 PyErr_Format(PyExc_AttributeError, "Invalid attribute name %.*s", (int)len, attr_name);
697 return -1;
698 }
699
700 // `self` is the parent of the pair we're building - so we build parents to that point.
701 if (!our_self->vp) {
702 if (del) return 0; // If we're deleting then no need to build parents.
703 our_self->vp = py_freeradius_build_parents(self);
704 }
705 if (!our_self->vp) return -1;
706
707 list = &our_self->vp->vp_group;
708
709 vp = fr_pair_find_by_da(list, NULL, da);
710 if (del) goto del;
711
712 if (!vp) {
713 if (fr_pair_append_by_da(fr_pair_list_parent(list), &vp, list, da) < 0) {
714 PyErr_Format(PyExc_MemoryError, "Failed to add attribute %s", da->name);
715 return -1;
716 }
717 } else {
719 }
720
721 /*
722 * list['attr'][n] = 'value'
723 * Look for instance n, creating if necessary
724 */
725 } else if (PyLong_CheckExact(attr)) {
726 long index = PyLong_AsLong(attr);
728
729 if (index < 0) {
730 PyErr_SetString(PyExc_AttributeError, "Cannot use negative attribute instance values");
731 return -1;
732 }
733
734 if (!parent->vp) {
735 if (del) return 0;
737 }
738 if (!parent->vp) return -1;
739
740 list = &parent->vp->vp_group;
741
742 if (index == 0) {
743 if (!our_self->vp) {
744 if (fr_pair_append_by_da(fr_pair_list_parent(list), &our_self->vp, list, our_self->da) < 0) {
745 PyErr_Format(PyExc_MemoryError, "Failed to add attribute %s", our_self->da->name);
746 return -1;
747 }
748 } else {
749 fr_value_box_clear_value(&our_self->vp->data);
750 }
751 vp = our_self->vp;
752 if (del) goto del;
753 } else {
754 vp = fr_pair_find_by_da_idx(list, our_self->da, index);
755 if (del) goto del;
756 if (!vp) {
757 unsigned int count = fr_pair_count_by_da(list, our_self->da);
758 if (count < index) {
759 PyErr_Format(PyExc_AttributeError, "Attempt to set instance %ld when only %d exist", index, count);
760 return -1;
761 }
762 if (fr_pair_append_by_da(fr_pair_list_parent(list), &vp, list, our_self->da) < 0) {
763 PyErr_Format(PyExc_MemoryError, "Failed to add attribute %s", our_self->da->name);
764 return -1;
765 }
766 }
767 }
768 } else {
769 PyErr_Format(PyExc_AttributeError, "Invalid object type '%s'", ((PyTypeObject *)PyObject_Type(attr))->tp_name);
770 return -1;
771 }
772
774
775 vstr = PyUnicode_AsUTF8AndSize(value, &vlen);
776
777 if (fr_pair_value_from_str(vp, vstr, vlen, NULL, false) < 0) {
778 PyErr_Format(PyExc_AttributeError, "Failed setting '%s' = '%.*s", vp->da->name, (int)vlen, vstr);
779 fr_pair_delete(list, vp);
780 return -1;
781 }
782
783 RDEBUG2("set %pP", vp);
784 return 0;
785
786del:
787 if (vp) {
788 RDEBUG2("delete %pP", vp);
789 fr_pair_delete(list, vp);
790 }
791 return 0;
792}
793
794/** Return a native Python object of the appropriate type for leaf pair objects
795 *
796 * Accessed as `request['attr'].value`
797 *
798 * `self` is the Python leaf pair object
799 */
800static PyObject *py_freeradius_pair_getvalue(PyObject *self, UNUSED void *closure)
801{
802 py_freeradius_pair_t *own_self = (py_freeradius_pair_t *)self;
803 PyObject *value = NULL;
804 fr_pair_t *vp = own_self->vp;
805
806 if (!vp) Py_RETURN_NONE;
807
808 switch(vp->vp_type) {
809 case FR_TYPE_STRING:
810 value = PyUnicode_FromStringAndSize(vp->vp_strvalue, vp->vp_length);
811 break;
812
813 case FR_TYPE_OCTETS:
814 value = PyBytes_FromStringAndSize((char const *)vp->vp_octets, vp->vp_length);
815 break;
816
817 case FR_TYPE_BOOL:
818 value = PyBool_FromLong(vp->vp_bool);
819 break;
820
821 case FR_TYPE_UINT8:
822 value = PyLong_FromUnsignedLong(vp->vp_uint8);
823 break;
824
825 case FR_TYPE_UINT16:
826 value = PyLong_FromUnsignedLong(vp->vp_uint16);
827 break;
828
829 case FR_TYPE_UINT32:
830 value = PyLong_FromUnsignedLong(vp->vp_uint32);
831 break;
832
833 case FR_TYPE_UINT64:
834 value = PyLong_FromUnsignedLongLong(vp->vp_uint64);
835 break;
836
837 case FR_TYPE_INT8:
838 value = PyLong_FromLong(vp->vp_int8);
839 break;
840
841 case FR_TYPE_INT16:
842 value = PyLong_FromLong(vp->vp_int16);
843 break;
844
845 case FR_TYPE_INT32:
846 value = PyLong_FromLong(vp->vp_int32);
847 break;
848
849 case FR_TYPE_INT64:
850 value = PyLong_FromLongLong(vp->vp_int64);
851 break;
852
853 case FR_TYPE_FLOAT32:
854 value = PyFloat_FromDouble((double) vp->vp_float32);
855 break;
856
857 case FR_TYPE_FLOAT64:
858 value = PyFloat_FromDouble(vp->vp_float64);
859 break;
860
861 case FR_TYPE_SIZE:
862 value = PyLong_FromSize_t(vp->vp_size);
863 break;
864
866 case FR_TYPE_DATE:
867 case FR_TYPE_IFID:
874 case FR_TYPE_ETHERNET:
875 {
876 ssize_t slen;
877 char buffer[1024];
878
879 slen = fr_value_box_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), &vp->data, NULL);
880 if (slen < 0) {
881 error:
882 PyErr_Format(PyExc_MemoryError, "Failed marshalling %s to Python value", vp->da->name);
883 return NULL;
884 }
885 value = PyUnicode_FromStringAndSize(buffer, (size_t)slen);
886 }
887 break;
888
889 case FR_TYPE_NON_LEAF:
890 fr_assert(0);
891 break;
892 }
893
894 if (value == NULL) goto error;
895
896 Py_INCREF(value);
897 return value;
898}
899
900/** Use a native Python object of the appropriate type to set a leaf pair
901 *
902 * Using Python syntax `request['attr'].value = object` or `request['attr'][n].value`
903 *
904 * `self` is the Python object representing the leaf node pair
905 */
906static int py_freeradius_pair_setvalue(PyObject *self, PyObject *value, UNUSED void *closure)
907{
908 py_freeradius_pair_t *own_self = (py_freeradius_pair_t *)self;
909 fr_pair_t *vp = own_self->vp;
910
911 /*
912 * The pair doesn't exist yet - so create it
913 */
914 if (!vp) {
916 fr_pair_list_t *list;
917 unsigned int count;
918
919 if (!parent->vp) parent->vp = py_freeradius_build_parents(own_self->parent);
920 if (!parent->vp) return -1;
921
922 list = &parent->vp->vp_group;
923 count = fr_pair_count_by_da(list, own_self->da);
924 if (count < own_self->idx) {
925 PyErr_Format(PyExc_AttributeError, "Attempt to set instance %d when only %d exist", own_self->idx, count);
926 return -1;
927 }
928 fr_pair_append_by_da(fr_pair_list_parent(list), &vp, list, own_self->da);
929 own_self->vp = vp;
930 }
931
932 switch (vp->da->type) {
933 case FR_TYPE_STRING:
934 {
935 char const *val;
936 ssize_t len;
937 if (!PyUnicode_CheckExact(value)){
938 wrong_type:
939 PyErr_Format(PyExc_AttributeError, "Incorrect Python type '%s' for attribute type '%s'",
940 ((PyTypeObject *)PyObject_Type(value))->tp_name, fr_type_to_str(vp->da->type));
941 return -1;
942 }
943 val = PyUnicode_AsUTF8AndSize(value, &len);
945 fr_value_box_bstrndup(vp, &vp->data, NULL, val, len, false);
946 }
947 break;
948
949 case FR_TYPE_OCTETS:
950 {
951 uint8_t *val;
952 ssize_t len;
953 if (!PyObject_IsInstance(value, (PyObject *)&PyBytes_Type)) goto wrong_type;
954 PyBytes_AsStringAndSize(value, (char **)&val, &len);
955 fr_value_box_clear(&vp->data);
956 fr_value_box_memdup(vp, &vp->data, NULL, val, len, false);
957 }
958 break;
959
960 case FR_TYPE_BOOL:
961 case FR_TYPE_UINT8:
962 case FR_TYPE_UINT16:
963 case FR_TYPE_UINT32:
964 case FR_TYPE_UINT64:
965 case FR_TYPE_INT8:
966 case FR_TYPE_INT16:
967 case FR_TYPE_INT32:
968 case FR_TYPE_INT64:
969 case FR_TYPE_SIZE:
970 {
971 long long val;
972 if (!PyLong_CheckExact(value)) goto wrong_type;
973 val = PyLong_AsLongLong(value);
974
975 switch (vp->da->type) {
976 case FR_TYPE_BOOL:
977 vp->vp_bool = (bool)val;
978 break;
979 case FR_TYPE_UINT8:
980 vp->vp_uint8 = (uint8_t)val;
981 break;
982 case FR_TYPE_UINT16:
983 vp->vp_uint16 = (uint16_t)val;
984 break;
985 case FR_TYPE_UINT32:
986 vp->vp_uint32 = (uint32_t)val;
987 break;
988 case FR_TYPE_UINT64:
989 vp->vp_uint64 = (uint64_t)val;
990 break;
991 case FR_TYPE_INT8:
992 vp->vp_int8 = (int8_t)val;
993 break;
994 case FR_TYPE_INT16:
995 vp->vp_int16 = (int16_t)val;
996 break;
997 case FR_TYPE_INT32:
998 vp->vp_int32 = (int32_t)val;
999 break;
1000 case FR_TYPE_INT64:
1001 vp->vp_int64 = (int64_t)val;
1002 break;
1003 case FR_TYPE_SIZE:
1004 vp->vp_size = (size_t)val;
1005 break;
1006 default:
1007 fr_assert(0);
1008 }
1009 }
1010 break;
1011
1012 case FR_TYPE_FLOAT32:
1013 case FR_TYPE_FLOAT64:
1014 {
1015 double val;
1016 if (!PyFloat_CheckExact(value)) goto wrong_type;
1017 val = PyFloat_AsDouble(value);
1018
1019 if (vp->da->type == FR_TYPE_FLOAT32) {
1020 vp->vp_float32 = (float)val;
1021 } else {
1022 vp->vp_float64 = val;
1023 }
1024 }
1025 break;
1026
1027 case FR_TYPE_TIME_DELTA:
1028 case FR_TYPE_DATE:
1029 case FR_TYPE_IFID:
1030 case FR_TYPE_IPV6_ADDR:
1032 case FR_TYPE_IPV4_ADDR:
1036 case FR_TYPE_ETHERNET:
1037 {
1038 char const *val;
1039 ssize_t len;
1040
1041 if (!PyUnicode_CheckExact(value)) goto wrong_type;
1042
1043 val = PyUnicode_AsUTF8AndSize(value, &len);
1044 if (fr_pair_value_from_str(vp, val, len, NULL, false) < 0) {
1045 PyErr_Format(PyExc_AttributeError, "Failed parsing %.*s as %s", (int)len, val, fr_type_to_str(vp->da->type));
1046 return -1;
1047 }
1048 }
1049 break;
1050
1051 case FR_TYPE_NON_LEAF:
1052 fr_assert(0);
1053 break;
1054 }
1055
1056 return 0;
1057}
1058
1059/** Return the string representation of a leaf pair node
1060 *
1061 * Called when the attribute is accessed in print() or str() or selective other
1062 * magical Python contexts.
1063 */
1064static PyObject *py_freeradius_pair_str(PyObject *self) {
1065 py_freeradius_pair_t *own_self = (py_freeradius_pair_t *)self;
1066 PyObject *value = NULL;
1067 fr_pair_t *vp = own_self->vp;
1068
1069 if (!vp) return PyObject_Str(Py_None);
1070
1071 switch(vp->vp_type) {
1072 case FR_TYPE_STRING:
1073 value = PyUnicode_FromStringAndSize(vp->vp_strvalue, vp->vp_length);
1074 break;
1075
1076 case FR_TYPE_NON_LEAF:
1077 fr_assert(0);
1078 break;
1079
1080 default:
1081 {
1082 ssize_t slen;
1083 char buffer[1024];
1084
1085 slen = fr_value_box_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), &vp->data, NULL);
1086 if (slen < 0) {
1087 error:
1088 PyErr_Format(PyExc_MemoryError, "Failed casting %s to Python string", vp->da->name);
1089 return NULL;
1090 }
1091 value = PyUnicode_FromStringAndSize(buffer, (size_t)slen);
1092 }
1093 break;
1094 }
1095
1096 if (value == NULL) goto error;
1097
1098 return value;
1099}
1100
1101/** Print out the current error
1102 *
1103 * Must be called with a valid thread state set
1104 */
1105static void python_error_log(rlm_python_t const *inst, request_t *request)
1106{
1107 PyObject *p_type = NULL, *p_value = NULL, *p_traceback = NULL, *p_str_1 = NULL, *p_str_2 = NULL;
1108
1109 PyErr_Fetch(&p_type, &p_value, &p_traceback);
1110 PyErr_NormalizeException(&p_type, &p_value, &p_traceback);
1111 if (!p_type || !p_value) goto failed;
1112
1113 if (((p_str_1 = PyObject_Str(p_type)) == NULL) || ((p_str_2 = PyObject_Str(p_value)) == NULL)) goto failed;
1114
1115 ROPTIONAL(RERROR, ERROR, "%s (%s)", PyUnicode_AsUTF8(p_str_1), PyUnicode_AsUTF8(p_str_2));
1116
1117 if (p_traceback != Py_None) {
1118 PyTracebackObject *ptb = (PyTracebackObject*)p_traceback;
1119 size_t fnum = 0;
1120
1121 while (ptb != NULL) {
1122 PyFrameObject *cur_frame = ptb->tb_frame;
1123#if PY_VERSION_HEX >= 0x030A0000
1124 PyCodeObject *code = PyFrame_GetCode(cur_frame);
1125
1126 ROPTIONAL(RERROR, ERROR, "[%ld] %s:%d at %s()",
1127 fnum,
1128 PyUnicode_AsUTF8(code->co_filename),
1129 PyFrame_GetLineNumber(cur_frame),
1130 PyUnicode_AsUTF8(code->co_name)
1131 );
1132 Py_XDECREF(code);
1133#else
1134 ROPTIONAL(RERROR, ERROR, "[%ld] %s:%d at %s()",
1135 fnum,
1136 PyUnicode_AsUTF8(cur_frame->f_code->co_filename),
1137 PyFrame_GetLineNumber(cur_frame),
1138 PyUnicode_AsUTF8(cur_frame->f_code->co_name)
1139 );
1140#endif
1141
1142 ptb = ptb->tb_next;
1143 fnum++;
1144 }
1145 }
1146
1147failed:
1148 Py_XDECREF(p_str_1);
1149 Py_XDECREF(p_str_2);
1150 Py_XDECREF(p_type);
1151 Py_XDECREF(p_value);
1152 Py_XDECREF(p_traceback);
1153}
1154
1155/** Create the Python object representing a pair list
1156 *
1157 */
1158static inline CC_HINT(always_inline) PyObject *pair_list_alloc(request_t *request, fr_dict_attr_t const *list)
1159{
1160 PyObject *py_list;
1161 py_freeradius_pair_t *our_list;
1162
1163 /*
1164 * When handing instantiate and detach, there is no request
1165 */
1166 if (!request) {
1167 Py_INCREF(Py_None);
1168 return Py_None;
1169 }
1170
1171 py_list = PyObject_CallObject((PyObject *)&py_freeradius_pair_list_def, NULL);
1172 if (unlikely(!py_list)) return NULL;
1173
1174 our_list = (py_freeradius_pair_t *)py_list;
1175 our_list->da = list;
1176 our_list->vp = fr_pair_list_parent(tmpl_list_head(request, list));
1177 our_list->parent = NULL;
1178 our_list->idx = 0;
1179 return py_list;
1180}
1181
1183 request_t *request, PyObject *p_func, char const *funcname)
1184{
1185 rlm_rcode_t rcode = RLM_MODULE_OK;
1186 rlm_python_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1187
1188 PyObject *p_ret = NULL;
1189 PyObject *py_request;
1190 py_freeradius_request_t *our_request;
1191
1192 /*
1193 * Instantiate the request
1194 */
1195 py_request = PyObject_CallObject((PyObject *)&py_freeradius_request_def, NULL);
1196 if (unlikely(!py_request)) {
1197 python_error_log(inst, request);
1199 }
1200
1201 our_request = (py_freeradius_request_t *)py_request;
1202 rlm_python_set_request(request);
1203
1204 /*
1205 * Create the list roots
1206 */
1207 our_request->request = pair_list_alloc(request, request_attr_request);
1208 if (unlikely(!our_request->request)) {
1209 req_error:
1210 Py_DECREF(py_request);
1211 python_error_log(inst, request);
1213 }
1214
1215 our_request->reply = pair_list_alloc(request, request_attr_reply);
1216 if (unlikely(!our_request->reply)) goto req_error;
1217
1218 our_request->control = pair_list_alloc(request, request_attr_control);
1219 if (unlikely(!our_request->control)) goto req_error;
1220
1221 our_request->state = pair_list_alloc(request, request_attr_state);
1222 if (unlikely(!our_request->state)) goto req_error;
1223
1224 /* Call Python function. */
1225 p_ret = PyObject_CallFunctionObjArgs(p_func, py_request, NULL);
1226 if (!p_ret) {
1227 RERROR("Python function returned no value");
1228 rcode = RLM_MODULE_FAIL;
1229 goto finish;
1230 }
1231
1232 if (!request) {
1233 // check return code at module instantiation time
1234 if (PyNumber_Check(p_ret)) rcode = PyLong_AsLong(p_ret);
1235 goto finish;
1236 }
1237
1238 /*
1239 * The function is expected to either return a return value
1240 * or None, which results in the default return value.
1241 */
1242 if (PyNumber_Check(p_ret)) {
1243 /* Just an integer */
1244 rcode = PyLong_AsLong(p_ret);
1245
1246 } else if (p_ret == Py_None) {
1247 /* returned 'None', return value defaults to "OK, continue." */
1248 rcode = RLM_MODULE_OK;
1249 } else {
1250 /* Not tuple or None */
1251 ERROR("%s - Function did not return a tuple or None", funcname);
1252 rcode = RLM_MODULE_FAIL;
1253 goto finish;
1254 }
1255
1256finish:
1258
1259 if (rcode == RLM_MODULE_FAIL) python_error_log(inst, request);
1260 Py_XDECREF(p_ret);
1261 Py_XDECREF(py_request);
1262
1263 RETURN_MODULE_RCODE(rcode);
1264}
1265
1266/** Thread safe call to a python function
1267 *
1268 * Will swap in thread state specific to module/thread.
1269 */
1270static unlang_action_t mod_python(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1271{
1272 rlm_python_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_python_thread_t);
1273 python_call_env_t *func = talloc_get_type_abort(mctx->env_data, python_call_env_t);
1274 rlm_rcode_t rcode;
1275
1276 /*
1277 * It's a NOOP if the function wasn't defined
1278 */
1279 if (!func->func->function) RETURN_MODULE_NOOP;
1280
1281 RDEBUG3("Using thread state %p/%p", mctx->mi->data, t->state);
1282
1283 PyEval_RestoreThread(t->state); /* Swap in our local thread state */
1284 do_python_single(&rcode, mctx, request, func->func->function, func->func->function_name);
1285 (void)fr_cond_assert(PyEval_SaveThread() == t->state);
1286
1287 RETURN_MODULE_RCODE(rcode);
1288}
1289
1290static void python_obj_destroy(PyObject **ob)
1291{
1292 if (*ob != NULL) {
1293 Py_DECREF(*ob);
1294 *ob = NULL;
1295 }
1296}
1297
1299{
1301 python_obj_destroy(&def->module);
1302}
1303
1304/** Import a user module and load a function from it
1305 *
1306 */
1308{
1309 rlm_python_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1310 char const *funcname = "python_function_load";
1311
1312 if (def->module_name == NULL || (def->function_name == NULL && def->name1 == NULL)) return 0;
1313
1314 LSAN_DISABLE(def->module = PyImport_ImportModule(def->module_name));
1315 if (!def->module) {
1316 ERROR("%s - Module '%s' load failed", funcname, def->module_name);
1317 error:
1318 python_error_log(inst, NULL);
1319 Py_XDECREF(def->function);
1320 def->function = NULL;
1321 Py_XDECREF(def->module);
1322 def->module = NULL;
1323
1324 return -1;
1325 }
1326
1327 /*
1328 * Calls found by call_env parsing will have name1 set
1329 * If name2 is set first look for <name1>_<name2> and fall back to <name1>
1330 */
1331 if (!def->function_name) def->function_name = def->name2 ? talloc_asprintf(def, "%s_%s", def->name1, def->name2) : def->name1;
1332
1333 def->function = PyObject_GetAttrString(def->module, def->function_name);
1334 if (!def->function && def->name2) {
1335 PyErr_Clear(); // Since we're checking for another function, clear any errors.
1337 def->function_name = def->name1;
1338 def->function = PyObject_GetAttrString(def->module, def->function_name);
1339 }
1340 if (!def->function) {
1341 ERROR("%s - Function '%s.%s' is not found", funcname, def->module_name, def->function_name);
1342 goto error;
1343 }
1344
1345 if (!PyCallable_Check(def->function)) {
1346 ERROR("%s - Function '%s.%s' is not callable", funcname, def->module_name, def->function_name);
1347 goto error;
1348 }
1349
1350 DEBUG2("Loaded function '%s.%s'", def->module_name, def->function_name);
1351 return 0;
1352}
1353
1354/*
1355 * Parse a configuration section, and populate a dict.
1356 * This function is recursively called (allows to have nested dicts.)
1357 */
1358static int python_parse_config(rlm_python_t const *inst, CONF_SECTION *cs, int lvl, PyObject *dict)
1359{
1360 int indent_section = (lvl * 4);
1361 int indent_item = (lvl + 1) * 4;
1362 int ret = 0;
1363 CONF_ITEM *ci = NULL;
1364
1365 if (!cs || !dict) return -1;
1366
1367 DEBUG("%*s%s {", indent_section, " ", cf_section_name1(cs));
1368
1369 while ((ci = cf_item_next(cs, ci))) {
1370 /*
1371 * This is a section.
1372 * Create a new dict, store it in current dict,
1373 * Then recursively call python_parse_config with this section and the new dict.
1374 */
1375 if (cf_item_is_section(ci)) {
1376 CONF_SECTION *sub_cs = cf_item_to_section(ci);
1377 char const *key = cf_section_name1(sub_cs); /* dict key */
1378 PyObject *sub_dict, *p_key;
1379
1380 p_key = PyUnicode_FromString(key);
1381 if (!p_key) {
1382 ERROR("Failed converting config key \"%s\" to python string", key);
1383 return -1;
1384 }
1385
1386 if (PyDict_Contains(dict, p_key)) {
1387 WARN("Ignoring duplicate config section '%s'", key);
1388 continue;
1389 }
1390
1391 MEM(sub_dict = PyDict_New());
1392 (void)PyDict_SetItem(dict, p_key, sub_dict);
1393
1394 ret = python_parse_config(inst, sub_cs, lvl + 1, sub_dict);
1395 if (ret < 0) break;
1396 } else if (cf_item_is_pair(ci)) {
1397 CONF_PAIR *cp = cf_item_to_pair(ci);
1398 char const *key = cf_pair_attr(cp); /* dict key */
1399 char const *value = cf_pair_value(cp); /* dict value */
1400 PyObject *p_key, *p_value;
1401
1402 if (!value) {
1403 WARN("Skipping \"%s\" as it has no value", key);
1404 continue;
1405 }
1406
1407 p_key = PyUnicode_FromString(key);
1408 p_value = PyUnicode_FromString(value);
1409 if (!p_key) {
1410 ERROR("Failed converting config key \"%s\" to python string", key);
1411 return -1;
1412 }
1413 if (!p_value) {
1414 ERROR("Failed converting config value \"%s\" to python string", value);
1415 return -1;
1416 }
1417
1418 /*
1419 * This is an item.
1420 * Store item attr / value in current dict.
1421 */
1422 if (PyDict_Contains(dict, p_key)) {
1423 WARN("Ignoring duplicate config item '%s'", key);
1424 continue;
1425 }
1426
1427 (void)PyDict_SetItem(dict, p_key, p_value);
1428
1429 DEBUG("%*s%s = \"%s\"", indent_item, " ", key, value);
1430 }
1431 }
1432
1433 DEBUG("%*s}", indent_section, " ");
1434
1435 return ret;
1436}
1437
1438/** Make the current instance's config available within the module we're initialising
1439 *
1440 */
1442{
1443 CONF_SECTION *cs;
1444
1445 /*
1446 * Convert a FreeRADIUS config structure into a python
1447 * dictionary.
1448 */
1449 inst->pythonconf_dict = PyDict_New();
1450 if (!inst->pythonconf_dict) {
1451 ERROR("Unable to create python dict for config");
1452 error:
1453 Py_XDECREF(inst->pythonconf_dict);
1454 inst->pythonconf_dict = NULL;
1455 python_error_log(inst, NULL);
1456 return -1;
1457 }
1458
1459 cs = cf_section_find(conf, "config", NULL);
1460 if (cs) {
1461 DEBUG("Inserting \"config\" section into python environment as radiusd.config");
1462 if (python_parse_config(inst, cs, 0, inst->pythonconf_dict) < 0) goto error;
1463 }
1464
1465 /*
1466 * Add module configuration as a dict
1467 */
1468 if (PyModule_AddObject(module, "config", inst->pythonconf_dict) < 0) goto error;
1469
1470 return 0;
1471}
1472
1473/** Import integer constants into the module we're initialising
1474 *
1475 */
1476static int python_module_import_constants(rlm_python_t const *inst, PyObject *module)
1477{
1478 size_t i;
1479
1480 for (i = 0; freeradius_constants[i].name; i++) {
1481 if ((PyModule_AddIntConstant(module, freeradius_constants[i].name, freeradius_constants[i].value)) < 0) {
1482 ERROR("Failed adding constant to module");
1483 python_error_log(inst, NULL);
1484 return -1;
1485 }
1486 }
1487
1488 return 0;
1489}
1490
1491/*
1492 * Python 3 interpreter initialisation and destruction
1493 */
1494static PyObject *python_module_init(void)
1495{
1496 PyObject *module;
1497 PyObject *p_state;
1498 static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
1499 static bool type_ready = false;
1500
1502
1503 /*
1504 * Only allow one thread at a time to do the module init. This is
1505 * out of an abundance of caution as it's unclear whether the
1506 * reference counts on the various objects are thread safe.
1507 */
1508 pthread_mutex_lock(&init_lock);
1509
1510 /*
1511 * The type definitions are global, so we only need to call the
1512 * init functions the first pass through.
1513 */
1514
1515 if (!type_ready) {
1516 /*
1517 * We need to initialise the definitions first
1518 * this fills in any fields we didn't explicitly
1519 * specify, and gets the structures ready for
1520 * use by the python interpreter.
1521 */
1522 if (PyType_Ready(&py_freeradius_pair_def) < 0) {
1523 error:
1524 pthread_mutex_unlock(&init_lock);
1526 Py_RETURN_NONE;
1527 }
1528
1529 if (PyType_Ready(&py_freeradius_value_pair_def) < 0) goto error;
1530
1531 if (PyType_Ready(&py_freeradius_grouping_pair_def) < 0) goto error;
1532
1533 if (PyType_Ready(&py_freeradius_pair_list_def) < 0) goto error;
1534
1535 if (PyType_Ready(&py_freeradius_request_def) < 0) goto error;
1536
1537 if (PyType_Ready(&py_freeradius_state_def) < 0) goto error;
1538
1539 type_ready = true;
1540 }
1541
1542 /*
1543 * The module is per-interpreter
1544 */
1545 module = PyModule_Create(&py_freeradius_def);
1546 if (!module) {
1548 goto error;
1549 }
1550
1551 /*
1552 * PyModule_AddObject steals ref on success, we we
1553 * INCREF here to give it something to steal, else
1554 * on free the refcount would go negative.
1555 *
1556 * Note here we're creating a new instance of an
1557 * object, not adding the object definition itself
1558 * as there's no reason that a python script would
1559 * ever need to create an instance object.
1560 *
1561 * The instantiation function associated with the
1562 * the __State object takes care of populating the
1563 * instance data from globals and thread-specific
1564 * variables.
1565 */
1566 p_state = PyObject_CallObject((PyObject *)&py_freeradius_state_def, NULL);
1567 Py_INCREF(&py_freeradius_state_def);
1568
1569 if (PyModule_AddObject(module, "__State", p_state) < 0) {
1570 Py_DECREF(&py_freeradius_state_def);
1571 Py_DECREF(module);
1572 goto error;
1573 }
1574
1575 /*
1576 * For "Pair" we're inserting an object definition
1577 * as opposed to the object instance we inserted
1578 * for inst.
1579 */
1580 Py_INCREF(&py_freeradius_pair_def);
1581 if (PyModule_AddObject(module, "Pair", (PyObject *)&py_freeradius_pair_def) < 0) {
1582 Py_DECREF(&py_freeradius_pair_def);
1583 Py_DECREF(module);
1584 goto error;
1585 }
1586 pthread_mutex_unlock(&init_lock);
1587
1588 return module;
1589}
1590
1592{
1593 rlm_python_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1594 CONF_SECTION *conf = mctx->mi->conf;
1595 PyObject *module;
1596
1597 /*
1598 * python_module_init takes no args, so we need
1599 * to set these globals so that when it's
1600 * called during interpreter initialisation
1601 * it can get at the current instance config.
1602 */
1605
1606 PyEval_RestoreThread(global_interpreter);
1607 LSAN_DISABLE(inst->interpreter = Py_NewInterpreter());
1608 if (!inst->interpreter) {
1609 ERROR("Failed creating new interpreter");
1610 return -1;
1611 }
1612 DEBUG3("Created new interpreter %p", inst->interpreter);
1613 PyEval_SaveThread(); /* Unlock GIL */
1614
1615 PyEval_RestoreThread(inst->interpreter);
1616
1617 /*
1618 * Import the radiusd module into this python
1619 * environment. Each interpreter gets its
1620 * own copy which it can mutate as much as
1621 * it wants.
1622 */
1623 module = PyImport_ImportModule("freeradius");
1624 if (!module) {
1625 ERROR("Failed importing \"freeradius\" module into interpreter %p", inst->interpreter);
1626 return -1;
1627 }
1628 if ((python_module_import_config(inst, conf, module) < 0) ||
1629 (python_module_import_constants(inst, module) < 0)) {
1630 Py_DECREF(module);
1631 return -1;
1632 }
1633 inst->module = module;
1634 PyEval_SaveThread();
1635
1636 return 0;
1637}
1638
1639static void python_interpreter_free(rlm_python_t *inst, PyThreadState *interp)
1640{
1641 PyEval_RestoreThread(interp); /* Switches thread state and locks GIL */
1642
1643 /*
1644 * We incremented the reference count earlier
1645 * during module initialisation.
1646 */
1647 Py_XDECREF(inst->module);
1648
1649 Py_EndInterpreter(interp); /* Destroys interpreter (GIL still locked) - sets thread state to NULL */
1650 PyThreadState_Swap(global_interpreter); /* Get a none-null thread state */
1651 PyEval_SaveThread(); /* Unlock GIL */
1652}
1653
1654/*
1655 * Do any per-module initialization that is separate to each
1656 * configured instance of the module. e.g. set up connections
1657 * to external databases, read configuration files, set up
1658 * dictionary entries, etc.
1659 *
1660 * If configuration information is given in the config section
1661 * that must be referenced in later calls, store a handle to it
1662 * in *instance otherwise put a null pointer there.
1663 *
1664 */
1665static int mod_instantiate(module_inst_ctx_t const *mctx)
1666{
1667 rlm_python_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1668 python_func_def_t *func = NULL;
1670 CONF_PAIR *cp;
1671 char *pair_name;
1672
1673 if (inst->interpreter) return 0;
1674 if (python_interpreter_init(mctx) < 0) return -1;
1675 inst->name = mctx->mi->name;
1676 if (!inst->funcs_init) fr_rb_inline_init(&inst->funcs, python_func_def_t, node, python_func_def_cmp, NULL);
1677 /*
1678 * Switch to our module specific interpreter
1679 */
1680 PyEval_RestoreThread(inst->interpreter);
1681
1682 /*
1683 * Process the various sections
1684 */
1685#define PYTHON_FUNC_LOAD(_x) if (python_function_load(mctx, &inst->_x) < 0) goto error
1687 PYTHON_FUNC_LOAD(detach);
1688
1689 /*
1690 * Load all the Python functions found by the call_env parser.
1691 */
1692 func = fr_rb_iter_init_inorder(&iter, &inst->funcs);
1693 while (func) {
1694 /*
1695 * Check for mod_<name1>_<name2> or mod_<name1> config pairs.
1696 * If neither exist, fall back to default Python module.
1697 */
1698 if (func->name2) {
1699 pair_name = talloc_asprintf(func, "mod_%s_%s", func->name1, func->name2);
1700 cp = cf_pair_find(mctx->mi->conf, pair_name);
1701 talloc_free(pair_name);
1702 if (cp) goto found_mod;
1703 }
1704 pair_name = talloc_asprintf(func, "mod_%s", func->name1);
1705 cp = cf_pair_find(mctx->mi->conf, pair_name);
1706 talloc_free(pair_name);
1707 found_mod:
1708 func->module_name = cp ? cf_pair_value(cp) : inst->def_module_name;
1709
1710 /*
1711 * Check for func_<name1>_<name2> or func_<name1> function overrides.
1712 * Checks for Python functions <name1>_<name2> and <name1> are done
1713 * in python_function_load.
1714 */
1715 if (func->name2) {
1716 pair_name = talloc_asprintf(func, "func_%s_%s", func->name1, func->name2);
1717 cp = cf_pair_find(mctx->mi->conf, pair_name);
1718 talloc_free(pair_name);
1719 if (cp) goto found_func;
1720 }
1721 pair_name = talloc_asprintf(func, "func_%s", func->name1);
1722 cp = cf_pair_find(mctx->mi->conf, pair_name);
1723 talloc_free(pair_name);
1724 found_func:
1725 if (cp) func->function_name = cf_pair_value(cp);
1726
1727 if (python_function_load(mctx, func) < 0) goto error;
1728 func = fr_rb_iter_next_inorder(&iter);
1729 }
1730
1731 /*
1732 * Call the instantiate function.
1733 */
1734 if (inst->instantiate.function) {
1735 rlm_rcode_t rcode;
1736
1737 do_python_single(&rcode, MODULE_CTX_FROM_INST(mctx), NULL, inst->instantiate.function, "instantiate");
1738 switch (rcode) {
1739 case RLM_MODULE_FAIL:
1740 case RLM_MODULE_REJECT:
1741 error:
1742 fr_cond_assert(PyEval_SaveThread() == inst->interpreter);
1743 python_interpreter_free(inst, inst->interpreter);
1744 return -1;
1745
1746 default:
1747 break;
1748 }
1749 }
1750
1751 /*
1752 * Switch back to the global interpreter
1753 */
1754 if (!fr_cond_assert(PyEval_SaveThread() == inst->interpreter)) goto error;
1755
1756 return 0;
1757}
1758
1759static int mod_detach(module_detach_ctx_t const *mctx)
1760{
1761 rlm_python_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1762 python_func_def_t *func = NULL;
1764
1765 /*
1766 * If we don't have a interpreter
1767 * we didn't get far enough into
1768 * instantiation to generate things
1769 * we need to clean up...
1770 */
1771 if (!inst->interpreter) return 0;
1772
1773 /*
1774 * Call module destructor
1775 */
1776 PyEval_RestoreThread(inst->interpreter);
1777
1778 /*
1779 * We don't care if this fails.
1780 */
1781 if (inst->detach.function) {
1782 rlm_rcode_t rcode;
1783
1784 (void)do_python_single(&rcode, MODULE_CTX_FROM_INST(mctx), NULL, inst->detach.function, "detach");
1785 }
1786
1787#define PYTHON_FUNC_DESTROY(_x) python_function_destroy(&inst->_x)
1789 PYTHON_FUNC_DESTROY(detach);
1790
1791 func = fr_rb_iter_init_inorder(&iter, &inst->funcs);
1792 while (func) {
1794 func = fr_rb_iter_next_inorder(&iter);
1795 }
1796
1797 PyEval_SaveThread();
1798
1799 /*
1800 * Free the module specific interpreter
1801 */
1802 python_interpreter_free(inst, inst->interpreter);
1803
1804 return 0;
1805}
1806
1808{
1809 rlm_python_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_python_t);
1810 rlm_python_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_python_thread_t);
1811
1812 PyThreadState *t_state;
1813 PyObject *t_dict;
1814 PyObject *p_state;
1815
1816 current_t = t;
1817
1818 t->inst = inst;
1819 t_state = PyThreadState_New(inst->interpreter->interp);
1820 if (!t_state) {
1821 ERROR("Failed initialising local PyThreadState");
1822 return -1;
1823 }
1824
1825 PyEval_RestoreThread(t_state); /* Switches thread state and locks GIL */
1826 t_dict = PyThreadState_GetDict();
1827 if (unlikely(!t_dict)) {
1828 ERROR("Failed getting PyThreadState dictionary");
1829 error:
1830 PyEval_SaveThread(); /* Unlock GIL */
1831 PyThreadState_Delete(t_state);
1832
1833 return -1;
1834 }
1835
1836 /*
1837 * Instantiate a new instance object which captures
1838 * the global and thread instances, and associates
1839 * them with the thread.
1840 */
1841 p_state = PyObject_CallObject((PyObject *)&py_freeradius_state_def, NULL);
1842 if (unlikely(!p_state)) {
1843 ERROR("Failed instantiating module instance information object");
1844 goto error;
1845 }
1846
1847 if (unlikely(PyDict_SetItemString(t_dict, "__State", p_state) < 0)) {
1848 ERROR("Failed setting module instance information in thread dict");
1849 goto error;
1850 }
1851
1852 DEBUG3("Initialised PyThreadState %p", t_state);
1853 t->state = t_state;
1854 PyEval_SaveThread(); /* Unlock GIL */
1855
1856 return 0;
1857}
1858
1860{
1861 rlm_python_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_python_thread_t);
1862
1863 PyEval_RestoreThread(t->state); /* Swap in our local thread state */
1864 PyThreadState_Clear(t->state);
1865 PyEval_SaveThread();
1866
1867 PyThreadState_Delete(t->state); /* Don't need to hold lock for this */
1868
1869 return 0;
1870}
1871
1872static int libpython_init(void)
1873{
1874#define LOAD_INFO(_fmt, ...) fr_log(LOG_DST, L_INFO, __FILE__, __LINE__, "rlm_python - " _fmt, ## __VA_ARGS__)
1875#define LOAD_WARN(_fmt, ...) fr_log_perror(LOG_DST, L_WARN, __FILE__, __LINE__, \
1876 &(fr_log_perror_format_t){ \
1877 .first_prefix = "rlm_python - ", \
1878 .subsq_prefix = "rlm_python - ", \
1879 }, \
1880 _fmt, ## __VA_ARGS__)
1881 PyConfig config;
1882 PyStatus status;
1883 wchar_t *wide_name;
1884
1885 fr_assert(!Py_IsInitialized());
1886
1887 LOAD_INFO("Python version: %s", Py_GetVersion());
1888 dependency_version_number_add(NULL, "python", Py_GetVersion());
1889
1890 /*
1891 * Load python using RTLD_GLOBAL and dlopen.
1892 * This fixes issues where python C extensions
1893 * can't find the symbols they need.
1894 */
1895 python_dlhandle = dl_open_by_sym("Py_IsInitialized", RTLD_NOW | RTLD_GLOBAL);
1896 if (!python_dlhandle) LOAD_WARN("Failed loading libpython symbols into global symbol table");
1897
1898 PyConfig_InitPythonConfig(&config);
1899
1900 /*
1901 * Set program name (i.e. the software calling the interpreter)
1902 * The value of argv[0] as a wide char string
1903 */
1904 wide_name = Py_DecodeLocale(main_config->name, NULL);
1905 status = PyConfig_SetString(&config, &config.program_name, wide_name);
1906 PyMem_RawFree(wide_name);
1907
1908 if (PyStatus_Exception(status)) {
1909 fail:
1910 LOAD_WARN("%s", status.err_msg);
1911 PyConfig_Clear(&config);
1912 return -1;
1913 }
1914
1915 /*
1916 * Python 3 introduces the concept of a
1917 * "inittab", i.e. a list of modules which
1918 * are automatically created when the first
1919 * interpreter is spawned.
1920 */
1921 PyImport_AppendInittab("freeradius", python_module_init);
1922
1924 wchar_t *wide_path = Py_DecodeLocale(libpython_global_config.path, NULL);
1925
1927 /*
1928 * The path from config is to be used in addition to the default.
1929 * Set it in the pythonpath_env.
1930 */
1931 status = PyConfig_SetString(&config, &config.pythonpath_env, wide_path);
1932 } else {
1933 /*
1934 * Only the path from config is to be used.
1935 * Setting module_search_paths_set to 1 disables any automatic paths.
1936 */
1937 config.module_search_paths_set = 1;
1938 status = PyWideStringList_Append(&config.module_search_paths, wide_path);
1939 }
1940 PyMem_RawFree(wide_path);
1941 if (PyStatus_Exception(status)) goto fail;
1942 }
1943
1944 config.install_signal_handlers = 0; /* Don't override signal handlers - noop on subs calls */
1945
1946 if (libpython_global_config.verbose) config.verbose = 1; /* Enable libpython logging*/
1947
1948 LSAN_DISABLE(status = Py_InitializeFromConfig(&config));
1949 if (PyStatus_Exception(status)) goto fail;
1950
1951 PyConfig_Clear(&config);
1952
1953 global_interpreter = PyEval_SaveThread(); /* Store reference to the main interpreter and release the GIL */
1954
1955 return 0;
1956}
1957
1958static void libpython_free(void)
1959{
1960 PyThreadState_Swap(global_interpreter); /* Swap to the main thread */
1961
1962 /*
1963 * PyImport_Cleanup - Leaks memory in python 3.6
1964 * should check once we require 3.8 that this is
1965 * still needed.
1966 */
1967 LSAN_DISABLE(Py_Finalize()); /* Ignore leaks on exit, we don't reload modules so we don't care */
1968 if (python_dlhandle) dlclose(python_dlhandle); /* dlclose will SEGV on null handle */
1969}
1970
1971/*
1972 * Restrict automatic Python function names to lowercase characters, numbers and underscore
1973 * meaning that a module call in `recv Access-Request` will look for `recv_access_request`
1974 */
1975static void python_func_name_safe(char *name) {
1976 char *p;
1977 size_t i;
1978
1979 p = name;
1980 for (i = 0; i < talloc_array_length(name); i++) {
1981 *p = tolower(*p);
1982 if (!strchr("abcdefghijklmnopqrstuvwxyz1234567890", *p)) *p = '_';
1983 p++;
1984 }
1985}
1986
1987static int python_func_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, UNUSED tmpl_rules_t const *t_rules,
1988 UNUSED CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
1989{
1990 rlm_python_t *inst = talloc_get_type_abort(cec->mi->data, rlm_python_t);
1991 call_env_parsed_t *parsed;
1992 python_func_def_t *func;
1993 void *found;
1994
1995 if (!inst->funcs_init) {
1997 inst->funcs_init = true;
1998 }
1999
2000 MEM(parsed = call_env_parsed_add(ctx, out,
2002 .name = "func",
2003 .flags = CALL_ENV_FLAG_PARSE_ONLY,
2004 .pair = {
2005 .parsed = {
2006 .offset = rule->pair.offset,
2008 }
2009 }
2010 }));
2011
2012 MEM(func = talloc_zero(inst, python_func_def_t));
2013 func->name1 = talloc_strdup(func, cec->asked->name1);
2015 if (cec->asked->name2) {
2016 func->name2 = talloc_strdup(func, cec->asked->name2);
2018 }
2019
2020 if (fr_rb_find_or_insert(&found, &inst->funcs, func) < 0) {
2021 talloc_free(func);
2022 return -1;
2023 }
2024
2025 /*
2026 * If the function call is already in the tree, use that entry.
2027 */
2028 if (found) {
2029 talloc_free(func);
2030 call_env_parsed_set_data(parsed, found);
2031 } else {
2032 call_env_parsed_set_data(parsed, func);
2033 }
2034 return 0;
2035}
2036
2044
2045/*
2046 * The module name should be the only globally exported symbol.
2047 * That is, everything else should be 'static'.
2048 *
2049 * If the module needs to temporarily modify it's instantiation
2050 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
2051 * The server will then take care of ensuring that the module
2052 * is single-threaded.
2053 */
2056 .common = {
2057 .magic = MODULE_MAGIC_INIT,
2058 .name = "python",
2059
2060 .inst_size = sizeof(rlm_python_t),
2061 .thread_inst_size = sizeof(rlm_python_thread_t),
2062
2063 .config = module_config,
2064
2065 .instantiate = mod_instantiate,
2066 .detach = mod_detach,
2067
2068 .thread_instantiate = mod_thread_instantiate,
2069 .thread_detach = mod_thread_detach
2070 },
2071 .method_group = {
2072 .bindings = (module_method_binding_t[]){
2073 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_python, .method_env = &python_method_env },
2075 }
2076 }
2077};
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:644
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:701
#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:3006
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: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:580
@ 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:958
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:672
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:1468
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:2591
fr_pair_list_t * fr_pair_parent_list(fr_pair_t const *vp)
Return a pointer to the parent pair list.
Definition pair.c:929
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:695
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:743
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:1828
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:62
#define RETURN_MODULE_RCODE(_rcode)
Definition rcode.h:64
#define RETURN_MODULE_FAIL
Definition rcode.h:56
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_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_NUMCODES
How many valid return codes there are.
Definition rcode.h:50
@ 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:428
static int libpython_init(void)
static PyMethodDef py_freeradius_methods[]
Definition rlm_python.c:381
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:273
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:662
#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:259
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:474
static rlm_python_t const * rlm_python_get_inst(void)
Return the rlm_python instance associated with the current interpreter.
Definition rlm_python.c:449
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:505
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:461
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:352
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:561
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:412
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:522
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:293
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:487
#define _PyCFunction_CAST(func)
Definition rlm_python.c:375
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:618
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:320
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:401
static void * python_dlhandle
Definition rlm_python.c:130
static struct @175 freeradius_constants[]
static PyTypeObject py_freeradius_pair_def
The class which all pair types inherit from.
Definition rlm_python.c:246
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:363
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:308
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:224
static fr_slen_t parent
Definition pair.h:845
#define FR_TYPE_NON_LEAF
Definition types.h:298
#define fr_type_is_leaf(_x)
Definition types.h:372
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:433
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:5487
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:3904
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:3946
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:4382
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:4623
static size_t char ** out
Definition value.h:1012