use new RCSID macro to prevent Id keyword from being optimized out
[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/autoconf.h>
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <ctype.h>
34
35 #include <freeradius-devel/radiusd.h>
36 #include <freeradius-devel/modules.h>
37 #include "../../include/md5.h"
38 #include "../../include/sha1.h"
39
40 #define PAP_ENC_INVALID -1
41 #define PAP_ENC_CLEAR           0
42 #define PAP_ENC_CRYPT           1
43 #define PAP_ENC_MD5             2
44 #define PAP_ENC_SHA1            3
45 #define PAP_ENC_NT              4
46 #define PAP_ENC_LM              5
47 #define PAP_ENC_SMD5            6
48 #define PAP_ENC_SSHA            7
49 #define PAP_ENC_NS_MTA_MD5      8
50 #define PAP_ENC_AUTO            9
51 #define PAP_MAX_ENC             9
52
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_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         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         VALUE_PAIR *vp;
291
292         for (vp = request->config_items; vp != NULL; vp = vp->next) {
293                 switch (vp->attribute) {
294                 case PW_USER_PASSWORD: /* deprecated */
295                         found_pw = TRUE;
296
297                         /*
298                          *      Look for '{foo}', and use them
299                          */
300                         if (!inst->auto_header ||
301                             (vp->vp_strvalue[0] != '{')) {
302                                 break;
303                         }
304                         /* FALL-THROUGH */
305
306                 case PW_PASSWORD_WITH_HEADER: /* preferred */
307                 {
308                         int attr;
309                         uint8_t *p, *q;
310                         char buffer[64];
311                         VALUE_PAIR *new_vp;
312                         
313                         found_pw = TRUE;
314                         q = vp->vp_strvalue;
315                         p = strchr(q + 1, '}');
316                         if (!p) {
317                                 /*
318                                  *      FIXME: Turn it into a
319                                  *      cleartext-password, unless it,
320                                  *      or user-password already
321                                  *      exists.
322                                  */
323                                 break;
324                         }
325                         
326                         if ((size_t) (p - q) > sizeof(buffer)) break;
327                         
328                         memcpy(buffer, q, p - q + 1);
329                         buffer[p - q + 1] = '\0';
330                         
331                         attr = lrad_str2int(header_names, buffer, 0);
332                         if (!attr) {
333                                 DEBUG2("rlm_pap: Found unknown header {%s}: Not doing anything", buffer);
334                                 break;
335                         }
336                         
337                         new_vp = paircreate(attr, PW_TYPE_STRING);
338                         if (!new_vp) break; /* OOM */
339                         
340                         strcpy(new_vp->vp_strvalue, p + 1);/* bounds OK */
341                         new_vp->length = strlen(new_vp->vp_strvalue);
342                         pairadd(&request->config_items, new_vp);
343
344                         /*
345                          *      May be old-style User-Password with header.
346                          *      We've found the header & created the proper
347                          *      attribute, so we should delete the old
348                          *      User-Password here.
349                          */
350                         pairdelete(&request->config_items, PW_USER_PASSWORD);
351                 }
352                         break;
353
354                 case PW_CLEARTEXT_PASSWORD:
355                 case PW_CRYPT_PASSWORD:
356                 case PW_NS_MTA_MD5_PASSWORD:
357                         found_pw = TRUE;
358                         break;  /* don't touch these */
359
360                 case PW_MD5_PASSWORD:
361                 case PW_SMD5_PASSWORD:
362                 case PW_NT_PASSWORD:
363                 case PW_LM_PASSWORD:
364                         normify(vp, 16); /* ensure it's in the right format */
365                         found_pw = TRUE;
366                         break;
367
368                 case PW_SHA_PASSWORD:
369                 case PW_SSHA_PASSWORD:
370                         normify(vp, 20); /* ensure it's in the right format */
371                         found_pw = TRUE;
372                         break;
373
374                         /*
375                          *      If it's proxied somewhere, don't complain
376                          *      about not having passwords or Auth-Type.
377                          */
378                 case PW_PROXY_TO_REALM:
379                 {
380                         REALM *realm = realm_find(vp->vp_strvalue, 0);
381                         if (realm &&
382                             (realm->ipaddr.af == AF_INET) &&
383                             (realm->ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_NONE))) {
384                                 return RLM_MODULE_NOOP;
385                         }
386                         break;
387                 }
388
389                 case PW_AUTH_TYPE:
390                         auth_type = TRUE;
391
392                         /*
393                          *      Auth-Type := Accept
394                          *      Auth-Type := Reject
395                          */
396                         if ((vp->lvalue == 254) ||
397                             (vp->lvalue == 4)) {
398                             found_pw = 1;
399                         }
400                         break;
401
402                 default:
403                         break;  /* ignore it */
404                         
405                 }
406         }
407
408         /*
409          *      Print helpful warnings if there was no password.
410          */
411         if (!found_pw) {
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 };