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