9fa68eaea6f96ac94da8c3d91def2bcd201f25eb
[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, 0, 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[128];
317                         VALUE_PAIR *new_vp;
318
319                         found_pw = TRUE;
320                 redo:
321                         q = vp->vp_strvalue;
322                         p = strchr(q + 1, '}');
323                         if (!p) {
324                                 int decoded;
325
326                                 /*
327                                  *      Password already exists: use
328                                  *      that instead of this one.
329                                  */
330                                 if (pairfind(request->config_items, PW_USER_PASSWORD) ||
331                                     pairfind(request->config_items, PW_CLEARTEXT_PASSWORD)) {
332                                         RDEBUG("Config already contains \"known good\" password.  Ignoring Password-With-Header");
333                                         break;
334                                 }
335
336                                 /*
337                                  *      If it's binary, it may be
338                                  *      base64 encoded.  Decode it,
339                                  *      and re-write the attribute to
340                                  *      have the decoded value.
341                                  */
342                                 decoded = base64_decode(vp->vp_strvalue, buffer);
343                                 if ((decoded > 0) && (buffer[0] == '{') &&
344                                     (strchr(buffer, '}') != NULL)) {
345                                         memcpy(vp->vp_octets, buffer, decoded);
346                                         vp->length = decoded;
347                                         goto redo;
348                                 }
349
350                                 RDEBUG("Failed to decode Password-With-Header = \"%s\"", vp->vp_strvalue);
351                                 break;
352                         }
353
354                         if ((size_t) (p - q) > sizeof(buffer)) break;
355
356                         memcpy(buffer, q, p - q + 1);
357                         buffer[p - q + 1] = '\0';
358
359                         attr = fr_str2int(header_names, buffer, 0);
360                         if (!attr) {
361                                 RDEBUG2("Found unknown header {%s}: Not doing anything", buffer);
362                                 break;
363                         }
364
365                         new_vp = radius_paircreate(request,
366                                                    &request->config_items,
367                                                    attr, 0, PW_TYPE_STRING);
368                         
369                         /*
370                          *      The data after the '}' may be binary,
371                          *      so we copy it via memcpy.
372                          */
373                         new_vp->length = vp->length;
374                         new_vp->length -= (p - q + 1);
375                         memcpy(new_vp->vp_strvalue, p + 1, new_vp->length);
376
377                         /*
378                          *      May be old-style User-Password with header.
379                          *      We've found the header & created the proper
380                          *      attribute, so we should delete the old
381                          *      User-Password here.
382                          */
383                         pairdelete(&request->config_items, PW_USER_PASSWORD, 0);
384                 }
385                         break;
386
387                 case PW_CLEARTEXT_PASSWORD:
388                 case PW_CRYPT_PASSWORD:
389                 case PW_NS_MTA_MD5_PASSWORD:
390                         found_pw = TRUE;
391                         break;  /* don't touch these */
392
393                 case PW_MD5_PASSWORD:
394                 case PW_SMD5_PASSWORD:
395                 case PW_NT_PASSWORD:
396                 case PW_LM_PASSWORD:
397                         normify(request, vp, 16); /* ensure it's in the right format */
398                         found_pw = TRUE;
399                         break;
400
401                 case PW_SHA_PASSWORD:
402                 case PW_SSHA_PASSWORD:
403                         normify(request, vp, 20); /* ensure it's in the right format */
404                         found_pw = TRUE;
405                         break;
406
407                         /*
408                          *      If it's proxied somewhere, don't complain
409                          *      about not having passwords or Auth-Type.
410                          */
411                 case PW_PROXY_TO_REALM:
412                 {
413                         REALM *realm = realm_find(vp->vp_strvalue);
414                         if (realm && realm->auth_pool) {
415                                 return RLM_MODULE_NOOP;
416                         }
417                         break;
418                 }
419
420                 case PW_AUTH_TYPE:
421                         auth_type = TRUE;
422
423                         /*
424                          *      Auth-Type := Accept
425                          *      Auth-Type := Reject
426                          */
427                         if ((vp->vp_integer == 254) ||
428                             (vp->vp_integer == 4)) {
429                             found_pw = 1;
430                         }
431                         break;
432
433                 default:
434                         break;  /* ignore it */
435
436                 }
437         }
438
439         /*
440          *      Print helpful warnings if there was no password.
441          */
442         if (!found_pw) {
443                 /*
444                  *      Likely going to be proxied.  Avoid printing
445                  *      warning message.
446                  */
447                 if (pairfind(request->config_items, PW_REALM, 0) ||
448                     (pairfind(request->config_items, PW_PROXY_TO_REALM, 0))) {
449                         return RLM_MODULE_NOOP;
450                 }
451
452                 /*
453                  *      The TLS types don't need passwords.
454                  */
455                 vp = pairfind(request->packet->vps, PW_EAP_TYPE, 0);
456                 if (vp &&
457                     ((vp->vp_integer == 13) || /* EAP-TLS */
458                      (vp->vp_integer == 21) || /* EAP-TTLS */
459                      (vp->vp_integer == 25))) { /* PEAP */
460                         return RLM_MODULE_NOOP;
461                 }
462
463                 RDEBUG("WARNING! No \"known good\" password found for the user.  Authentication may fail because of this.");
464                 return RLM_MODULE_NOOP;
465         }
466
467         /*
468          *      Don't touch existing Auth-Types.
469          */
470         if (auth_type) {
471                 RDEBUG2("Found existing Auth-Type, not changing it.");
472                 return RLM_MODULE_NOOP;
473         }
474
475         /*
476          *      Can't do PAP if there's no password.
477          */
478         if (!request->password ||
479             (request->password->attribute != PW_USER_PASSWORD)) {
480                 /*
481                  *      Don't print out debugging messages if we know
482                  *      they're useless.
483                  */
484                 if (request->packet->code == PW_ACCESS_CHALLENGE) {
485                         return RLM_MODULE_NOOP;
486                 }
487
488                 RDEBUG2("No clear-text password in the request.  Not performing PAP.");
489                 return RLM_MODULE_NOOP;
490         }
491
492         if (inst->auth_type) {
493                 vp = radius_paircreate(request, &request->config_items,
494                                        PW_AUTH_TYPE, 0, PW_TYPE_INTEGER);
495                 vp->vp_integer = inst->auth_type;
496         }
497
498         return RLM_MODULE_UPDATED;
499 }
500
501
502 /*
503  *      Authenticate the user via one of any well-known password.
504  */
505 static int pap_authenticate(void *instance, REQUEST *request)
506 {
507         rlm_pap_t *inst = instance;
508         VALUE_PAIR *vp;
509         VALUE_PAIR *module_fmsg_vp;
510         char module_fmsg[MAX_STRING_LEN];
511         FR_MD5_CTX md5_context;
512         fr_SHA1_CTX sha1_context;
513         uint8_t digest[40];
514         char buff[MAX_STRING_LEN];
515         char buff2[MAX_STRING_LEN + 50];
516         int scheme = PAP_ENC_INVALID;
517
518         if (!request->password){
519                 radlog_request(L_AUTH, 0, request, "Attribute \"Password\" is required for authentication.");
520                 return RLM_MODULE_INVALID;
521         }
522
523         /*
524          *      Clear-text passwords are the only ones we support.
525          */
526         if (request->password->attribute != PW_USER_PASSWORD) {
527                 radlog_request(L_AUTH, 0, request, "Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
528                 return RLM_MODULE_INVALID;
529         }
530
531         /*
532          *      The user MUST supply a non-zero-length password.
533          */
534         if (request->password->length == 0) {
535                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: empty password supplied");
536                 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
537                 pairadd(&request->packet->vps, module_fmsg_vp);
538                 return RLM_MODULE_INVALID;
539         }
540
541         RDEBUG("login attempt with password \"%s\"",
542               request->password->vp_strvalue);
543
544         /*
545          *      First, auto-detect passwords, by attribute in the
546          *      config items.
547          */
548         if (inst->sch == PAP_ENC_AUTO) {
549                 for (vp = request->config_items; vp != NULL; vp = vp->next) {
550                         switch (vp->attribute) {
551                         case PW_USER_PASSWORD: /* deprecated */
552                         case PW_CLEARTEXT_PASSWORD: /* preferred */
553                                 goto do_clear;
554
555                         case PW_CRYPT_PASSWORD:
556                                 goto do_crypt;
557
558                         case PW_MD5_PASSWORD:
559                                 goto do_md5;
560
561                         case PW_SHA_PASSWORD:
562                                 goto do_sha;
563
564                         case PW_NT_PASSWORD:
565                                 goto do_nt;
566
567                         case PW_LM_PASSWORD:
568                                 goto do_lm;
569
570                         case PW_SMD5_PASSWORD:
571                                 goto do_smd5;
572
573                         case PW_SSHA_PASSWORD:
574                                 goto do_ssha;
575
576                         case PW_NS_MTA_MD5_PASSWORD:
577                                 goto do_ns_mta_md5;
578
579                         default:
580                                 break;  /* ignore it */
581
582                         }
583                 }
584
585         fail:
586                 RDEBUG("No password configured for the user.  Cannot do authentication");
587                 return RLM_MODULE_FAIL;
588
589         } else {
590                 vp = NULL;
591
592                 if (inst->sch == PAP_ENC_CRYPT) {
593                         vp = pairfind(request->config_items, PW_CRYPT_PASSWORD, 0);
594                 }
595
596                 /*
597                  *      Old-style: all passwords are in User-Password.
598                  */
599                 if (!vp) {
600                         vp = pairfind(request->config_items, PW_USER_PASSWORD, 0);
601                         if (!vp) goto fail;
602                 }
603         }
604
605         /*
606          *      Now that we've decided what to do, go do it.
607          */
608         switch (scheme) {
609         case PAP_ENC_CLEAR:
610         do_clear:
611                 RDEBUG("Using clear text password \"%s\"",
612                       vp->vp_strvalue);
613                 if (strcmp((char *) vp->vp_strvalue,
614                            (char *) request->password->vp_strvalue) != 0){
615                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CLEAR TEXT password check failed");
616                         goto make_msg;
617                 }
618         done:
619                 RDEBUG("User authenticated successfully");
620                 return RLM_MODULE_OK;
621                 break;
622
623         case PAP_ENC_CRYPT:
624         do_crypt:
625                 RDEBUG("Using CRYPT encryption.");
626                 if (fr_crypt_check((char *) request->password->vp_strvalue,
627                                      (char *) 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(digest, &md5_context);
649                 if (memcmp(digest, 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(digest, &md5_context);
672
673                 /*
674                  *      Compare only the MD5 hash results, not the salt.
675                  */
676                 if (memcmp(digest, 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(digest,&sha1_context);
698                 if (memcmp(digest, 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(digest,&sha1_context);
722                 if (memcmp(digest, 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(digest, sizeof(digest),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(digest, digest, 16) != vp->length) ||
748                     (memcmp(digest, 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(digest,sizeof(digest),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(digest, digest, 16) != vp->length) ||
772                     (memcmp(digest, 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, buff, 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(buff2) - 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(digest, &md5_context);
833                 }
834                 if (memcmp(digest, 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 };