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