'static' to 'static const'
[freeradius.git] / src / modules / rlm_pap / rlm_pap.c
1 /*
2  * rlm_pap.c
3  *
4  * Version:  $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2001  The FreeRADIUS server project
21  * Copyright 2001  Kostas Kalevras <kkalev@noc.ntua.gr>
22  */
23
24 #include "autoconf.h"
25 #include "libradius.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <ctype.h>
32
33 #include "radiusd.h"
34 #include "modules.h"
35 #include "conffile.h"
36 #include "../../include/md5.h"
37 #include "../../include/sha1.h"
38
39 #define PAP_ENC_INVALID -1
40 #define PAP_ENC_CLEAR           0
41 #define PAP_ENC_CRYPT           1
42 #define PAP_ENC_MD5             2
43 #define PAP_ENC_SHA1            3
44 #define PAP_ENC_NT              4
45 #define PAP_ENC_LM              5
46 #define PAP_ENC_SMD5            6
47 #define PAP_ENC_SSHA            7
48 #define PAP_ENC_NS_MTA_MD5      8
49 #define PAP_ENC_AUTO            9
50 #define PAP_MAX_ENC             9
51
52
53 static const char rcsid[] = "$Id$";
54
55 /*
56  *      Define a structure for our module configuration.
57  *
58  *      These variables do not need to be in a structure, but it's
59  *      a lot cleaner to do so, and a pointer to the structure can
60  *      be used as the instance handle.
61  */
62 typedef struct rlm_pap_t {
63         char *scheme;  /* password encryption scheme */
64         int sch;
65         char norm_passwd;
66 } rlm_pap_t;
67
68 /*
69  *      A mapping of configuration file names to internal variables.
70  *
71  *      Note that the string is dynamically allocated, so it MUST
72  *      be freed.  When the configuration file parse re-reads the string,
73  *      it free's the old one, and strdup's the new one, placing the pointer
74  *      to the strdup'd string into 'config.string'.  This gets around
75  *      buffer over-flows.
76  */
77 static const CONF_PARSER module_config[] = {
78   { "encryption_scheme", PW_TYPE_STRING_PTR, offsetof(rlm_pap_t,scheme), NULL, "auto" },
79   { NULL, -1, 0, NULL, NULL }
80 };
81
82 static const LRAD_NAME_NUMBER schemes[] = {
83   { "clear", PAP_ENC_CLEAR },
84   { "crypt", PAP_ENC_CRYPT },
85   { "md5", PAP_ENC_MD5 },
86   { "sha1", PAP_ENC_SHA1 },
87   { "nt", PAP_ENC_NT },
88   { "lm", PAP_ENC_LM },
89   { "smd5", PAP_ENC_SMD5 },
90   { "ssha", PAP_ENC_SSHA },
91   { "auto", PAP_ENC_AUTO },
92   { NULL, PAP_ENC_INVALID }
93 };
94
95
96 static int pap_detach(void *instance)
97 {
98         rlm_pap_t *inst = (rlm_pap_t *) instance;
99
100         if (inst->scheme) free((char *)inst->scheme);
101         free(inst);
102
103         return 0;
104 }
105
106
107 static int pap_instantiate(CONF_SECTION *conf, void **instance)
108 {
109         rlm_pap_t *inst;
110
111         /*
112          *      Set up a storage area for instance data
113          */
114         inst = rad_malloc(sizeof(*inst));
115         if (!inst) {
116                 return -1;
117         }
118         memset(inst, 0, sizeof(*inst));
119
120         /*
121          *      If the configuration parameters can't be parsed, then
122          *      fail.
123          */
124         if (cf_section_parse(conf, inst, module_config) < 0) {
125                 pap_detach(inst);
126                 return -1;
127         }
128         if (inst->scheme == NULL || strlen(inst->scheme) == 0){
129                 radlog(L_ERR, "rlm_pap: No scheme defined");
130                 pap_detach(inst);
131                 return -1;
132         }
133
134         inst->sch = lrad_str2int(schemes, inst->scheme, PAP_ENC_INVALID);
135         if (inst->sch == PAP_ENC_INVALID) {
136                 radlog(L_ERR, "rlm_pap: Unknown scheme \"%s\"", inst->scheme);
137                 pap_detach(inst);
138                 return -1;
139         }
140
141         *instance = inst;
142
143         return 0;
144 }
145
146
147 /*
148  *      Decode one base64 chunk
149  */
150 static int decode_it(const char *src, uint8_t *dst)
151 {
152         int i;
153         unsigned int x = 0;
154
155         for(i = 0; i < 4; i++) {
156                 if (src[i] >= 'A' && src[i] <= 'Z')
157                         x = (x << 6) + (unsigned int)(src[i] - 'A' + 0);
158                 else if (src[i] >= 'a' && src[i] <= 'z')
159                          x = (x << 6) + (unsigned int)(src[i] - 'a' + 26);
160                 else if(src[i] >= '0' && src[i] <= '9') 
161                          x = (x << 6) + (unsigned int)(src[i] - '0' + 52);
162                 else if(src[i] == '+')
163                         x = (x << 6) + 62;
164                 else if (src[i] == '/')
165                         x = (x << 6) + 63;
166                 else if (src[i] == '=')
167                         x = (x << 6);
168                 else return 0;
169         }
170         
171         dst[2] = (unsigned char)(x & 255); x >>= 8;
172         dst[1] = (unsigned char)(x & 255); x >>= 8;
173         dst[0] = (unsigned char)(x & 255); x >>= 8;
174         
175         return 1;
176 }
177
178
179 /*
180  *      Base64 decoding.
181  */
182 static int base64_decode (const char *src, uint8_t *dst)
183 {
184         int length, equals;
185         int i, num;
186         char last[3];
187
188         length = equals = 0;
189         while (src[length] && src[length] != '=') length++;
190
191         if (src[length] != '=') return 0; /* no trailing '=' */
192
193         while (src[length + equals] == '=') equals++;
194
195         num = (length + equals) / 4;
196         
197         for (i = 0; i < num - 1; i++) {
198                 if (!decode_it(src, dst)) return 0;
199                 src += 4;
200                 dst += 3;
201         }
202
203         decode_it(src, last);
204         for (i = 0; i < (3 - equals); i++) {
205                 dst[i] = last[i];
206         }
207
208         return (num * 3) - equals;
209 }
210
211
212 /*
213  *      Hex or base64 or bin auto-discovery.
214  */
215 static void normify(VALUE_PAIR *vp, int min_length)
216 {
217         int decoded;
218         char buffer[64];
219         
220         if (min_length >= sizeof(buffer)) return; /* paranoia */
221
222         /*
223          *      Hex encoding.
224          */
225         if (vp->length >= (2 * min_length)) {
226                 decoded = lrad_hex2bin(vp->strvalue, buffer, vp->length >> 1);
227                 if (decoded == (vp->length >> 1)) {
228                         DEBUG2("rlm_pap: Normalizing %s from hex encoding", vp->name);
229                         memcpy(vp->strvalue, buffer, decoded);
230                         vp->length = decoded;
231                         return;
232                 }
233         }
234
235         /*
236          *      Base 64 encoding.  It's at least 4/3 the original size,
237          *      and we want to avoid division...
238          */
239         if ((vp->length * 3) >= ((min_length * 4))) {
240                 decoded = base64_decode(vp->strvalue, buffer);
241                 if (decoded >= min_length) {
242                         DEBUG2("rlm_pap: Normalizing %s from base64 encoding", vp->name);
243                         memcpy(vp->strvalue, buffer, decoded);
244                         vp->length = decoded;
245                         return;
246                 }
247         }
248
249         /*
250          *      Else unknown encoding, or already binary.  Leave it.
251          */
252 }
253
254
255 /*
256  *      Authorize the user for PAP authentication.
257  *
258  *      This isn't strictly necessary, but it does make the
259  *      server simpler to configure.
260  */
261 static int pap_authorize(void *instance, REQUEST *request)
262 {
263         int auth_type = FALSE;
264         int found_pw = FALSE;
265         VALUE_PAIR *vp;
266
267         instance = instance;    /* -Wunused */
268
269         for (vp = request->config_items; vp != NULL; vp = vp->next) {
270                 switch (vp->attribute) {
271                 case PW_USER_PASSWORD:
272                 case PW_CRYPT_PASSWORD:
273                 case PW_NS_MTA_MD5_PASSWORD:
274                         found_pw = TRUE;
275                         break;  /* don't touch these */
276
277                 case PW_MD5_PASSWORD:
278                 case PW_SMD5_PASSWORD:
279                 case PW_NT_PASSWORD:
280                 case PW_LM_PASSWORD:
281                         normify(vp, 16); /* ensure it's in the right format */
282                         found_pw = TRUE;
283                         break;
284
285                 case PW_SHA_PASSWORD:
286                 case PW_SSHA_PASSWORD:
287                         normify(vp, 20); /* ensure it's in the right format */
288                         found_pw = TRUE;
289                         break;
290
291                 case PW_AUTH_TYPE:
292                         auth_type = TRUE;
293                         break;
294
295                 default:
296                         break;  /* ignore it */
297                         
298                 }
299         }
300
301         /*
302          *      Print helpful warnings if there was no password.
303          */
304         if (!found_pw) {
305                 DEBUG("rlm_pap: WARNING! No \"known good\" password found for the user.  Authentication will probably fail");
306                 return RLM_MODULE_NOOP;
307         }
308
309         /*
310          *      Don't touch existing Auth-Types.
311          */
312         if (auth_type) {
313                 DEBUG2("rlm_pap: Found existing Auth-Type, not changing it.");
314                 return RLM_MODULE_NOOP;
315         }       
316
317         /*
318          *      Can't do PAP if there's no password.
319          */
320         if (!request->password ||
321             (request->password->attribute != PW_USER_PASSWORD)) {
322                 /*
323                  *      Don't print out debugging messages if we know
324                  *      they're useless.
325                  */
326                 if (request->packet->code == PW_ACCESS_CHALLENGE) {
327                         return RLM_MODULE_NOOP;
328                 }
329
330                 DEBUG2("rlm_pap: No clear-text password in the request.  Not performing PAP.");
331                 return RLM_MODULE_NOOP;
332         }
333
334
335         vp = pairmake("Auth-Type", "PAP", T_OP_SET);
336         if (!vp) return RLM_MODULE_FAIL;
337
338         pairadd(&request->config_items, vp);
339
340         return RLM_MODULE_UPDATED;
341 }
342
343
344 /*
345  *      Authenticate the user via one of any well-known password.
346  */
347 static int pap_authenticate(void *instance, REQUEST *request)
348 {
349         rlm_pap_t *inst = instance;
350         VALUE_PAIR *vp;
351         VALUE_PAIR *module_fmsg_vp;
352         char module_fmsg[MAX_STRING_LEN];
353         MD5_CTX md5_context;
354         SHA1_CTX sha1_context;
355         char digest[40];
356         char buff[MAX_STRING_LEN];
357         char buff2[MAX_STRING_LEN + 50];
358         int scheme = PAP_ENC_INVALID;
359
360         /* quiet the compiler */
361         instance = instance;
362         request = request;
363
364         if (!request->password){
365                 radlog(L_AUTH, "rlm_pap: Attribute \"Password\" is required for authentication.");
366                 return RLM_MODULE_INVALID;
367         }
368
369         /*
370          *      Clear-text passwords are the only ones we support.
371          */
372         if (request->password->attribute != PW_USER_PASSWORD) {
373                 radlog(L_AUTH, "rlm_pap: Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
374                 return RLM_MODULE_INVALID;
375         }
376
377         /*
378          *      The user MUST supply a non-zero-length password.
379          */
380         if (request->password->length == 0) {
381                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: empty password supplied");
382                 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
383                 pairadd(&request->packet->vps, module_fmsg_vp);
384                 return RLM_MODULE_INVALID;
385         }
386
387         DEBUG("rlm_pap: login attempt with password %s",
388               request->password->strvalue);
389
390         /*
391          *      First, auto-detect passwords, by attribute in the
392          *      config items.
393          */
394         if (inst->sch == PAP_ENC_AUTO) {
395                 for (vp = request->config_items; vp != NULL; vp = vp->next) {
396                         switch (vp->attribute) {
397                         case PW_USER_PASSWORD:
398                                 goto do_clear;
399                                 
400                         case PW_CRYPT_PASSWORD:
401                                 goto do_crypt;
402                                 
403                         case PW_MD5_PASSWORD:
404                                 goto do_md5;
405                                 
406                         case PW_SHA_PASSWORD:
407                                 goto do_sha;
408                                 
409                         case PW_NT_PASSWORD:
410                                 goto do_nt;
411
412                         case PW_LM_PASSWORD:
413                                 goto do_lm;
414
415                         case PW_SMD5_PASSWORD:
416                                 goto do_smd5;
417
418                         case PW_SSHA_PASSWORD:
419                                 goto do_ssha;
420
421                         case PW_NS_MTA_MD5_PASSWORD:
422                                 goto do_ns_mta_md5;
423
424                         default:
425                                 break;  /* ignore it */
426                                 
427                         }
428                 }
429
430         fail:
431                 DEBUG("rlm_pap: No password configured for the user.  Cannot do authentication");
432                 return RLM_MODULE_FAIL;
433
434         } else {
435                 vp = NULL;
436                 
437                 if (inst->sch == PAP_ENC_CRYPT) {
438                         vp = pairfind(request->config_items, PW_CRYPT_PASSWORD);
439                 }
440
441                 /*
442                  *      Old-style: all passwords are in User-Password.
443                  */
444                 if (!vp) {
445                         vp = pairfind(request->config_items, PW_USER_PASSWORD);
446                         if (!vp) goto fail;
447                 }
448         }
449
450         /*
451          *      Now that we've decided what to do, go do it.
452          */
453         switch (scheme) {
454         case PAP_ENC_CLEAR:
455         do_clear:
456                 DEBUG("rlm_pap: Using clear text password.");
457                 if (strcmp((char *) vp->strvalue,
458                            (char *) request->password->strvalue) != 0){
459                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CLEAR TEXT password check failed");
460                         goto make_msg;
461                 }
462         done:
463                 DEBUG("rlm_pap: User authenticated succesfully");
464                 return RLM_MODULE_OK;
465                 break;
466                 
467         case PAP_ENC_CRYPT:
468         do_crypt:
469                 DEBUG("rlm_pap: Using CRYPT encryption.");
470                 if (lrad_crypt_check((char *) request->password->strvalue,
471                                      (char *) vp->strvalue) != 0) {
472                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CRYPT password check failed");
473                         goto make_msg;
474                 }
475                 goto done;
476                 break;
477                 
478         case PW_MD5_PASSWORD:
479         do_md5:
480                 DEBUG("rlm_pap: Using MD5 encryption.");
481
482                 normify(vp, 16);
483                 if (vp->length != 16) {
484                 DEBUG("rlm_pap: Configured MD5 password has incorrect length");
485                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured MD5 password has incorrect length");
486                         goto make_msg;
487                 }
488                 
489                 MD5Init(&md5_context);
490                 MD5Update(&md5_context, request->password->strvalue,
491                           request->password->length);
492                 MD5Final(digest, &md5_context);
493                 if (memcmp(digest, vp->strvalue, vp->length) != 0) {
494                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: MD5 password check failed");
495                         goto make_msg;
496                 }
497                 goto done;
498                 break;
499                 
500         case PW_SMD5_PASSWORD:
501         do_smd5:
502                 DEBUG("rlm_pap: Using SMD5 encryption.");
503
504                 normify(vp, 16);
505                 if (vp->length <= 16) {
506                         DEBUG("rlm_pap: Configured SMD5 password has incorrect length");
507                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured SMD5 password has incorrect length");
508                         goto make_msg;
509                 }
510                 
511                 MD5Init(&md5_context);
512                 MD5Update(&md5_context, request->password->strvalue,
513                           request->password->length);
514                 MD5Update(&md5_context, &vp->strvalue[16], vp->length - 16);
515                 MD5Final(digest, &md5_context);
516
517                 /*
518                  *      Compare only the MD5 hash results, not the salt.
519                  */
520                 if (memcmp(digest, vp->strvalue, 16) != 0) {
521                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: SMD5 password check failed");
522                         goto make_msg;
523                 }
524                 goto done;
525                 break;
526                 
527         case PW_SHA_PASSWORD:
528         do_sha:
529                 DEBUG("rlm_pap: Using SHA1 encryption.");
530                 
531                 normify(vp, 20);
532                 if (vp->length != 20) {
533                         DEBUG("rlm_pap: Configured SHA1 password has incorrect length");
534                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured SHA1 password has incorrect length");
535                         goto make_msg;
536                 }
537                 
538                 SHA1Init(&sha1_context);
539                 SHA1Update(&sha1_context, request->password->strvalue,
540                            request->password->length);
541                 SHA1Final(digest,&sha1_context);
542                 if (memcmp(digest, vp->strvalue, vp->length) != 0) {
543                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: SHA1 password check failed");
544                         goto make_msg;
545                 }
546                 goto done;
547                 break;
548                 
549         case PW_SSHA_PASSWORD:
550         do_ssha:
551                 DEBUG("rlm_pap: Using SSHA encryption.");
552                 
553                 normify(vp, 20);
554                 if (vp->length <= 20) {
555                         DEBUG("rlm_pap: Configured SSHA password has incorrect length");
556                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured SHA password has incorrect length");
557                         goto make_msg;
558                 }
559
560                 
561                 SHA1Init(&sha1_context);
562                 SHA1Update(&sha1_context, request->password->strvalue,
563                            request->password->length);
564                 SHA1Update(&sha1_context, &vp->strvalue[20], vp->length - 20);
565                 SHA1Final(digest,&sha1_context);
566                 if (memcmp(digest, vp->strvalue, 20) != 0) {
567                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: SSHA password check failed");
568                         goto make_msg;
569                 }
570                 goto done;
571                 break;
572                 
573         case PW_NT_PASSWORD:
574         do_nt:
575                 DEBUG("rlm_pap: Using NT encryption.");
576
577                 normify(vp, 16);
578                 if (vp->length != 16) {
579                         DEBUG("rlm_pap: Configured NT-Password has incorrect length");
580                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured NT-Password has incorrect length");
581                         goto make_msg;
582                 }
583                 
584                 sprintf(buff2,"%%{mschap:NT-Hash %s}",
585                         request->password->strvalue);
586                 if (!radius_xlat(digest,sizeof(digest),buff2,request,NULL)){
587                         DEBUG("rlm_pap: mschap xlat failed");
588                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: mschap xlat failed");
589                         goto make_msg;
590                 }
591                 if ((lrad_hex2bin(digest, digest, 16) != vp->length) ||
592                     (memcmp(digest, vp->strvalue, vp->length) != 0)) {
593                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: NT password check failed");
594                         goto make_msg;
595                 }
596                 goto done;
597                 break;
598                 
599         case PW_LM_PASSWORD:
600         do_lm:
601                 DEBUG("rlm_pap: Using LM encryption.");
602                 
603                 normify(vp, 16);
604                 if (vp->length != 16) {
605                         DEBUG("rlm_pap: Configured LM-Password has incorrect length");
606                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured LM-Password has incorrect length");
607                         goto make_msg;
608                 }
609                 sprintf(buff2,"%%{mschap:LM-Hash %s}",request->password->strvalue);
610                 if (!radius_xlat(digest,sizeof(digest),buff2,request,NULL)){
611                         DEBUG("rlm_pap: mschap xlat failed");
612                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: mschap xlat failed");
613                         goto make_msg;
614                 }
615                 if ((lrad_hex2bin(digest, digest, 16) != vp->length) ||
616                     (memcmp(digest, vp->strvalue, vp->length) != 0)) {
617                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: LM password check failed");
618                 make_msg:
619                         DEBUG("rlm_pap: Passwords don't match");
620                         module_fmsg_vp = pairmake("Module-Failure-Message",
621                                                   module_fmsg, T_OP_EQ);
622                         pairadd(&request->packet->vps, module_fmsg_vp);
623                         return RLM_MODULE_REJECT;
624                 }
625                 goto done;
626                 break;
627
628         case PAP_ENC_NS_MTA_MD5:
629         do_ns_mta_md5:
630                 DEBUG("rlm_pap: Using NT-MTA-MD5 password");
631
632                 if (vp->length != 64) {
633                         DEBUG("rlm_pap: Configured NS-MTA-MD5-Password has incorrect length");
634                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured NS-MTA-MD5-Password has incorrect length");
635                         goto make_msg;
636                 }
637
638                 /*
639                  *      Sanity check the value of NS-MTA-MD5-Password
640                  */
641                 if (lrad_hex2bin(vp->strvalue, buff, 32) != 16) {
642                         DEBUG("rlm_pap: Configured NS-MTA-MD5-Password has invalid value");
643                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured NS-MTA-MD5-Password has invalid value");
644                         goto make_msg;
645                 }
646
647                 /*
648                  *      Ensure we don't have buffer overflows.
649                  *
650                  *      This really: sizeof(buff) - 2 - 2*32 - strlen(passwd)
651                  */
652                 if (strlen(request->password->strvalue) >= (sizeof(buff2) - 2 - 2 * 32)) {
653                         DEBUG("rlm_pap: Configured password is too long");
654                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: password is too long");
655                         goto make_msg;
656                 }
657
658                 /*
659                  *      Set up the algorithm.
660                  */
661                 {
662                         char *p = buff2;
663
664                         memcpy(p, &vp->strvalue[32], 32);
665                         p += 32;
666                         *(p++) = 89;
667                         strcpy(p, request->password->strvalue);
668                         p += strlen(p);
669                         *(p++) = 247;
670                         memcpy(p, &vp->strvalue[32], 32);
671                         p += 32;
672
673                         MD5Init(&md5_context);
674                         MD5Update(&md5_context, buff2, p - buff2);
675                         MD5Final(digest, &md5_context);
676                 }
677                 if (memcmp(digest, buff, 16) != 0) {
678                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: NS-MTA-MD5 password check failed");
679                         goto make_msg;
680                 }
681                 goto done;
682
683         default:
684                 break;
685         }
686
687         DEBUG("rlm_pap: No password configured for the user.  Cannot do authentication");
688         return RLM_MODULE_FAIL;
689 }
690
691
692 /*
693  *      The module name should be the only globally exported symbol.
694  *      That is, everything else should be 'static'.
695  *
696  *      If the module needs to temporarily modify it's instantiation
697  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
698  *      The server will then take care of ensuring that the module
699  *      is single-threaded.
700  */
701 module_t rlm_pap = {
702         "PAP",
703         0,                              /* type */
704         NULL,                           /* initialization */
705         pap_instantiate,                /* instantiation */
706         {
707                 pap_authenticate,       /* authentication */
708                 pap_authorize,          /* authorization */
709                 NULL,                   /* preaccounting */
710                 NULL,                   /* accounting */
711                 NULL,                   /* checksimul */
712                 NULL,                   /* pre-proxy */
713                 NULL,                   /* post-proxy */
714                 NULL                    /* post-auth */
715         },
716         pap_detach,                     /* detach */
717         NULL,                           /* destroy */
718 };