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