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