port fix from branch_1_1
[freeradius.git] / src / modules / rlm_python / rlm_python.c
1 /*
2  * rlm_python.c
3  *
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  *
19  * Copyright 2000,2006  The FreeRADIUS server project
20  * Copyright 2002  Miguel A.L. Paraz <mparaz@mparaz.com>
21  * Copyright 2002  Imperium Technology, Inc.
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <Python.h>
28
29 #include <freeradius-devel/autoconf.h>
30
31 #include <stdio.h>
32 #include <stdlib.h>
33
34 #include <freeradius-devel/radiusd.h>
35 #include <freeradius-devel/modules.h>
36 #include <freeradius-devel/conffile.h>
37
38 /*
39  *      Define a structure for our module configuration.
40  *
41  *      These variables do not need to be in a structure, but it's
42  *      a lot cleaner to do so, and a pointer to the structure can
43  *      be used as the instance handle.
44  */
45 typedef struct rlm_python_t {
46     /* Config section */
47
48     /* Names of modules */
49     char
50         *mod_instantiate,
51         *mod_authorize,
52         *mod_authenticate,
53         *mod_preacct,
54         *mod_accounting,
55         *mod_checksimul,
56         *mod_detach,
57
58     /* Names of functions */
59         *func_instantiate,
60         *func_authorize,
61         *func_authenticate,
62         *func_preacct,
63         *func_accounting,
64         *func_checksimul,
65         *func_detach;
66
67
68     /* End Config section */
69
70
71     /* Python objects for modules */
72     PyObject
73         *pModule_builtin,
74         *pModule_instantiate,
75         *pModule_authorize,
76         *pModule_authenticate,
77         *pModule_preacct,
78         *pModule_accounting,
79         *pModule_checksimul,
80         *pModule_detach,
81
82
83         /* Functions */
84
85         *pFunc_instantiate,
86         *pFunc_authorize,
87         *pFunc_authenticate,
88         *pFunc_preacct,
89         *pFunc_accounting,
90         *pFunc_checksimul,
91         *pFunc_detach;
92
93 } rlm_python_t;
94
95 /*
96  *      A mapping of configuration file names to internal variables.
97  *
98  *      Note that the string is dynamically allocated, so it MUST
99  *      be freed.  When the configuration file parse re-reads the string,
100  *      it free's the old one, and strdup's the new one, placing the pointer
101  *      to the strdup'd string into 'config.string'.  This gets around
102  *      buffer over-flows.
103  */
104 static const CONF_PARSER module_config[] = {
105   { "mod_instantiate",  PW_TYPE_STRING_PTR,
106     offsetof(rlm_python_t, mod_instantiate), NULL,  NULL},
107   { "func_instantiate",  PW_TYPE_STRING_PTR,
108     offsetof(rlm_python_t, func_instantiate), NULL,  NULL},
109
110   { "mod_authorize",  PW_TYPE_STRING_PTR,
111     offsetof(rlm_python_t, mod_authorize), NULL,  NULL},
112   { "func_authorize",  PW_TYPE_STRING_PTR,
113     offsetof(rlm_python_t, func_authorize), NULL,  NULL},
114
115   { "mod_authenticate",  PW_TYPE_STRING_PTR,
116     offsetof(rlm_python_t, mod_authenticate), NULL,  NULL},
117   { "func_authenticate",  PW_TYPE_STRING_PTR,
118     offsetof(rlm_python_t, func_authenticate), NULL,  NULL},
119
120   { "mod_preacct",  PW_TYPE_STRING_PTR,
121     offsetof(rlm_python_t, mod_preacct), NULL,  NULL},
122   { "func_preacct",  PW_TYPE_STRING_PTR,
123     offsetof(rlm_python_t, func_preacct), NULL,  NULL},
124
125   { "mod_accounting",  PW_TYPE_STRING_PTR,
126     offsetof(rlm_python_t, mod_accounting), NULL,  NULL},
127   { "func_accounting",  PW_TYPE_STRING_PTR,
128     offsetof(rlm_python_t, func_accounting), NULL,  NULL},
129
130   { "mod_checksimul",  PW_TYPE_STRING_PTR,
131     offsetof(rlm_python_t, mod_checksimul), NULL,  NULL},
132   { "func_checksimul",  PW_TYPE_STRING_PTR,
133     offsetof(rlm_python_t, func_checksimul), NULL,  NULL},
134
135   { "mod_detach",  PW_TYPE_STRING_PTR,
136     offsetof(rlm_python_t, mod_detach), NULL,  NULL},
137   { "func_detach",  PW_TYPE_STRING_PTR,
138     offsetof(rlm_python_t, func_detach), NULL,  NULL},
139
140
141   { NULL, -1, 0, NULL, NULL }           /* end the list */
142 };
143
144 /*
145  * radiusd Python functions
146  */
147
148 /* radlog wrapper */
149 static PyObject *radlog_py(const PyObject *self, PyObject *args) {
150     int status;
151     char *msg;
152
153     if (!PyArg_ParseTuple(args, "is", &status, &msg)) {
154         return NULL;
155     }
156
157     radlog(status, msg);
158     return Py_None;
159 }
160
161 static PyMethodDef radiusd_methods[] = {
162     {"radlog", (PyCFunction)radlog_py, METH_VARARGS, "freeradius radlog()."},
163     {NULL, NULL, 0, NULL}
164 };
165
166
167 /* Extract string representation of Python error. */
168 static void python_error(void) {
169     PyObject *pType, *pValue, *pTraceback, *pStr1, *pStr2;
170
171     PyErr_Fetch(&pType, &pValue, &pTraceback);
172     pStr1 = PyObject_Str(pType);
173     pStr2 = PyObject_Str(pValue);
174
175     radlog(L_ERR, "%s: %s\n",
176            PyString_AsString(pStr1), PyString_AsString(pStr2));
177 }
178
179 /* Tuple to value pair conversion */
180 static void add_vp_tuple(VALUE_PAIR **vpp, PyObject *pValue,
181                          const char *function_name) {
182     int i, outertuplesize;
183     VALUE_PAIR  *vp;
184
185     /* If the Python function gave us None for the tuple, then just return. */
186     if (pValue == Py_None) {
187         return;
188     }
189
190     if (!PyTuple_Check(pValue)) {
191         radlog(L_ERR, "%s: non-tuple passed", function_name);
192     }
193
194     /* Get the tuple size. */
195     outertuplesize = PyTuple_Size(pValue);
196
197     for (i = 0; i < outertuplesize; i++) {
198         PyObject *pTupleElement = PyTuple_GetItem(pValue, i);
199
200         if ((pTupleElement != NULL) &&
201             (PyTuple_Check(pTupleElement))) {
202
203             /* Check if it's a pair */
204             int tuplesize;
205
206             if ((tuplesize = PyTuple_Size(pTupleElement)) != 2) {
207                 radlog(L_ERR, "%s: tuple element %d is a tuple "
208                        " of size %d. must be 2\n", function_name,
209                        i, tuplesize);
210             }
211             else {
212                 PyObject *pString1, *pString2;
213
214                 pString1 = PyTuple_GetItem(pTupleElement, 0);
215                 pString2 = PyTuple_GetItem(pTupleElement, 1);
216
217                 /* xxx PyString_Check does not compile here */
218                 if  ((pString1 != NULL) &&
219                      (pString2 != NULL) &&
220                      PyObject_TypeCheck(pString1,&PyString_Type) &&
221                      PyObject_TypeCheck(pString2,&PyString_Type)) {
222
223
224                     const char *s1, *s2;
225
226                     /* pairmake() will convert and find any
227                      * errors in the pair.
228                      */
229
230                     s1 = PyString_AsString(pString1);
231                     s2 = PyString_AsString(pString2);
232
233                     if ((s1 != NULL) && (s2 != NULL)) {
234                         radlog(L_DBG, "%s: %s = %s ",
235                                function_name, s1, s2);
236
237                         /* xxx Might need to support other T_OP */
238                         vp = pairmake(s1, s2, T_OP_EQ);
239                         if (vp != NULL) {
240                             pairadd(vpp, vp);
241                             radlog(L_DBG, "%s: s1, s2 OK\n",
242                                    function_name);
243                         }
244                         else {
245                             radlog(L_DBG, "%s: s1, s2 FAILED\n",
246                                    function_name);
247                         }
248                     }
249                     else {
250                         radlog(L_ERR, "%s: string conv failed\n",
251                                function_name);
252                     }
253
254                 }
255                 else {
256                     radlog(L_ERR, "%s: tuple element %d must be "
257                            "(string, string)", function_name, i);
258                 }
259             }
260         }
261         else {
262             radlog(L_ERR, "%s: tuple element %d is not a tuple\n",
263                    function_name, i);
264         }
265     }
266
267 }
268
269 /* This is the core Python function that the others wrap around.
270  * Pass the value-pair print strings in a tuple.
271  * xxx We're not checking the errors. If we have errors, what do we do?
272  */
273
274 static int python_function(REQUEST *request,
275                            PyObject *pFunc, const char *function_name)
276 {
277 #define BUF_SIZE 1024
278
279     char buf[BUF_SIZE];         /* same size as vp_print buffer */
280
281     VALUE_PAIR  *vp;
282
283     PyObject *pValue, *pValuePairContainer, **pValueHolder, **pValueHolderPtr;
284     int i, n_tuple, return_value;
285
286     /* Return with "OK, continue" if the function is not defined. */
287     if (pFunc == NULL) {
288         return RLM_MODULE_OK;
289     }
290
291     /* Default return value is "OK, continue" */
292     return_value = RLM_MODULE_OK;
293
294     /* We will pass a tuple containing (name, value) tuples
295      * We can safely use the Python function to build up a tuple,
296      * since the tuple is not used elsewhere.
297      *
298      * Determine the size of our tuple by walking through the packet.
299      * If request is NULL, pass None.
300      */
301     n_tuple = 0;
302
303     if (request != NULL) {
304         for (vp = request->packet->vps; vp; vp = vp->next) {
305             n_tuple++;
306         }
307     }
308
309     /* Create the tuple and a holder for the pointers, so that we can
310      * decref more efficiently later without the overhead of reading
311      * the tuple.
312      *
313      * We use malloc() instead of the Python memory allocator since we
314      * are not embedded.
315      */
316
317     if (NULL == (pValueHolder = pValueHolderPtr =
318                  malloc(sizeof(PyObject *) * n_tuple))) {
319
320         radlog(L_ERR, "%s: malloc of %d bytes failed\n",
321                function_name, sizeof(PyObject *) * n_tuple);
322
323         return -1;
324     }
325
326     if (n_tuple == 0) {
327         pValuePairContainer = Py_None;
328     }
329     else {
330         pValuePairContainer = PyTuple_New(n_tuple);
331
332         i = 0;
333         for (vp = request->packet->vps; vp; vp = vp->next) {
334             PyObject *pValuePair, *pString1, *pString2;
335
336             /* The inside tuple has two only: */
337             pValuePair = PyTuple_New(2);
338
339             /* The name. logic from vp_prints, lib/print.c */
340             if (vp->flags.has_tag) {
341                 snprintf(buf, BUF_SIZE, "%s:%d", vp->name, vp->flags.tag);
342             }
343             else {
344                 strcpy(buf, vp->name);
345             }
346
347             pString1 = PyString_FromString(buf);
348             PyTuple_SetItem(pValuePair, 0, pString1);
349
350
351             /* The value. Use delimiter - don't know what that means */
352             vp_prints_value(buf, sizeof(buf), vp, 1);
353             pString2 = PyString_FromString(buf);
354             PyTuple_SetItem(pValuePair, 1, pString2);
355
356             /* Put the tuple inside the container */
357             PyTuple_SetItem(pValuePairContainer, i++, pValuePair);
358
359             /* Store the pointer in our malloc() storage */
360             *pValueHolderPtr++ = pValuePair;
361         }
362     }
363
364
365     /* Call Python function.
366      */
367
368     if (pFunc && PyCallable_Check(pFunc)) {
369         PyObject *pArgs;
370
371         /* call the function with a singleton tuple containing the
372          * container tuple.
373          */
374
375         if ((pArgs = PyTuple_New(1)) == NULL) {
376             radlog(L_ERR, "%s: could not create tuple", function_name);
377             return -1;
378         }
379         if ((PyTuple_SetItem(pArgs, 0, pValuePairContainer)) != 0) {
380             radlog(L_ERR, "%s: could not set tuple item", function_name);
381             return -1;
382         }
383
384         if ((pValue = PyObject_CallObject(pFunc, pArgs)) == NULL) {
385             radlog(L_ERR, "%s: function call failed", function_name);
386             python_error();
387             return -1;
388         }
389
390         /* The function returns either:
391          *  1. tuple containing the integer return value,
392          *  then the integer reply code (or None to not set),
393          *  then the string tuples to build the reply with.
394          *     (returnvalue, (p1, s1), (p2, s2))
395          *
396          *  2. the function return value alone
397          *
398          *  3. None - default return value is set
399          *
400          * xxx This code is messy!
401          */
402
403         if (PyTuple_Check(pValue)) {
404             PyObject *pTupleInt;
405
406             if (PyTuple_Size(pValue) != 3) {
407                 radlog(L_ERR, "%s: tuple must be " \
408                        "(return, replyTuple, configTuple)",
409                        function_name);
410
411             }
412             else {
413                 pTupleInt = PyTuple_GetItem(pValue, 0);
414
415                 if ((pTupleInt == NULL) || !PyInt_Check(pTupleInt)) {
416                     radlog(L_ERR, "%s: first tuple element not an integer",
417                            function_name);
418                 }
419                 else {
420                     /* Now have the return value */
421                     return_value = PyInt_AsLong(pTupleInt);
422
423                     /* Reply item tuple */
424                     add_vp_tuple(&request->reply->vps,
425                                  PyTuple_GetItem(pValue, 1), function_name);
426
427                     /* Config item tuple */
428                     add_vp_tuple(&request->config_items,
429                                  PyTuple_GetItem(pValue, 2), function_name);
430                 }
431             }
432         }
433         else if (PyInt_Check(pValue)) {
434             /* Just an integer */
435             return_value = PyInt_AsLong(pValue);
436         }
437         else if (pValue == Py_None) {
438             /* returned 'None', return value defaults to "OK, continue." */
439             return_value = RLM_MODULE_OK;
440         }
441         else {
442             /* Not tuple or None */
443             radlog(L_ERR, "%s function did not return a tuple or None\n",
444                    function_name);
445         }
446
447
448         /* Decrease reference counts for the argument and return tuple */
449         Py_DECREF(pArgs);
450         Py_DECREF(pValue);
451     }
452
453     /* Decrease reference count for the tuples passed, the
454      * container tuple, and the return value.
455      */
456
457     pValueHolderPtr = pValueHolder;
458     i = n_tuple;
459     while (i--) {
460         /* Can't write as pValueHolderPtr since Py_DECREF is a macro */
461         Py_DECREF(*pValueHolderPtr);
462         pValueHolderPtr++;
463     }
464     free(pValueHolder);
465     Py_DECREF(pValuePairContainer);
466
467     /* pDict and pFunc are borrowed and must not be Py_DECREF-ed */
468
469     /* Free pairs if we are rejecting.
470      * xxx Shouldn't the core do that?
471      */
472
473     if ((return_value == RLM_MODULE_REJECT) && (request != NULL)) {
474         pairfree(&(request->reply->vps));
475     }
476
477     /* Return the specified by the Python module */
478     return return_value;
479 }
480
481
482 static struct varlookup {
483         const char*     name;
484         int             value;
485 } constants[] = {
486         { "L_DBG",              L_DBG                   },
487         { "L_AUTH",             L_AUTH                  },
488         { "L_INFO",             L_INFO                  },
489         { "L_ERR",              L_ERR                   },
490         { "L_PROXY",            L_PROXY                 },
491         { "L_CONS",             L_CONS                  },
492         { "RLM_MODULE_REJECT",  RLM_MODULE_REJECT       },
493         { "RLM_MODULE_FAIL",    RLM_MODULE_FAIL         },
494         { "RLM_MODULE_OK",      RLM_MODULE_OK           },
495         { "RLM_MODULE_HANDLED", RLM_MODULE_HANDLED      },
496         { "RLM_MODULE_INVALID", RLM_MODULE_INVALID      },
497         { "RLM_MODULE_USERLOCK",RLM_MODULE_USERLOCK     },
498         { "RLM_MODULE_NOTFOUND",RLM_MODULE_NOTFOUND     },
499         { "RLM_MODULE_NOOP",    RLM_MODULE_NOOP         },
500         { "RLM_MODULE_UPDATED", RLM_MODULE_UPDATED      },
501         { "RLM_MODULE_NUMCODES",RLM_MODULE_NUMCODES     },
502         { NULL, 0 },
503 };
504
505 /*
506  * Import a user module and load a function from it
507  */
508 static int load_python_function(const char* module, const char* func,
509                                 PyObject** pyModule, PyObject** pyFunc) {
510
511     if ((module==NULL) || (func==NULL)) {
512         *pyFunc=NULL;
513         *pyModule=NULL;
514     } else {
515         PyObject *pName;
516
517         pName = PyString_FromString(module);
518         Py_INCREF(pName);
519         *pyModule = PyImport_Import(pName);
520         Py_DECREF(pName);
521         if (*pyModule != NULL) {
522             PyObject *pDict;
523
524             pDict = PyModule_GetDict(*pyModule);
525             /* pDict: borrowed reference */
526
527             *pyFunc = PyDict_GetItemString(pDict, func);
528             /* pFunc: Borrowed reference */
529         } else {
530             python_error();
531
532             radlog(L_ERR, "Failed to import python module \"%s\"\n", module);
533             return -1;
534         }
535     }
536
537     return 0;
538 }
539
540
541 /*
542  *      Do any per-module initialization that is separate to each
543  *      configured instance of the module.  e.g. set up connections
544  *      to external databases, read configuration files, set up
545  *      dictionary entries, etc.
546  *
547  *      If configuration information is given in the config section
548  *      that must be referenced in later calls, store a handle to it
549  *      in *instance otherwise put a null pointer there.
550  *
551  */
552 static int python_instantiate(CONF_SECTION *conf, void **instance)
553 {
554     rlm_python_t *data;
555     PyObject *module;
556     int idx;
557
558     /*
559      * Initialize Python interpreter. Fatal error if this fails.
560      */
561     Py_Initialize();
562
563     /*
564      *  Set up a storage area for instance data
565      */
566     data = rad_malloc(sizeof(*data));
567     if (!data) {
568       return -1;
569     }
570     memset(data, 0, sizeof(*data));
571
572     /*
573          *      If the configuration parameters can't be parsed, then
574          *      fail.
575          */
576     if (cf_section_parse(conf, data, module_config) < 0) {
577         free(data);
578         return -1;
579     }
580
581
582     /*
583      * Setup our 'radiusd' module.
584      */
585
586     /* Code */
587     if ((module = data->pModule_builtin =
588          Py_InitModule3("radiusd", radiusd_methods,
589                         "FreeRADIUS Module.")) == NULL) {
590
591         radlog(L_ERR, "Python Py_InitModule3 failed");
592         free(data);
593         return -1;
594     }
595
596     /*
597      * Load constants into module
598      */
599     for (idx=0; constants[idx].name; idx++)
600         if ((PyModule_AddIntConstant(module, constants[idx].name, constants[idx].value)) == -1) {
601
602             radlog(L_ERR, "Python AddIntConstant failed");
603         }
604
605
606     /*
607      * Import user modules.
608      */
609
610     if (load_python_function(data->mod_instantiate, data->func_instantiate,
611                 &data->pModule_instantiate, &data->pFunc_instantiate)==-1) {
612         /* TODO: check if we need to cleanup data */
613         return -1;
614     }
615
616     if (load_python_function(data->mod_authenticate, data->func_authenticate,
617                 &data->pModule_authenticate, &data->pFunc_authenticate)==-1) {
618         /* TODO: check if we need to cleanup data */
619         return -1;
620     }
621
622     if (load_python_function(data->mod_authorize, data->func_authorize,
623                 &data->pModule_authorize, &data->pFunc_authorize)==-1) {
624         /* TODO: check if we need to cleanup data */
625         return -1;
626     }
627
628     if (load_python_function(data->mod_preacct, data->func_preacct,
629                 &data->pModule_preacct, &data->pFunc_preacct)==-1) {
630         /* TODO: check if we need to cleanup data */
631         return -1;
632     }
633
634     if (load_python_function(data->mod_accounting, data->func_accounting,
635                 &data->pModule_accounting, &data->pFunc_accounting)==-1) {
636         /* TODO: check if we need to cleanup data */
637         return -1;
638     }
639
640     if (load_python_function(data->mod_checksimul, data->func_checksimul,
641                 &data->pModule_checksimul, &data->pFunc_checksimul)==-1) {
642         /* TODO: check if we need to cleanup data */
643         return -1;
644     }
645
646     if (load_python_function(data->mod_detach, data->func_detach,
647                 &data->pModule_detach, &data->pFunc_detach)==-1) {
648         /* TODO: check if we need to cleanup data */
649         return -1;
650     }
651
652     *instance=data;
653
654     /* Call the instantiate function.  No request.  Use the return value. */
655     return python_function(NULL, data->pFunc_instantiate, "instantiate");
656 }
657
658 /* Wrapper functions */
659 static int python_authorize(void *instance, REQUEST *request)
660 {
661     return python_function(request,
662                            ((struct rlm_python_t *)instance)->pFunc_authorize,
663                            "authorize");
664 }
665
666 static int python_authenticate(void *instance, REQUEST *request)
667 {
668     return python_function(
669         request,
670         ((struct rlm_python_t *)instance)->pFunc_authenticate,
671         "authenticate");
672 }
673
674 static int python_preacct(void *instance, REQUEST *request)
675 {
676     return python_function(
677         request,
678         ((struct rlm_python_t *)instance)->pFunc_preacct,
679         "preacct");
680 }
681
682 static int python_accounting(void *instance, REQUEST *request)
683 {
684     return python_function(
685         request,
686         ((struct rlm_python_t *)instance)->pFunc_accounting,
687         "accounting");
688 }
689
690 static int python_checksimul(void *instance, REQUEST *request)
691 {
692     return python_function(
693         request,
694         ((struct rlm_python_t *)instance)->pFunc_checksimul,
695         "checksimul");
696 }
697
698
699 static int python_detach(void *instance)
700 {
701     int return_value;
702
703     /* Default return value is failure */
704     return_value = -1;
705
706     if (((rlm_python_t *)instance)->pFunc_detach &&
707         PyCallable_Check(((rlm_python_t *)instance)->pFunc_detach)) {
708
709         PyObject *pArgs, *pValue;
710
711         /* call the function with an empty tuple */
712
713         pArgs = PyTuple_New(0);
714         pValue = PyObject_CallObject(((rlm_python_t *)instance)->pFunc_detach,
715                                      pArgs);
716
717         if (pValue == NULL) {
718             python_error();
719             return -1;
720         }
721         else {
722             if (!PyInt_Check(pValue)) {
723                 radlog(L_ERR, "detach: return value not an integer");
724             }
725             else {
726                 return_value = PyInt_AsLong(pValue);
727             }
728         }
729
730         /* Decrease reference counts for the argument and return tuple */
731         Py_DECREF(pArgs);
732         Py_DECREF(pValue);
733     }
734
735     free(instance);
736
737 #if 0
738     /* xxx test delete module object so it will be reloaded later.
739      * xxx useless since we can't SIGHUP reliably, anyway.
740      */
741     PyObject_Del(((struct rlm_python_t *)instance)->pModule_accounting);
742 #endif
743
744     radlog(L_DBG, "python_detach done");
745
746     /* Return the specified by the Python module */
747     return return_value;
748 }
749
750 /*
751  *      The module name should be the only globally exported symbol.
752  *      That is, everything else should be 'static'.
753  *
754  *      If the module needs to temporarily modify it's instantiation
755  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
756  *      The server will then take care of ensuring that the module
757  *      is single-threaded.
758  */
759 module_t rlm_python = {
760         RLM_MODULE_INIT,
761         "python",
762         RLM_TYPE_THREAD_SAFE,           /* type */
763         python_instantiate,             /* instantiation */
764         python_detach,                  /* detach */
765         {
766                 python_authenticate,    /* authentication */
767                 python_authorize,       /* authorization */
768                 python_preacct,         /* preaccounting */
769                 python_accounting,      /* accounting */
770                 python_checksimul,      /* checksimul */
771                 NULL,                   /* pre-proxy */
772                 NULL,                   /* post-proxy */
773                 NULL                    /* post-auth */
774         },
775 };