3d3a40a7a7fc61756408f59d66bcaac547fbc543
[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
32 #include "radiusd.h"
33 #include "modules.h"
34 #include "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 CONF_PARSER module_config[] = {
79   { "encryption_scheme", PW_TYPE_STRING_PTR, offsetof(rlm_pap_t,scheme), NULL, "crypt" },
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_CLEARTEXT_PASSWORD },
103         { "{cleartext}", PW_CLEARTEXT_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
149         if (inst->scheme == NULL || strlen(inst->scheme) == 0){
150                 radlog(L_ERR, "rlm_pap: No scheme defined");
151                 pap_detach(inst);
152                 return -1;
153         }
154
155         inst->sch = lrad_str2int(schemes, inst->scheme, PAP_ENC_INVALID);
156         if (inst->sch == PAP_ENC_INVALID) {
157                 radlog(L_ERR, "rlm_pap: Unknown scheme \"%s\"", inst->scheme);
158                 pap_detach(inst);
159                 return -1;
160         }
161
162         *instance = inst;
163         inst->name = cf_section_name2(conf);
164         if (!inst->name) {
165                 inst->name = cf_section_name1(conf);
166         }
167
168         return 0;
169 }
170
171
172 /*
173  *      Decode one base64 chunk
174  */
175 static int decode_it(const char *src, uint8_t *dst)
176 {
177         int i;
178         unsigned int x = 0;
179
180         for(i = 0; i < 4; i++) {
181                 if (src[i] >= 'A' && src[i] <= 'Z')
182                         x = (x << 6) + (unsigned int)(src[i] - 'A' + 0);
183                 else if (src[i] >= 'a' && src[i] <= 'z')
184                          x = (x << 6) + (unsigned int)(src[i] - 'a' + 26);
185                 else if(src[i] >= '0' && src[i] <= '9') 
186                          x = (x << 6) + (unsigned int)(src[i] - '0' + 52);
187                 else if(src[i] == '+')
188                         x = (x << 6) + 62;
189                 else if (src[i] == '/')
190                         x = (x << 6) + 63;
191                 else if (src[i] == '=')
192                         x = (x << 6);
193                 else return 0;
194         }
195         
196         dst[2] = (unsigned char)(x & 255); x >>= 8;
197         dst[1] = (unsigned char)(x & 255); x >>= 8;
198         dst[0] = (unsigned char)(x & 255); x >>= 8;
199         
200         return 1;
201 }
202
203
204 /*
205  *      Base64 decoding.
206  */
207 static int base64_decode (const char *src, uint8_t *dst)
208 {
209         int length, equals;
210         int i, num;
211         uint8_t last[3];
212
213         length = equals = 0;
214         while (src[length] && src[length] != '=') length++;
215
216         if (src[length] != '=') return 0; /* no trailing '=' */
217
218         while (src[length + equals] == '=') equals++;
219
220         num = (length + equals) / 4;
221         
222         for (i = 0; i < num - 1; i++) {
223                 if (!decode_it(src, dst)) return 0;
224                 src += 4;
225                 dst += 3;
226         }
227
228         decode_it(src, last);
229         for (i = 0; i < (3 - equals); i++) {
230                 dst[i] = last[i];
231         }
232
233         return (num * 3) - equals;
234 }
235
236
237 /*
238  *      Hex or base64 or bin auto-discovery.
239  */
240 static void normify(VALUE_PAIR *vp, int min_length)
241 {
242         int decoded;
243         char buffer[64];
244         
245         if ((size_t) min_length >= sizeof(buffer)) return; /* paranoia */
246
247         /*
248          *      Hex encoding.
249          */
250         if (vp->length >= (2 * min_length)) {
251                 decoded = lrad_hex2bin(vp->strvalue, buffer, vp->length >> 1);
252                 if (decoded == (vp->length >> 1)) {
253                         DEBUG2("rlm_pap: Normalizing %s from hex encoding", vp->name);
254                         memcpy(vp->strvalue, buffer, decoded);
255                         vp->length = decoded;
256                         return;
257                 }
258         }
259
260         /*
261          *      Base 64 encoding.  It's at least 4/3 the original size,
262          *      and we want to avoid division...
263          */
264         if ((vp->length * 3) >= ((min_length * 4))) {
265                 decoded = base64_decode(vp->strvalue, buffer);
266                 if (decoded >= min_length) {
267                         DEBUG2("rlm_pap: Normalizing %s from base64 encoding", vp->name);
268                         memcpy(vp->strvalue, buffer, decoded);
269                         vp->length = decoded;
270                         return;
271                 }
272         }
273
274         /*
275          *      Else unknown encoding, or already binary.  Leave it.
276          */
277 }
278
279
280 /*
281  *      Authorize the user for PAP authentication.
282  *
283  *      This isn't strictly necessary, but it does make the
284  *      server simpler to configure.
285  */
286 static int pap_authorize(void *instance, REQUEST *request)
287 {
288         rlm_pap_t *inst = instance;
289         int auth_type = FALSE;
290         int found_pw = FALSE;
291         int user_pw = FALSE;
292         VALUE_PAIR *vp;
293         VALUE_PAIR *cleartext_pw = NULL;
294
295         for (vp = request->config_items; vp != NULL; vp = vp->next) {
296                 switch (vp->attribute) {
297                 case PW_USER_PASSWORD: /* deprecated */
298                         user_pw = TRUE;
299                         found_pw = TRUE;
300
301                         /*
302                          *      Look for '{foo}', and use them
303                          */
304                         if (!inst->auto_header ||
305                             (vp->strvalue[0] != '{')) {
306                                 break;
307                         }
308                         /* FALL-THROUGH */
309
310                 case PW_PASSWORD_WITH_HEADER: /* preferred */
311                 {
312                         int attr;
313                         uint8_t *p, *q;
314                         char buffer[64];
315                         VALUE_PAIR *new_vp;
316                         
317                         found_pw = TRUE;
318                         q = vp->strvalue;
319                         p = strchr(q + 1, '}');
320                         if (!p) {
321                                 /*
322                                  *      FIXME: Turn it into a
323                                  *      cleartext-password, unless it,
324                                  *      or user-password already
325                                  *      exists.
326                                  */
327                                 break;
328                         }
329                         
330                         if ((size_t) (p - q) > sizeof(buffer)) break;
331                         
332                         memcpy(buffer, q, p - q + 1);
333                         buffer[p - q + 1] = '\0';
334                         
335                         attr = lrad_str2int(header_names, buffer, 0);
336                         if (!attr) {
337                                 DEBUG2("rlm_pap: Found unknown header {%s}: Not doing anything", buffer);
338                                 break;
339                         }
340                         
341                         new_vp = paircreate(attr, PW_TYPE_STRING);
342                         if (!new_vp) break; /* OOM */
343                         
344                         strcpy(new_vp->strvalue, p + 1);/* bounds OK */
345                         new_vp->length = strlen(new_vp->strvalue);
346                         pairadd(&request->config_items, new_vp);
347
348                         /*
349                          *      May be old-style User-Password with header.
350                          *      We've found the header & created the proper
351                          *      attribute, so we should delete the old
352                          *      User-Password here.
353                          */
354                         pairdelete(&request->config_items, PW_USER_PASSWORD);
355                 }
356                         break;
357
358                 case PW_CLEARTEXT_PASSWORD:
359                         cleartext_pw = vp;
360
361                 case PW_CRYPT_PASSWORD:
362                 case PW_NS_MTA_MD5_PASSWORD:
363                         found_pw = TRUE;
364                         break;  /* don't touch these */
365
366                 case PW_MD5_PASSWORD:
367                 case PW_SMD5_PASSWORD:
368                 case PW_NT_PASSWORD:
369                 case PW_LM_PASSWORD:
370                         normify(vp, 16); /* ensure it's in the right format */
371                         found_pw = TRUE;
372                         break;
373
374                 case PW_SHA_PASSWORD:
375                 case PW_SSHA_PASSWORD:
376                         normify(vp, 20); /* ensure it's in the right format */
377                         found_pw = TRUE;
378                         break;
379
380                         /*
381                          *      If it's proxied somewhere, don't complain
382                          *      about not having passwords or Auth-Type.
383                          */
384                 case PW_PROXY_TO_REALM:
385                 {
386                         REALM *realm = realm_find(vp->strvalue, 0);
387                         if (realm &&
388                             (realm->ipaddr != htonl(INADDR_NONE))) {
389                                 return RLM_MODULE_NOOP;
390                         }
391                         break;
392                 }
393
394                 case PW_AUTH_TYPE:
395                         auth_type = TRUE;
396
397                         /*
398                          *      Auth-Type := Accept
399                          *      Auth-Type := Reject
400                          */
401                         if ((vp->lvalue == 254) ||
402                             (vp->lvalue == 4)) {
403                             found_pw = 1;
404                         }
405                         break;
406
407                 default:
408                         break;  /* ignore it */
409                         
410                 }
411         }
412
413         /*
414          *      Print helpful warnings if there was no password.
415          */
416         if (!found_pw) {
417                 DEBUG("rlm_pap: WARNING! No \"known good\" password found for the user.  Authentication may fail because of this.");
418                 return RLM_MODULE_NOOP;
419         }
420
421         /*
422          *      For backwards compatibility with all of the other
423          *      modules, copy Cleartext-Password to User-Password
424          */
425         if (cleartext_pw && !user_pw) {
426                 vp = paircreate(PW_USER_PASSWORD, PW_TYPE_STRING);
427                 if (!vp) return RLM_MODULE_FAIL;
428
429                 memcpy(vp, cleartext_pw, sizeof(*vp));
430                 vp->next = NULL;
431                 pairadd(&request->config_items, vp);
432         }
433
434         /*
435          *      Don't touch existing Auth-Types.
436          */
437         if (auth_type) {
438                 DEBUG2("rlm_pap: Found existing Auth-Type, not changing it.");
439                 return RLM_MODULE_NOOP;
440         }       
441
442         /*
443          *      Can't do PAP if there's no password.
444          */
445         if (!request->password ||
446             (request->password->attribute != PW_USER_PASSWORD)) {
447                 /*
448                  *      Don't print out debugging messages if we know
449                  *      they're useless.
450                  */
451                 if (request->packet->code == PW_ACCESS_CHALLENGE) {
452                         return RLM_MODULE_NOOP;
453                 }
454
455                 DEBUG2("rlm_pap: No clear-text password in the request.  Not performing PAP.");
456                 return RLM_MODULE_NOOP;
457         }
458
459         vp = paircreate(PW_AUTH_TYPE, PW_TYPE_INTEGER);
460         if (!vp) return RLM_MODULE_FAIL;
461         pairparsevalue(vp, inst->name);
462
463         pairadd(&request->config_items, vp);
464
465         return RLM_MODULE_UPDATED;
466 }
467
468
469 /*
470  *      Authenticate the user via one of any well-known password.
471  */
472 static int pap_authenticate(void *instance, REQUEST *request)
473 {
474         rlm_pap_t *inst = instance;
475         VALUE_PAIR *vp;
476         VALUE_PAIR *module_fmsg_vp;
477         char module_fmsg[MAX_STRING_LEN];
478         MD5_CTX md5_context;
479         SHA1_CTX sha1_context;
480         uint8_t digest[40];
481         char buff[MAX_STRING_LEN];
482         char buff2[MAX_STRING_LEN + 50];
483         int scheme = PAP_ENC_INVALID;
484
485         if (!request->password){
486                 radlog(L_AUTH, "rlm_pap: Attribute \"Password\" is required for authentication.");
487                 return RLM_MODULE_INVALID;
488         }
489
490         /*
491          *      Clear-text passwords are the only ones we support.
492          */
493         if (request->password->attribute != PW_USER_PASSWORD) {
494                 radlog(L_AUTH, "rlm_pap: Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
495                 return RLM_MODULE_INVALID;
496         }
497
498         /*
499          *      The user MUST supply a non-zero-length password.
500          */
501         if (request->password->length == 0) {
502                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: empty password supplied");
503                 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
504                 pairadd(&request->packet->vps, module_fmsg_vp);
505                 return RLM_MODULE_INVALID;
506         }
507
508         DEBUG("rlm_pap: login attempt with password %s",
509               request->password->strvalue);
510
511         /*
512          *      First, auto-detect passwords, by attribute in the
513          *      config items.
514          */
515         if ((inst->sch == PAP_ENC_AUTO) || inst->auto_header) {
516                 for (vp = request->config_items; vp != NULL; vp = vp->next) {
517                         switch (vp->attribute) {
518                         case PW_USER_PASSWORD: /* deprecated */
519                         case PW_CLEARTEXT_PASSWORD: /* preferred */
520                                 goto do_clear;
521                                 
522                         case PW_CRYPT_PASSWORD:
523                                 goto do_crypt;
524                                 
525                         case PW_MD5_PASSWORD:
526                                 goto do_md5;
527                                 
528                         case PW_SHA_PASSWORD:
529                                 goto do_sha;
530                                 
531                         case PW_NT_PASSWORD:
532                                 goto do_nt;
533
534                         case PW_LM_PASSWORD:
535                                 goto do_lm;
536
537                         case PW_SMD5_PASSWORD:
538                                 goto do_smd5;
539
540                         case PW_SSHA_PASSWORD:
541                                 goto do_ssha;
542
543                         case PW_NS_MTA_MD5_PASSWORD:
544                                 goto do_ns_mta_md5;
545
546                         default:
547                                 break;  /* ignore it */
548                                 
549                         }
550                 }
551
552         fail:
553                 DEBUG("rlm_pap: No password configured for the user.  Cannot do authentication");
554                 return RLM_MODULE_FAIL;
555
556         } else {
557                 vp = NULL;
558                 
559                 if (inst->sch == PAP_ENC_CRYPT) {
560                         vp = pairfind(request->config_items, PW_CRYPT_PASSWORD);
561                 }
562
563                 /*
564                  *      Old-style: all passwords are in User-Password.
565                  */
566                 if (!vp) {
567                         vp = pairfind(request->config_items, PW_USER_PASSWORD);
568                         if (!vp) goto fail;
569                 }
570                 scheme = inst->sch;
571         }
572
573         /*
574          *      Now that we've decided what to do, go do it.
575          */
576         switch (scheme) {
577         case PAP_ENC_CLEAR:
578         do_clear:
579                 DEBUG("rlm_pap: Using clear text password.");
580                 if (strcmp((char *) vp->strvalue,
581                            (char *) request->password->strvalue) != 0){
582                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CLEAR TEXT password check failed");
583                         goto make_msg;
584                 }
585         done:
586                 DEBUG("rlm_pap: User authenticated succesfully");
587                 return RLM_MODULE_OK;
588                 break;
589                 
590         case PAP_ENC_CRYPT:
591         do_crypt:
592                 DEBUG("rlm_pap: Using CRYPT encryption.");
593                 if (lrad_crypt_check((char *) request->password->strvalue,
594                                      (char *) vp->strvalue) != 0) {
595                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CRYPT password check failed");
596                         goto make_msg;
597                 }
598                 goto done;
599                 break;
600                 
601         case PW_MD5_PASSWORD:
602         do_md5:
603                 DEBUG("rlm_pap: Using MD5 encryption.");
604
605                 normify(vp, 16);
606                 if (vp->length != 16) {
607                 DEBUG("rlm_pap: Configured MD5 password has incorrect length");
608                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured MD5 password has incorrect length");
609                         goto make_msg;
610                 }
611                 
612                 MD5Init(&md5_context);
613                 MD5Update(&md5_context, request->password->strvalue,
614                           request->password->length);
615                 MD5Final(digest, &md5_context);
616                 if (memcmp(digest, vp->strvalue, vp->length) != 0) {
617                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: MD5 password check failed");
618                         goto make_msg;
619                 }
620                 goto done;
621                 break;
622                 
623         case PW_SMD5_PASSWORD:
624         do_smd5:
625                 DEBUG("rlm_pap: Using SMD5 encryption.");
626
627                 normify(vp, 16);
628                 if (vp->length <= 16) {
629                         DEBUG("rlm_pap: Configured SMD5 password has incorrect length");
630                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured SMD5 password has incorrect length");
631                         goto make_msg;
632                 }
633                 
634                 MD5Init(&md5_context);
635                 MD5Update(&md5_context, request->password->strvalue,
636                           request->password->length);
637                 MD5Update(&md5_context, &vp->strvalue[16], vp->length - 16);
638                 MD5Final(digest, &md5_context);
639
640                 /*
641                  *      Compare only the MD5 hash results, not the salt.
642                  */
643                 if (memcmp(digest, vp->strvalue, 16) != 0) {
644                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: SMD5 password check failed");
645                         goto make_msg;
646                 }
647                 goto done;
648                 break;
649                 
650         case PW_SHA_PASSWORD:
651         do_sha:
652                 DEBUG("rlm_pap: Using SHA1 encryption.");
653                 
654                 normify(vp, 20);
655                 if (vp->length != 20) {
656                         DEBUG("rlm_pap: Configured SHA1 password has incorrect length");
657                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured SHA1 password has incorrect length");
658                         goto make_msg;
659                 }
660                 
661                 SHA1Init(&sha1_context);
662                 SHA1Update(&sha1_context, request->password->strvalue,
663                            request->password->length);
664                 SHA1Final(digest,&sha1_context);
665                 if (memcmp(digest, vp->strvalue, vp->length) != 0) {
666                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: SHA1 password check failed");
667                         goto make_msg;
668                 }
669                 goto done;
670                 break;
671                 
672         case PW_SSHA_PASSWORD:
673         do_ssha:
674                 DEBUG("rlm_pap: Using SSHA encryption.");
675                 
676                 normify(vp, 20);
677                 if (vp->length <= 20) {
678                         DEBUG("rlm_pap: Configured SSHA password has incorrect length");
679                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured SHA password has incorrect length");
680                         goto make_msg;
681                 }
682
683                 
684                 SHA1Init(&sha1_context);
685                 SHA1Update(&sha1_context, request->password->strvalue,
686                            request->password->length);
687                 SHA1Update(&sha1_context, &vp->strvalue[20], vp->length - 20);
688                 SHA1Final(digest,&sha1_context);
689                 if (memcmp(digest, vp->strvalue, 20) != 0) {
690                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: SSHA password check failed");
691                         goto make_msg;
692                 }
693                 goto done;
694                 break;
695                 
696         case PW_NT_PASSWORD:
697         do_nt:
698                 DEBUG("rlm_pap: Using NT encryption.");
699
700                 normify(vp, 16);
701                 if (vp->length != 16) {
702                         DEBUG("rlm_pap: Configured NT-Password has incorrect length");
703                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured NT-Password has incorrect length");
704                         goto make_msg;
705                 }
706                 
707                 sprintf(buff2,"%%{mschap:NT-Hash %s}",
708                         request->password->strvalue);
709                 if (!radius_xlat(digest,sizeof(digest),buff2,request,NULL)){
710                         DEBUG("rlm_pap: mschap xlat failed");
711                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: mschap xlat failed");
712                         goto make_msg;
713                 }
714                 if ((lrad_hex2bin(digest, digest, 16) != vp->length) ||
715                     (memcmp(digest, vp->strvalue, vp->length) != 0)) {
716                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: NT password check failed");
717                         goto make_msg;
718                 }
719                 goto done;
720                 break;
721                 
722         case PW_LM_PASSWORD:
723         do_lm:
724                 DEBUG("rlm_pap: Using LM encryption.");
725                 
726                 normify(vp, 16);
727                 if (vp->length != 16) {
728                         DEBUG("rlm_pap: Configured LM-Password has incorrect length");
729                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured LM-Password has incorrect length");
730                         goto make_msg;
731                 }
732                 sprintf(buff2,"%%{mschap:LM-Hash %s}",
733                         request->password->strvalue);
734                 if (!radius_xlat(digest,sizeof(digest),buff2,request,NULL)){
735                         DEBUG("rlm_pap: mschap xlat failed");
736                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: mschap xlat failed");
737                         goto make_msg;
738                 }
739                 if ((lrad_hex2bin(digest, digest, 16) != vp->length) ||
740                     (memcmp(digest, vp->strvalue, vp->length) != 0)) {
741                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: LM password check failed");
742                 make_msg:
743                         DEBUG("rlm_pap: Passwords don't match");
744                         module_fmsg_vp = pairmake("Module-Failure-Message",
745                                                   module_fmsg, T_OP_EQ);
746                         pairadd(&request->packet->vps, module_fmsg_vp);
747                         return RLM_MODULE_REJECT;
748                 }
749                 goto done;
750                 break;
751
752         case PAP_ENC_NS_MTA_MD5:
753         do_ns_mta_md5:
754                 DEBUG("rlm_pap: Using NT-MTA-MD5 password");
755
756                 if (vp->length != 64) {
757                         DEBUG("rlm_pap: Configured NS-MTA-MD5-Password has incorrect length");
758                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured NS-MTA-MD5-Password has incorrect length");
759                         goto make_msg;
760                 }
761
762                 /*
763                  *      Sanity check the value of NS-MTA-MD5-Password
764                  */
765                 if (lrad_hex2bin(vp->strvalue, buff, 32) != 16) {
766                         DEBUG("rlm_pap: Configured NS-MTA-MD5-Password has invalid value");
767                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured NS-MTA-MD5-Password has invalid value");
768                         goto make_msg;
769                 }
770
771                 /*
772                  *      Ensure we don't have buffer overflows.
773                  *
774                  *      This really: sizeof(buff) - 2 - 2*32 - strlen(passwd)
775                  */
776                 if (strlen(request->password->strvalue) >= (sizeof(buff2) - 2 - 2 * 32)) {
777                         DEBUG("rlm_pap: Configured password is too long");
778                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: password is too long");
779                         goto make_msg;
780                 }
781
782                 /*
783                  *      Set up the algorithm.
784                  */
785                 {
786                         char *p = buff2;
787
788                         memcpy(p, &vp->strvalue[32], 32);
789                         p += 32;
790                         *(p++) = 89;
791                         strcpy(p, request->password->strvalue);
792                         p += strlen(p);
793                         *(p++) = 247;
794                         memcpy(p, &vp->strvalue[32], 32);
795                         p += 32;
796
797                         MD5Init(&md5_context);
798                         MD5Update(&md5_context, buff2, p - buff2);
799                         MD5Final(digest, &md5_context);
800                 }
801                 if (memcmp(digest, buff, 16) != 0) {
802                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: NS-MTA-MD5 password check failed");
803                         goto make_msg;
804                 }
805                 goto done;
806
807         default:
808                 break;
809         }
810
811         DEBUG("rlm_pap: No password configured for the user.  Cannot do authentication");
812         return RLM_MODULE_FAIL;
813 }
814
815
816 /*
817  *      The module name should be the only globally exported symbol.
818  *      That is, everything else should be 'static'.
819  *
820  *      If the module needs to temporarily modify it's instantiation
821  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
822  *      The server will then take care of ensuring that the module
823  *      is single-threaded.
824  */
825 module_t rlm_pap = {
826         "PAP",
827         0,                              /* type */
828         NULL,                           /* initialization */
829         pap_instantiate,                /* instantiation */
830         {
831                 pap_authenticate,       /* authentication */
832                 pap_authorize,          /* authorization */
833                 NULL,                   /* preaccounting */
834                 NULL,                   /* accounting */
835                 NULL,                   /* checksimul */
836                 NULL,                   /* pre-proxy */
837                 NULL,                   /* post-proxy */
838                 NULL                    /* post-auth */
839         },
840         pap_detach,                     /* detach */
841         NULL,                           /* destroy */
842 };