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