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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2001-2012 The FreeRADIUS server project
21 * Copyright 2012 Matthew Newton <matthew@newtoncomputing.co.uk>
22 * Copyright 2001 Kostas Kalevras <kkalev@noc.ntua.gr>
25 #include <freeradius-devel/ident.h>
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/modules.h>
33 #include "../../include/md5.h"
34 #include "../../include/sha1.h"
39 * Define a structure for our module configuration.
41 * These variables do not need to be in a structure, but it's
42 * a lot cleaner to do so, and a pointer to the structure can
43 * be used as the instance handle.
45 typedef struct rlm_pap_t {
46 const char *name; /* CONF_SECTION->name, not strdup'd */
52 * A mapping of configuration file names to internal variables.
54 * Note that the string is dynamically allocated, so it MUST
55 * be freed. When the configuration file parse re-reads the string,
56 * it free's the old one, and strdup's the new one, placing the pointer
57 * to the strdup'd string into 'config.string'. This gets around
60 static const CONF_PARSER module_config[] = {
61 { "auto_header", PW_TYPE_BOOLEAN, offsetof(rlm_pap_t,auto_header), NULL, "no" },
62 { NULL, -1, 0, NULL, NULL }
67 * For auto-header discovery.
69 static const FR_NAME_NUMBER header_names[] = {
70 { "{clear}", PW_CLEARTEXT_PASSWORD },
71 { "{cleartext}", PW_CLEARTEXT_PASSWORD },
72 { "{md5}", PW_MD5_PASSWORD },
73 { "{BASE64_MD5}", PW_MD5_PASSWORD },
74 { "{smd5}", PW_SMD5_PASSWORD },
75 { "{crypt}", PW_CRYPT_PASSWORD },
76 { "{sha}", PW_SHA_PASSWORD },
77 { "{ssha}", PW_SSHA_PASSWORD },
78 { "{nt}", PW_NT_PASSWORD },
79 { "{nthash}", PW_NT_PASSWORD },
80 { "{x-nthash}", PW_NT_PASSWORD },
81 { "{ns-mta-md5}", PW_NS_MTA_MD5_PASSWORD },
82 { "{x- orcllmv}", PW_LM_PASSWORD },
83 { "{X- ORCLNTV}", PW_NT_PASSWORD },
88 static int pap_detach(void *instance)
90 rlm_pap_t *inst = (rlm_pap_t *) instance;
98 static int pap_instantiate(CONF_SECTION *conf, void **instance)
104 * Set up a storage area for instance data
106 inst = rad_malloc(sizeof(*inst));
110 memset(inst, 0, sizeof(*inst));
113 * If the configuration parameters can't be parsed, then
116 if (cf_section_parse(conf, inst, module_config) < 0) {
121 inst->name = cf_section_name2(conf);
123 inst->name = cf_section_name1(conf);
126 dval = dict_valbyname(PW_AUTH_TYPE, 0, inst->name);
128 inst->auth_type = dval->value;
140 * Decode one base64 chunk
142 static int decode_it(const char *src, uint8_t *dst)
147 for(i = 0; i < 4; i++) {
148 if (src[i] >= 'A' && src[i] <= 'Z')
149 x = (x << 6) + (unsigned int)(src[i] - 'A' + 0);
150 else if (src[i] >= 'a' && src[i] <= 'z')
151 x = (x << 6) + (unsigned int)(src[i] - 'a' + 26);
152 else if(src[i] >= '0' && src[i] <= '9')
153 x = (x << 6) + (unsigned int)(src[i] - '0' + 52);
154 else if(src[i] == '+')
156 else if (src[i] == '/')
158 else if (src[i] == '=')
163 dst[2] = (unsigned char)(x & 255); x >>= 8;
164 dst[1] = (unsigned char)(x & 255); x >>= 8;
165 dst[0] = (unsigned char)(x & 255);
174 static int base64_decode (const char *src, uint8_t *dst)
181 while (src[length] && src[length] != '=') length++;
183 while (src[length + equals] == '=') equals++;
185 num = (length + equals) / 4;
187 for (i = 0; i < num - 1; i++) {
188 if (!decode_it(src, dst)) return 0;
193 decode_it(src, last);
194 for (i = 0; i < (3 - equals); i++) {
198 return (num * 3) - equals;
203 * Hex or base64 or bin auto-discovery.
205 static void normify(REQUEST *request, VALUE_PAIR *vp, size_t min_length)
210 if (min_length >= sizeof(buffer)) return; /* paranoia */
215 if (vp->length >= (2 * min_length)) {
216 decoded = fr_hex2bin(vp->vp_strvalue, buffer, vp->length >> 1);
217 if (decoded == (vp->length >> 1)) {
218 RDEBUG2("Normalizing %s from hex encoding", vp->name);
219 memcpy(vp->vp_octets, buffer, decoded);
220 vp->length = decoded;
226 * Base 64 encoding. It's at least 4/3 the original size,
227 * and we want to avoid division...
229 if ((vp->length * 3) >= ((min_length * 4))) {
230 decoded = base64_decode(vp->vp_strvalue, buffer);
231 if (decoded >= min_length) {
232 RDEBUG2("Normalizing %s from base64 encoding", vp->name);
233 memcpy(vp->vp_octets, buffer, decoded);
234 vp->length = decoded;
240 * Else unknown encoding, or already binary. Leave it.
246 * Authorize the user for PAP authentication.
248 * This isn't strictly necessary, but it does make the
249 * server simpler to configure.
251 static int pap_authorize(void *instance, REQUEST *request)
253 rlm_pap_t *inst = instance;
254 int auth_type = FALSE;
255 int found_pw = FALSE;
256 VALUE_PAIR *vp, *next;
258 for (vp = request->config_items; vp != NULL; vp = next) {
261 switch (vp->attribute) {
262 case PW_USER_PASSWORD: /* deprecated */
266 * Look for '{foo}', and use them
268 if (!inst->auto_header ||
269 (vp->vp_strvalue[0] != '{')) {
274 case PW_PASSWORD_WITH_HEADER: /* preferred */
285 p = strchr(q + 1, '}');
290 * Password already exists: use
291 * that instead of this one.
293 if (pairfind(request->config_items, PW_USER_PASSWORD, 0) ||
294 pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0)) {
295 RDEBUG("Config already contains \"known good\" password. Ignoring Password-With-Header");
300 * If it's binary, it may be
301 * base64 encoded. Decode it,
302 * and re-write the attribute to
303 * have the decoded value.
305 decoded = base64_decode(vp->vp_strvalue, binbuf);
306 if ((decoded > 0) && (binbuf[0] == '{') &&
307 (memchr(binbuf, '}', decoded) != NULL)) {
308 memcpy(vp->vp_octets, binbuf, decoded);
309 vp->length = decoded;
313 RDEBUG("Failed to decode Password-With-Header = \"%s\"", vp->vp_strvalue);
317 if ((size_t) (p - q) > sizeof(charbuf)) break;
319 memcpy(charbuf, q, p - q + 1);
320 charbuf[p - q + 1] = '\0';
322 attr = fr_str2int(header_names, charbuf, 0);
324 RDEBUG2("Found unknown header {%s}: Not doing anything", charbuf);
328 new_vp = radius_paircreate(request,
329 &request->config_items,
330 attr, 0, PW_TYPE_STRING);
333 * The data after the '}' may be binary,
334 * so we copy it via memcpy.
336 new_vp->length = vp->length;
337 new_vp->length -= (p - q + 1);
338 memcpy(new_vp->vp_strvalue, p + 1, new_vp->length);
341 * May be old-style User-Password with header.
342 * We've found the header & created the proper
343 * attribute, so we should delete the old
344 * User-Password here.
346 pairdelete(&request->config_items, PW_USER_PASSWORD, 0);
350 case PW_CLEARTEXT_PASSWORD:
351 case PW_CRYPT_PASSWORD:
352 case PW_NS_MTA_MD5_PASSWORD:
354 break; /* don't touch these */
356 case PW_MD5_PASSWORD:
357 case PW_SMD5_PASSWORD:
360 normify(request, vp, 16); /* ensure it's in the right format */
364 case PW_SHA_PASSWORD:
365 case PW_SSHA_PASSWORD:
366 normify(request, vp, 20); /* ensure it's in the right format */
371 * If it's proxied somewhere, don't complain
372 * about not having passwords or Auth-Type.
374 case PW_PROXY_TO_REALM:
376 REALM *realm = realm_find(vp->vp_strvalue);
377 if (realm && realm->auth_pool) {
378 return RLM_MODULE_NOOP;
387 * Auth-Type := Accept
388 * Auth-Type := Reject
390 if ((vp->vp_integer == 254) ||
391 (vp->vp_integer == 4)) {
397 break; /* ignore it */
403 * Print helpful warnings if there was no password.
407 * Likely going to be proxied. Avoid printing
410 if (pairfind(request->config_items, PW_REALM, 0) ||
411 (pairfind(request->config_items, PW_PROXY_TO_REALM, 0))) {
412 return RLM_MODULE_NOOP;
416 * The TLS types don't need passwords.
418 vp = pairfind(request->packet->vps, PW_EAP_TYPE, 0);
420 ((vp->vp_integer == 13) || /* EAP-TLS */
421 (vp->vp_integer == 21) || /* EAP-TTLS */
422 (vp->vp_integer == 25))) { /* PEAP */
423 return RLM_MODULE_NOOP;
426 RDEBUG("WARNING! No \"known good\" password found for the user. Authentication may fail because of this.");
427 return RLM_MODULE_NOOP;
431 * Don't touch existing Auth-Types.
434 RDEBUG2("WARNING: Auth-Type already set. Not setting to PAP");
435 return RLM_MODULE_NOOP;
439 * Can't do PAP if there's no password.
441 if (!request->password ||
442 (request->password->attribute != PW_USER_PASSWORD)) {
444 * Don't print out debugging messages if we know
447 if (request->packet->code == PW_ACCESS_CHALLENGE) {
448 return RLM_MODULE_NOOP;
451 RDEBUG2("No clear-text password in the request. Not performing PAP.");
452 return RLM_MODULE_NOOP;
455 if (inst->auth_type) {
456 vp = radius_paircreate(request, &request->config_items,
457 PW_AUTH_TYPE, 0, PW_TYPE_INTEGER);
458 vp->vp_integer = inst->auth_type;
461 return RLM_MODULE_UPDATED;
466 * Authenticate the user via one of any well-known password.
468 static int pap_authenticate(void *instance, REQUEST *request)
471 VALUE_PAIR *module_fmsg_vp;
472 char module_fmsg[MAX_STRING_LEN];
473 int rc = RLM_MODULE_INVALID;
474 int (*auth_func)(REQUEST *, VALUE_PAIR *, char *) = NULL;
476 /* Shut the compiler up */
479 if (!request->password ||
480 (request->password->attribute != PW_USER_PASSWORD)) {
481 RDEBUG("ERROR: You set 'Auth-Type = PAP' for a request that does not contain a User-Password attribute!");
482 return RLM_MODULE_INVALID;
486 * The user MUST supply a non-zero-length password.
488 if (request->password->length == 0) {
489 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: empty password supplied");
490 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
491 pairadd(&request->packet->vps, module_fmsg_vp);
492 return RLM_MODULE_INVALID;
495 RDEBUG("login attempt with password \"%s\"", request->password->vp_strvalue);
498 * Auto-detect passwords, by attribute in the
499 * config items, to find out which authentication
502 for (vp = request->config_items; vp != NULL; vp = vp->next) {
503 switch (vp->attribute) {
504 case PW_USER_PASSWORD: /* deprecated */
505 case PW_CLEARTEXT_PASSWORD: /* preferred */
506 auth_func = &pap_auth_clear;
509 case PW_CRYPT_PASSWORD:
510 auth_func = &pap_auth_crypt;
513 case PW_MD5_PASSWORD:
514 auth_func = &pap_auth_md5;
517 case PW_SMD5_PASSWORD:
518 auth_func = &pap_auth_smd5;
521 case PW_SHA_PASSWORD:
522 auth_func = &pap_auth_sha;
525 case PW_SSHA_PASSWORD:
526 auth_func = &pap_auth_ssha;
530 auth_func = &pap_auth_nt;
534 auth_func = &pap_auth_lm;
537 case PW_NS_MTA_MD5_PASSWORD:
538 auth_func = &pap_auth_ns_mta_md5;
545 if (auth_func != NULL) break;
549 * No attribute was found that looked like a password to match.
551 if (auth_func == NULL) {
552 RDEBUG("No password configured for the user. Cannot do authentication");
553 return RLM_MODULE_FAIL;
557 * Authenticate, and return.
559 rc = auth_func(request, vp, module_fmsg);
561 if (rc == RLM_MODULE_REJECT) {
562 RDEBUG("Passwords don't match");
563 module_fmsg_vp = pairmake("Module-Failure-Message",
564 module_fmsg, T_OP_EQ);
565 pairadd(&request->packet->vps, module_fmsg_vp);
568 if (rc == RLM_MODULE_OK) {
569 RDEBUG("User authenticated successfully");
577 * PAP authentication functions
580 static int pap_auth_clear(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
582 if (vp->attribute == PW_USER_PASSWORD) {
583 RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
584 RDEBUG("!!! Please update your configuration so that the \"known !!!");
585 RDEBUG("!!! good\" clear text password is in Cleartext-Password, !!!");
586 RDEBUG("!!! and NOT in User-Password. !!!");
587 RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
590 RDEBUG("Using clear text password \"%s\"", vp->vp_strvalue);
592 if ((vp->length != request->password->length) ||
593 (rad_digest_cmp(vp->vp_octets,
594 request->password->vp_octets,
596 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
597 "rlm_pap: CLEAR TEXT password check failed");
598 return RLM_MODULE_REJECT;
600 return RLM_MODULE_OK;
603 static int pap_auth_crypt(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
605 RDEBUG("Using CRYPT password \"%s\"", vp->vp_strvalue);
607 if (fr_crypt_check(request->password->vp_strvalue,
608 vp->vp_strvalue) != 0) {
609 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
610 "rlm_pap: CRYPT password check failed");
611 return RLM_MODULE_REJECT;
613 return RLM_MODULE_OK;
616 static int pap_auth_md5(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
618 FR_MD5_CTX md5_context;
621 RDEBUG("Using MD5 encryption.");
623 normify(request, vp, 16);
624 if (vp->length != 16) {
625 RDEBUG("Configured MD5 password has incorrect length");
626 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
627 "rlm_pap: Configured MD5 password has incorrect length");
628 return RLM_MODULE_REJECT;
631 fr_MD5Init(&md5_context);
632 fr_MD5Update(&md5_context, request->password->vp_octets,
633 request->password->length);
634 fr_MD5Final(binbuf, &md5_context);
636 if (rad_digest_cmp(binbuf, vp->vp_octets, vp->length) != 0) {
637 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
638 "rlm_pap: MD5 password check failed");
639 return RLM_MODULE_REJECT;
642 return RLM_MODULE_OK;
646 static int pap_auth_smd5(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
648 FR_MD5_CTX md5_context;
651 RDEBUG("Using SMD5 encryption.");
653 normify(request, vp, 16);
654 if (vp->length <= 16) {
655 RDEBUG("Configured SMD5 password has incorrect length");
656 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
657 "rlm_pap: Configured SMD5 password has incorrect length");
658 return RLM_MODULE_REJECT;
661 fr_MD5Init(&md5_context);
662 fr_MD5Update(&md5_context, request->password->vp_octets,
663 request->password->length);
664 fr_MD5Update(&md5_context, &vp->vp_octets[16], vp->length - 16);
665 fr_MD5Final(binbuf, &md5_context);
668 * Compare only the MD5 hash results, not the salt.
670 if (rad_digest_cmp(binbuf, vp->vp_octets, 16) != 0) {
671 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
672 "rlm_pap: SMD5 password check failed");
673 return RLM_MODULE_REJECT;
676 return RLM_MODULE_OK;
679 static int pap_auth_sha(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
681 fr_SHA1_CTX sha1_context;
684 RDEBUG("Using SHA1 encryption.");
686 normify(request, vp, 20);
687 if (vp->length != 20) {
688 RDEBUG("Configured SHA1 password has incorrect length");
689 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
690 "rlm_pap: Configured SHA1 password has incorrect length");
691 return RLM_MODULE_REJECT;
694 fr_SHA1Init(&sha1_context);
695 fr_SHA1Update(&sha1_context, request->password->vp_octets,
696 request->password->length);
697 fr_SHA1Final(binbuf,&sha1_context);
699 if (rad_digest_cmp(binbuf, vp->vp_octets, vp->length) != 0) {
700 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
701 "rlm_pap: SHA1 password check failed");
702 return RLM_MODULE_REJECT;
705 return RLM_MODULE_OK;
708 static int pap_auth_ssha(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
710 fr_SHA1_CTX sha1_context;
713 RDEBUG("Using SSHA encryption.");
715 normify(request, vp, 20);
716 if (vp->length <= 20) {
717 RDEBUG("Configured SSHA password has incorrect length");
718 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
719 "rlm_pap: Configured SHA password has incorrect length");
720 return RLM_MODULE_REJECT;
723 fr_SHA1Init(&sha1_context);
724 fr_SHA1Update(&sha1_context, request->password->vp_octets,
725 request->password->length);
726 fr_SHA1Update(&sha1_context, &vp->vp_octets[20], vp->length - 20);
727 fr_SHA1Final(binbuf,&sha1_context);
729 if (rad_digest_cmp(binbuf, vp->vp_octets, 20) != 0) {
730 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
731 "rlm_pap: SSHA password check failed");
732 return RLM_MODULE_REJECT;
735 return RLM_MODULE_OK;
738 static int pap_auth_nt(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
742 char buff2[MAX_STRING_LEN + 50];
744 RDEBUG("Using NT encryption.");
746 normify(request, vp, 16);
747 if (vp->length != 16) {
748 RDEBUG("Configured NT-Password has incorrect length");
749 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
750 "rlm_pap: Configured NT-Password has incorrect length");
751 return RLM_MODULE_REJECT;
754 strlcpy(buff2, "%{mschap:NT-Hash %{User-Password}}", sizeof(buff2));
755 if (!radius_xlat(charbuf, sizeof(charbuf),buff2,request,NULL)){
756 RDEBUG("mschap xlat failed");
757 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
758 "rlm_pap: mschap xlat failed");
759 return RLM_MODULE_REJECT;
762 if ((fr_hex2bin(charbuf, binbuf, 16) != vp->length) ||
763 (rad_digest_cmp(binbuf, vp->vp_octets, vp->length) != 0)) {
764 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
765 "rlm_pap: NT password check failed");
766 return RLM_MODULE_REJECT;
769 return RLM_MODULE_OK;
773 static int pap_auth_lm(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
777 char buff2[MAX_STRING_LEN + 50];
779 RDEBUG("Using LM encryption.");
781 normify(request, vp, 16);
782 if (vp->length != 16) {
783 RDEBUG("Configured LM-Password has incorrect length");
784 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
785 "rlm_pap: Configured LM-Password has incorrect length");
786 return RLM_MODULE_REJECT;
789 strlcpy(buff2, "%{mschap:LM-Hash %{User-Password}}", sizeof(buff2));
790 if (!radius_xlat(charbuf,sizeof(charbuf),buff2,request,NULL)){
791 RDEBUG("mschap xlat failed");
792 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
793 "rlm_pap: mschap xlat failed");
794 return RLM_MODULE_REJECT;
797 if ((fr_hex2bin(charbuf, binbuf, 16) != vp->length) ||
798 (rad_digest_cmp(binbuf, vp->vp_octets, vp->length) != 0)) {
799 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
800 "rlm_pap: LM password check failed");
801 return RLM_MODULE_REJECT;
804 return RLM_MODULE_OK;
807 static int pap_auth_ns_mta_md5(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
809 FR_MD5_CTX md5_context;
811 uint8_t buff[MAX_STRING_LEN];
812 char buff2[MAX_STRING_LEN + 50];
814 RDEBUG("Using NT-MTA-MD5 password");
816 if (vp->length != 64) {
817 RDEBUG("Configured NS-MTA-MD5-Password has incorrect length");
818 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
819 "rlm_pap: Configured NS-MTA-MD5-Password has incorrect length");
820 return RLM_MODULE_REJECT;
824 * Sanity check the value of NS-MTA-MD5-Password
826 if (fr_hex2bin(vp->vp_strvalue, binbuf, 32) != 16) {
827 RDEBUG("Configured NS-MTA-MD5-Password has invalid value");
828 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
829 "rlm_pap: Configured NS-MTA-MD5-Password has invalid value");
830 return RLM_MODULE_REJECT;
834 * Ensure we don't have buffer overflows.
836 * This really: sizeof(buff) - 2 - 2*32 - strlen(passwd)
838 if (strlen(request->password->vp_strvalue) >= (sizeof(buff) - 2 - 2 * 32)) {
839 RDEBUG("Configured password is too long");
840 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
841 "rlm_pap: password is too long");
842 return RLM_MODULE_REJECT;
846 * Set up the algorithm.
851 memcpy(p, &vp->vp_octets[32], 32);
854 strcpy(p, request->password->vp_strvalue);
857 memcpy(p, &vp->vp_octets[32], 32);
860 fr_MD5Init(&md5_context);
861 fr_MD5Update(&md5_context, (uint8_t *) buff2, p - buff2);
862 fr_MD5Final(buff, &md5_context);
865 if (rad_digest_cmp(binbuf, buff, 16) != 0) {
866 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
867 "rlm_pap: NS-MTA-MD5 password check failed");
868 return RLM_MODULE_REJECT;
871 return RLM_MODULE_OK;
875 * The module name should be the only globally exported symbol.
876 * That is, everything else should be 'static'.
878 * If the module needs to temporarily modify it's instantiation
879 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
880 * The server will then take care of ensuring that the module
881 * is single-threaded.
886 RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE, /* type */
887 pap_instantiate, /* instantiation */
888 pap_detach, /* detach */
890 pap_authenticate, /* authentication */
891 pap_authorize, /* authorization */
892 NULL, /* preaccounting */
893 NULL, /* accounting */
894 NULL, /* checksimul */
895 NULL, /* pre-proxy */
896 NULL, /* post-proxy */