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