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