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