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