Require that the modules call talloc for their instance handle.
[freeradius.git] / src / modules / rlm_securid / rlm_securid.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15  
16 /**
17  * $Id$
18  * @file rlm_securid.c
19  * @brief Supports auth against SecurID servers using OTP h/w tokens.
20  *
21  * Supports "next-token code" and "new-pin" modes.
22  *
23  * @copyright 2012  The FreeRADIUS server project
24  * @copyright 2012  Alan DeKok <aland@networkradius.com>
25  */
26 #include <freeradius-devel/ident.h>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include <ctype.h>
30
31 #include "rlm_securid.h"
32
33 typedef enum {
34         RC_SECURID_AUTH_SUCCESS = 0,
35         RC_SECURID_AUTH_FAILURE = -3,
36         RC_SECURID_AUTH_ACCESS_DENIED_FAILURE = -4,
37         RC_SECURID_AUTH_INVALID_SERVER_FAILURE = -5,
38         RC_SECURID_AUTH_CHALLENGE = -17
39 }
40         SECURID_AUTH_RC;
41
42
43 static const CONF_PARSER module_config[] = {
44         { "timer_expire", PW_TYPE_INTEGER,
45           offsetof(rlm_securid_t, timer_limit), NULL, "600"},
46         { "max_sessions", PW_TYPE_INTEGER,
47           offsetof(rlm_securid_t, max_sessions), NULL, "2048"},
48         { "max_trips_per_session", PW_TYPE_INTEGER,
49           offsetof(rlm_securid_t, max_trips_per_session), NULL, NULL},
50         { "max_round_trips", PW_TYPE_INTEGER,
51           offsetof(rlm_securid_t, max_trips_per_session), NULL, "6"},
52         { NULL, -1, 0, NULL, NULL }             /* end the list */
53 };
54
55
56 /* comparison function to find session in the tree */
57 static int securid_session_cmp(const void *a, const void *b)
58 {
59         int rcode;
60         const SECURID_SESSION *one = a;
61         const SECURID_SESSION *two = b;
62
63         rad_assert(one != NULL);
64         rad_assert(two != NULL);
65
66         rcode = fr_ipaddr_cmp(&one->src_ipaddr, &two->src_ipaddr);
67         if (rcode != 0) return rcode;
68
69         return memcmp(one->state, two->state, sizeof(one->state));
70 }
71
72
73 static SECURID_AUTH_RC securidAuth(void *instance, REQUEST *request,
74                                    const char* username, 
75                                    const char* passcode,
76                                    char* replyMsgBuffer,int replyMsgBufferSize)
77 {
78         rlm_securid_t *inst = (rlm_securid_t *) instance;
79         int         acmRet;
80         SD_PIN pinParams;
81         char newPin[10];
82         char format[30];
83         SECURID_SESSION *pSecurid_session=NULL;
84         int rc=-1;
85
86         if (!username) {
87                 radlog(L_ERR, "SecurID username is NULL");
88                 return RC_SECURID_AUTH_FAILURE;         
89         }
90
91         if (!passcode) {
92                 radlog(L_ERR, "SecurID passcode is NULL for %s user",username);
93                 return RC_SECURID_AUTH_FAILURE;         
94         }
95
96         *replyMsgBuffer = '\0';
97
98         pSecurid_session = securid_sessionlist_find(inst,request);
99         if (pSecurid_session == NULL) {
100                 /* securid session not found */
101                 SDI_HANDLE  sdiHandle = SDI_HANDLE_NONE;
102
103                 acmRet = SD_Init(&sdiHandle);
104                 if (acmRet != ACM_OK) {
105                         radlog(L_ERR, "Cannot communicate with the ACE/Server");
106                         return -1;
107                 }
108
109                 acmRet = SD_Lock(sdiHandle, (SD_CHAR*)username);
110                 if (acmRet != ACM_OK) {
111                         radlog(L_ERR,"SecurID: Access denied. Name [%s] lock failed.",username);
112                         return -2;
113                 }
114
115                 acmRet = SD_Check(sdiHandle, (SD_CHAR*) passcode,
116                                   (SD_CHAR*) username);
117                 switch (acmRet) {
118                 case ACM_OK:
119                         /* we are in now */
120                         RDEBUG("SecurID authentication successful for %s.",
121                                username);
122                         SD_Close(sdiHandle);
123
124                         return RC_SECURID_AUTH_SUCCESS;
125
126                 case ACM_ACCESS_DENIED:         
127                         /* not this time */
128                         RDEBUG("SecurID Access denied for %s", username);
129                         SD_Close(sdiHandle);
130                         return RC_SECURID_AUTH_ACCESS_DENIED_FAILURE;
131
132                 case ACM_INVALID_SERVER:
133                         radlog(L_ERR,"SecurID: Invalid ACE server.");
134                         return RC_SECURID_AUTH_INVALID_SERVER_FAILURE;
135
136                 case ACM_NEW_PIN_REQUIRED:
137                         RDEBUG2("SeecurID new pin required for %s",
138                                 username);
139
140                         /* create a new session */
141                         pSecurid_session = securid_session_alloc();
142                         pSecurid_session->sdiHandle = sdiHandle; /* save ACE handle for future use */
143                         pSecurid_session->securidSessionState = NEW_PIN_REQUIRED_STATE;
144                         pSecurid_session->identity = strdup(username);
145                          
146                         /* Get PIN requirements */
147                         acmRet = AceGetPinParams(sdiHandle, &pinParams);
148                          
149                         /* If a system-generated PIN is required */
150                         if (pinParams.Selectable == CANNOT_CHOOSE_PIN) {
151                                 /* Prompt user to accept a system generated PIN */
152                                 snprintf(replyMsgBuffer, replyMsgBufferSize,
153                                          "\r\nAre you prepared to accept a new system-generated PIN [y/n]?");
154                                 pSecurid_session->securidSessionState = NEW_PIN_SYSTEM_ACCEPT_STATE;
155
156                         } else if (pinParams.Selectable == USER_SELECTABLE) { //may be returned by AM 6.x servers.
157                                 snprintf(replyMsgBuffer, replyMsgBufferSize,
158                                          "\r\nPress 'y' to generate a new PIN\r\nOR\r\n'n'to enter a new PIN yourself [y/n]");
159                                 pSecurid_session->securidSessionState = NEW_PIN_USER_SELECT_STATE;
160
161                         } else {
162                                 if (pinParams.Alphanumeric) {
163                                         strcpy(format, "alphanumeric characters");
164                                 } else {
165                                         strcpy(format, "digits");
166                                 }
167                                 snprintf(replyMsgBuffer, replyMsgBufferSize,
168                                          " \r\n   Enter your new PIN of %d to %d %s,\r\n                or\r\n   <Ctrl-D> to cancel the New PIN procedure:",
169                                          pinParams.Min, pinParams.Max, format);
170                         }
171
172                         /* insert new session in the session list */
173                         securid_sessionlist_add(inst,request,pSecurid_session);
174                          
175                         return RC_SECURID_AUTH_CHALLENGE;
176
177                 case ACM_NEXT_CODE_REQUIRED:
178                         RDEBUG2("Next securid token code required for %s",
179                                 username);
180
181                         /* create a new session */
182                         pSecurid_session = securid_session_alloc();
183                         pSecurid_session->sdiHandle = sdiHandle;
184                         pSecurid_session->securidSessionState = NEXT_CODE_REQUIRED_STATE;
185                         pSecurid_session->identity = strdup(username);
186
187                         /* insert new session in the session list */
188                         securid_sessionlist_add(inst,request,pSecurid_session);
189                      
190                         strlcpy(replyMsgBuffer, "\r\nPlease Enter the Next Code from Your Token:", replyMsgBufferSize);
191                         return RC_SECURID_AUTH_CHALLENGE;
192                 default:
193                         radlog(L_ERR,"SecurID: Unexpected error from ACE/Agent API acmRet=%d",acmRet);
194                         return RC_SECURID_AUTH_FAILURE;
195   
196                         
197                 }
198         } else {
199                 /* existing session found */
200                 RDEBUG("Continuing previous session found for user [%s]",username);
201
202                 /* continue previous session */
203                 switch (pSecurid_session->securidSessionState) {
204                 case NEXT_CODE_REQUIRED_STATE:
205                         DEBUG2("Securid NEXT_CODE_REQUIRED_STATE: User [%s]",username);
206                         /* next token code mode */
207
208                         acmRet = SD_Next(pSecurid_session->sdiHandle, (SD_CHAR*)passcode);
209                         if (acmRet == ACM_OK) {
210                                 radlog(L_INFO,"Next SecurID token accepted for [%s].",pSecurid_session->identity);
211                                 rc = RC_SECURID_AUTH_SUCCESS;
212
213                         } else {
214                                 radlog(L_INFO,"SecurID: Next token rejected for [%s].",pSecurid_session->identity);
215                                 rc = RC_SECURID_AUTH_FAILURE;
216                         }
217
218                         /* deallocate session */
219                         securid_session_free(inst,request,pSecurid_session);
220                         return rc;
221
222                 case NEW_PIN_REQUIRED_STATE:
223                         RDEBUG2("SecurID NEW_PIN_REQUIRED_STATE for %s",
224                                 username);
225
226                         /* save the previous pin */
227                         if (pSecurid_session->pin) {
228                                 free(pSecurid_session->pin);
229                                 pSecurid_session->pin = NULL;
230                         }
231                         pSecurid_session->pin = strdup(passcode);
232
233                         strlcpy(replyMsgBuffer,"\r\n                 Please re-enter new PIN:", replyMsgBufferSize);
234
235                         /* set next state */
236                         pSecurid_session->securidSessionState = NEW_PIN_USER_CONFIRM_STATE;
237
238                         /* insert the updated session in the session list */
239                         securid_sessionlist_add(inst,request,pSecurid_session);
240                         return RC_SECURID_AUTH_CHALLENGE;
241                           
242                 case NEW_PIN_USER_CONFIRM_STATE:
243                         RDEBUG2("SecurID NEW_PIN_USER_CONFIRM_STATE: User [%s]",username);
244                         /* compare previous pin and current pin */
245                         if (!pSecurid_session->pin || strcmp(pSecurid_session->pin,passcode)) {
246                                 RDEBUG2("Pin confirmation failed. Pins do not match [%s] and [%s]",
247                                        SAFE_STR(pSecurid_session->pin),
248                                        passcode);
249                                 /* pins do not match */
250
251                                 /* challenge the user again */
252                                 AceGetPinParams(pSecurid_session->sdiHandle, &pinParams);
253                                 if (pinParams.Alphanumeric) {
254                                         strcpy(format, "alphanumeric characters");
255                                 } else {
256                                         strcpy(format, "digits");
257                                 }
258                                 snprintf(replyMsgBuffer, replyMsgBufferSize,
259                                          " \r\n   Pins do not match--Please try again.\r\n   Enter your new PIN of %d to %d %s,\r\n                or\r\n   <Ctrl-D> to cancel the New PIN procedure:",
260                                          pinParams.Min, pinParams.Max, format);
261
262                                 pSecurid_session->securidSessionState = NEW_PIN_REQUIRED_STATE;
263
264                                 /* insert the updated session in the session list */
265                                 securid_sessionlist_add(inst,request,pSecurid_session);
266                                 rc = RC_SECURID_AUTH_CHALLENGE;
267
268                         } else {
269                                 /* pins match */
270                                 RDEBUG2("Pin confirmation succeeded. Pins match");
271                                 acmRet = SD_Pin(pSecurid_session->sdiHandle, (SD_CHAR*)passcode);
272                                 if (acmRet == ACM_NEW_PIN_ACCEPTED) {
273                                         RDEBUG("New SecurID pin accepted for %s.",pSecurid_session->identity);
274
275                                         pSecurid_session->securidSessionState = NEW_PIN_AUTH_VALIDATE_STATE;
276
277                                         /* insert the updated session in the session list */
278                                         securid_sessionlist_add(inst,request,pSecurid_session);
279
280                                         rc = RC_SECURID_AUTH_CHALLENGE;
281                                         strlcpy(replyMsgBuffer," \r\n\r\nWait for the code on your card to change, then enter new PIN and TokenCode\r\n\r\nEnter PASSCODE:", replyMsgBufferSize);
282                                 } else {
283                                         RDEBUG("SecurID: New SecurID pin rejected for %s.",pSecurid_session->identity);
284                                         SD_Pin(pSecurid_session->sdiHandle, (SD_CHAR*)"");  /* cancel PIN */
285                                         
286
287                                         rc = RC_SECURID_AUTH_FAILURE;
288
289                                         /* deallocate session */
290                                         securid_session_free(inst, request,
291                                                              pSecurid_session);
292                                 }
293                         }
294                         return rc;                
295                 case NEW_PIN_AUTH_VALIDATE_STATE:
296                         acmRet = SD_Check(pSecurid_session->sdiHandle, (SD_CHAR*)passcode, (SD_CHAR*)username);
297                         if (acmRet == ACM_OK) {
298                                 RDEBUG("New SecurID passcode accepted for %s.",
299                                        pSecurid_session->identity);
300                                 rc = RC_SECURID_AUTH_SUCCESS;
301
302                         } else {
303                                 radlog(L_INFO,"SecurID: New passcode rejected for [%s].",pSecurid_session->identity);
304                                 rc = RC_SECURID_AUTH_FAILURE;
305                         }
306
307                         /* deallocate session */
308                         securid_session_free(inst,request,pSecurid_session);
309
310                         return rc;
311                 case NEW_PIN_SYSTEM_ACCEPT_STATE:
312                         if (!strcmp(passcode, "y")) {
313                                 AceGetSystemPin(pSecurid_session->sdiHandle, newPin);
314                                         
315                                 /* Save the PIN for the next session
316                                  * continuation */
317                                 if (pSecurid_session->pin) {
318                                         free(pSecurid_session->pin);
319                                         pSecurid_session->pin = NULL;
320                                 }
321                                 pSecurid_session->pin = strdup(newPin);
322                                         
323                                 snprintf(replyMsgBuffer, replyMsgBufferSize,
324                                          "\r\nYour new PIN is: %s\r\nDo you accept this [y/n]?",
325                                          newPin);
326                                 pSecurid_session->securidSessionState = NEW_PIN_SYSTEM_CONFIRM_STATE;
327                                         
328                                 /* insert the updated session in the
329                                  * session list */
330                                 securid_sessionlist_add(inst, request,
331                                                         pSecurid_session);
332                                         
333                                 rc = RC_SECURID_AUTH_CHALLENGE;
334
335                         } else {
336                                 SD_Pin(pSecurid_session->sdiHandle, (SD_CHAR*)""); //Cancel new PIN
337                                         
338                                 /* deallocate session */
339                                 securid_session_free(inst, request,
340                                                      pSecurid_session);
341                                         
342                                 rc = RC_SECURID_AUTH_FAILURE;
343                         }
344                                 
345                         return rc;                              
346                          
347                 case NEW_PIN_SYSTEM_CONFIRM_STATE:
348                         acmRet = SD_Pin(pSecurid_session->sdiHandle, (SD_CHAR*)pSecurid_session->pin);
349                         if (acmRet == ACM_NEW_PIN_ACCEPTED) {
350                                 strlcpy(replyMsgBuffer," \r\n\r\nPin Accepted. Wait for the code on your card to change, then enter new PIN and TokenCode\r\n\r\nEnter PASSCODE:",replyMsgBufferSize);
351                                 pSecurid_session->securidSessionState = NEW_PIN_AUTH_VALIDATE_STATE;
352                                 /* insert the updated session in the session list */
353                                 securid_sessionlist_add(inst,request,pSecurid_session);
354                                 rc = RC_SECURID_AUTH_CHALLENGE;
355
356                         } else {
357                                 SD_Pin(pSecurid_session->sdiHandle, (SD_CHAR*)""); //Cancel new PIN
358                                 strlcpy(replyMsgBuffer," \r\n\r\nPin Rejected. Wait for the code on your card to change, then try again.\r\n\r\nEnter PASSCODE:",replyMsgBufferSize);
359                                 /* deallocate session */
360                                 securid_session_free(inst, request,
361                                                      pSecurid_session);
362                                 rc = RC_SECURID_AUTH_FAILURE;
363                         }
364                                 
365                         return rc;
366                          
367                         /* USER_SELECTABLE state should be implemented to preserve compatibility with AM 6.x servers, which can return this state */
368                 case NEW_PIN_USER_SELECT_STATE:
369                         if (!strcmp(passcode, "y")) {
370                                 /* User has opted for a system-generated PIN */
371                                 AceGetSystemPin(pSecurid_session->sdiHandle, newPin);
372                                 snprintf(replyMsgBuffer, replyMsgBufferSize,
373                                          "\r\nYour new PIN is: %s\r\nDo you accept this [y/n]?",
374                                          newPin);
375                                 pSecurid_session->securidSessionState = NEW_PIN_SYSTEM_CONFIRM_STATE;
376                                         
377                                 /* insert the updated session in the session list */
378                                 securid_sessionlist_add(inst, request,
379                                                         pSecurid_session);
380                                 rc = RC_SECURID_AUTH_CHALLENGE;
381
382                         } else {
383                                 /* User has opted for a user-defined PIN */
384                                 AceGetPinParams(pSecurid_session->sdiHandle,
385                                                 &pinParams);
386                                 if (pinParams.Alphanumeric) {
387                                         strcpy(format, "alphanumeric characters");
388                                 } else {
389                                         strcpy(format, "digits");
390                                 }
391                                         
392                                 snprintf(replyMsgBuffer, replyMsgBufferSize,
393                                          " \r\n   Enter your new PIN of %d to %d %s,\r\n                or\r\n   <Ctrl-D> to cancel the New PIN procedure:",
394                                          pinParams.Min, pinParams.Max, format);
395                                 pSecurid_session->securidSessionState = NEW_PIN_REQUIRED_STATE;
396                                         
397                                 /* insert the updated session in the session list */
398                                 securid_sessionlist_add(inst, request,
399                                                         pSecurid_session);
400                                 rc = RC_SECURID_AUTH_CHALLENGE;
401                         }
402                                 
403                         return rc;
404                                 
405                 default:
406                         radlog(L_ERR, "rlm_securid: Invalid session state %d for user [%s]",
407                                pSecurid_session->securidSessionState,
408                                username);
409                         break;  
410                 }
411         }
412         
413         return 0;
414                 
415 }
416
417 /******************************************/
418 static int securid_detach(void *instance)
419 {
420         rlm_securid_t *inst = (rlm_securid_t *) instance;
421
422         /* delete session tree */
423         if (inst->session_tree) {
424                 rbtree_free(inst->session_tree);
425                 inst->session_tree = NULL;
426         }
427
428         pthread_mutex_destroy(&(inst->session_mutex));
429
430         return 0;
431 }
432
433
434 static rlm_rcode_t securid_instantiate(CONF_SECTION *conf, void **instance)
435 {
436         rlm_securid_t *inst;
437
438         /* Set up a storage area for instance data */
439         *instance = inst = talloc_zero(conf, rlm_securid_t);
440         if (!inst) return -1;
441
442         /* If the configuration parameters can't be parsed, then fail. */
443         if (cf_section_parse(conf, inst, module_config) < 0) {
444                 radlog(L_ERR, "rlm_securid: Unable to parse configuration section.");
445                 return -1;
446         }
447
448         /*
449          *      Lookup sessions in the tree.  We don't free them in
450          *      the tree, as that's taken care of elsewhere...
451          */
452         inst->session_tree = rbtree_create(securid_session_cmp, NULL, 0);
453         if (!inst->session_tree) {
454                 radlog(L_ERR, "rlm_securid: Cannot initialize session tree.");
455                 return -1;
456         }
457
458         pthread_mutex_init(&(inst->session_mutex), NULL);
459         return 0;
460 }
461
462
463 /*
464  *      Authenticate the user via one of any well-known password.
465  */
466 static int securid_authenticate(void *instance, REQUEST *request)
467 {
468         int rcode;
469         rlm_securid_t *inst = instance;
470         VALUE_PAIR *module_fmsg_vp;
471         VALUE_PAIR *vp;
472         char  buffer[MAX_STRING_LEN]="";
473         const char *username=NULL, *password=NULL;
474         char module_fmsg[MAX_STRING_LEN]="";
475         
476         /*
477          *      We can only authenticate user requests which HAVE
478          *      a User-Name attribute.
479          */
480         if (!request->username) {
481                 radlog(L_AUTH, "rlm_securid: Attribute \"User-Name\" is required for authentication.");
482                 return RLM_MODULE_INVALID;
483         }
484
485         if (!request->password) {
486                 radlog_request(L_AUTH, 0, request, "Attribute \"Password\" is required for authentication.");
487                 return RLM_MODULE_INVALID;
488         }
489
490         /*
491          *      Clear-text passwords are the only ones we support.
492          */
493         if (request->password->da->attr != PW_USER_PASSWORD) {
494                 radlog_request(L_AUTH, 0, request, "Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->da->name);
495                 return RLM_MODULE_INVALID;
496         }
497
498         /*
499          *      The user MUST supply a non-zero-length password.
500          */
501         if (request->password->length == 0) {
502                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_securid: empty password supplied");
503                 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
504                 pairadd(&request->packet->vps, module_fmsg_vp);
505                 return RLM_MODULE_INVALID;
506         }
507
508         /*
509          *      shortcuts
510          */
511         username = request->username->vp_strvalue;
512         password = request->password->vp_strvalue;
513         
514         RDEBUG("User [%s] login attempt with password [%s]",
515                username, password);
516         
517         rcode = securidAuth(inst, request, username, password,
518                             buffer, sizeof(buffer));
519         
520         switch (rcode) {
521         case RC_SECURID_AUTH_SUCCESS:
522                 rcode = RLM_MODULE_OK;
523                 break;
524
525         case RC_SECURID_AUTH_CHALLENGE:
526                 /* reply with Access-challenge message code (11) */
527
528                 /* Generate Prompt attribute */
529                 vp = paircreate(PW_PROMPT, 0, PW_TYPE_INTEGER);
530                                 
531                 rad_assert(vp != NULL);
532                 vp->vp_integer = 0; /* no echo */
533                 pairadd(&request->reply->vps, vp);
534
535                 /* Mark the packet as a Acceess-Challenge Packet */
536                 request->reply->code = PW_ACCESS_CHALLENGE;
537                 RDEBUG("Sending Access-Challenge.");
538                 rcode = RLM_MODULE_HANDLED;
539                 break;
540
541         case RC_SECURID_AUTH_FAILURE:
542         case RC_SECURID_AUTH_ACCESS_DENIED_FAILURE:
543         case RC_SECURID_AUTH_INVALID_SERVER_FAILURE:
544         default:
545                 rcode = RLM_MODULE_REJECT;
546                 break;
547         }
548
549         if (*buffer) {
550                 /* Generate Reply-Message attribute with reply message data */
551                 vp = pairmake("Reply-Message", buffer, T_OP_EQ);
552                 
553                 /* make sure message ends with '\0' */
554                 if (vp->length < (int) sizeof(vp->vp_strvalue)) {
555                         vp->vp_strvalue[vp->length] = '\0';
556                         vp->length++;
557                 }
558                 pairadd(&request->reply->vps,vp);
559         }
560         return rcode;
561 }
562
563
564 /*
565  *      The module name should be the only globally exported symbol.
566  *      That is, everything else should be 'static'.
567  *
568  *      If the module needs to temporarily modify it's instantiation
569  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
570  *      The server will then take care of ensuring that the module
571  *      is single-threaded.
572  */
573 module_t rlm_securid = {
574         RLM_MODULE_INIT,
575         "securid",
576         RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,         /* type */
577         securid_instantiate,            /* instantiation */
578         securid_detach,                 /* detach */
579         {
580                 securid_authenticate,   /* authentication */
581                 NULL,                   /* authorization */
582                 NULL,                   /* preaccounting */
583                 NULL,                   /* accounting */
584                 NULL,                   /* checksimul */
585                 NULL,                   /* pre-proxy */
586                 NULL,                   /* post-proxy */
587                 NULL                    /* post-auth */
588         },
589 };