remove auth check against User-Password, add autz warning
[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                         RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
264                         RDEBUG("!!! Please update your configuration so that the \"known !!!");
265                         RDEBUG("!!! good\" clear text password is in Cleartext-Password, !!!");
266                         RDEBUG("!!! and NOT in User-Password.                           !!!");
267                         RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
268                         break;
269
270                 case PW_PASSWORD_WITH_HEADER: /* preferred */
271                 {
272                         int attr;
273                         char *p, *q;
274                         uint8_t binbuf[128];
275                         char charbuf[128];
276                         VALUE_PAIR *new_vp;
277
278                         found_pw = TRUE;
279                 redo:
280                         q = vp->vp_strvalue;
281                         p = strchr(q + 1, '}');
282                         if (!p) {
283                                 int decoded;
284
285                                 /*
286                                  *      Password already exists: use
287                                  *      that instead of this one.
288                                  */
289                                 if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0)) {
290                                         RDEBUG("Config already contains \"known good\" password.  Ignoring Password-With-Header");
291                                         break;
292                                 }
293
294                                 /*
295                                  *      If it's binary, it may be
296                                  *      base64 encoded.  Decode it,
297                                  *      and re-write the attribute to
298                                  *      have the decoded value.
299                                  */
300                                 decoded = base64_decode(vp->vp_strvalue, binbuf);
301                                 if ((decoded > 0) && (binbuf[0] == '{') &&
302                                     (memchr(binbuf, '}', decoded) != NULL)) {
303                                         memcpy(vp->vp_octets, binbuf, decoded);
304                                         vp->length = decoded;
305                                         goto redo;
306                                 }
307
308                                 RDEBUG("Failed to decode Password-With-Header = \"%s\"", vp->vp_strvalue);
309                                 break;
310                         }
311
312                         if ((size_t) (p - q) > sizeof(charbuf)) break;
313
314                         memcpy(charbuf, q, p - q + 1);
315                         charbuf[p - q + 1] = '\0';
316
317                         attr = fr_str2int(header_names, charbuf, 0);
318                         if (!attr) {
319                                 RDEBUG2("Found unknown header {%s}: Not doing anything", charbuf);
320                                 break;
321                         }
322
323                         new_vp = radius_paircreate(request,
324                                                    &request->config_items,
325                                                    attr, 0, PW_TYPE_STRING);
326                         
327                         /*
328                          *      The data after the '}' may be binary,
329                          *      so we copy it via memcpy.
330                          */
331                         new_vp->length = vp->length;
332                         new_vp->length -= (p - q + 1);
333                         memcpy(new_vp->vp_strvalue, p + 1, new_vp->length);
334                 }
335                         break;
336
337                 case PW_CLEARTEXT_PASSWORD:
338                 case PW_CRYPT_PASSWORD:
339                 case PW_NS_MTA_MD5_PASSWORD:
340                         found_pw = TRUE;
341                         break;  /* don't touch these */
342
343                 case PW_MD5_PASSWORD:
344                 case PW_SMD5_PASSWORD:
345                 case PW_NT_PASSWORD:
346                 case PW_LM_PASSWORD:
347                         normify(request, vp, 16); /* ensure it's in the right format */
348                         found_pw = TRUE;
349                         break;
350
351                 case PW_SHA_PASSWORD:
352                 case PW_SSHA_PASSWORD:
353                         normify(request, vp, 20); /* ensure it's in the right format */
354                         found_pw = TRUE;
355                         break;
356
357                         /*
358                          *      If it's proxied somewhere, don't complain
359                          *      about not having passwords or Auth-Type.
360                          */
361                 case PW_PROXY_TO_REALM:
362                 {
363                         REALM *realm = realm_find(vp->vp_strvalue);
364                         if (realm && realm->auth_pool) {
365                                 return RLM_MODULE_NOOP;
366                         }
367                         break;
368                 }
369
370                 case PW_AUTH_TYPE:
371                         auth_type = TRUE;
372
373                         /*
374                          *      Auth-Type := Accept
375                          *      Auth-Type := Reject
376                          */
377                         if ((vp->vp_integer == 254) ||
378                             (vp->vp_integer == 4)) {
379                             found_pw = 1;
380                         }
381                         break;
382
383                 default:
384                         break;  /* ignore it */
385
386                 }
387         }
388
389         /*
390          *      Print helpful warnings if there was no password.
391          */
392         if (!found_pw) {
393                 /*
394                  *      Likely going to be proxied.  Avoid printing
395                  *      warning message.
396                  */
397                 if (pairfind(request->config_items, PW_REALM, 0) ||
398                     (pairfind(request->config_items, PW_PROXY_TO_REALM, 0))) {
399                         return RLM_MODULE_NOOP;
400                 }
401
402                 /*
403                  *      The TLS types don't need passwords.
404                  */
405                 vp = pairfind(request->packet->vps, PW_EAP_TYPE, 0);
406                 if (vp &&
407                     ((vp->vp_integer == 13) || /* EAP-TLS */
408                      (vp->vp_integer == 21) || /* EAP-TTLS */
409                      (vp->vp_integer == 25))) { /* PEAP */
410                         return RLM_MODULE_NOOP;
411                 }
412
413                 RDEBUG("WARNING! No \"known good\" password found for the user.  Authentication may fail because of this.");
414                 return RLM_MODULE_NOOP;
415         }
416
417         /*
418          *      Don't touch existing Auth-Types.
419          */
420         if (auth_type) {
421                 RDEBUG2("WARNING: Auth-Type already set.  Not setting to PAP");
422                 return RLM_MODULE_NOOP;
423         }
424
425         /*
426          *      Can't do PAP if there's no password.
427          */
428         if (!request->password ||
429             (request->password->attribute != PW_USER_PASSWORD)) {
430                 /*
431                  *      Don't print out debugging messages if we know
432                  *      they're useless.
433                  */
434                 if (request->packet->code == PW_ACCESS_CHALLENGE) {
435                         return RLM_MODULE_NOOP;
436                 }
437
438                 RDEBUG2("No clear-text password in the request.  Not performing PAP.");
439                 return RLM_MODULE_NOOP;
440         }
441
442         if (inst->auth_type) {
443                 vp = radius_paircreate(request, &request->config_items,
444                                        PW_AUTH_TYPE, 0, PW_TYPE_INTEGER);
445                 vp->vp_integer = inst->auth_type;
446         }
447
448         return RLM_MODULE_UPDATED;
449 }
450
451
452 /*
453  *      Authenticate the user via one of any well-known password.
454  */
455 static int pap_authenticate(void *instance, REQUEST *request)
456 {
457         VALUE_PAIR *vp;
458         VALUE_PAIR *module_fmsg_vp;
459         char module_fmsg[MAX_STRING_LEN];
460         int rc = RLM_MODULE_INVALID;
461         int (*auth_func)(REQUEST *, VALUE_PAIR *, char *) = NULL;
462
463         /* Shut the compiler up */
464         instance = instance;
465
466         if (!request->password ||
467             (request->password->attribute != PW_USER_PASSWORD)) {
468                 RDEBUG("ERROR: You set 'Auth-Type = PAP' for a request that does not contain a User-Password attribute!");
469                 return RLM_MODULE_INVALID;
470         }
471
472         /*
473          *      The user MUST supply a non-zero-length password.
474          */
475         if (request->password->length == 0) {
476                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: empty password supplied");
477                 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
478                 pairadd(&request->packet->vps, module_fmsg_vp);
479                 return RLM_MODULE_INVALID;
480         }
481
482         RDEBUG("login attempt with password \"%s\"", request->password->vp_strvalue);
483
484         /*
485          *      Auto-detect passwords, by attribute in the
486          *      config items, to find out which authentication
487          *      function to call.
488          */
489         for (vp = request->config_items; vp != NULL; vp = vp->next) {
490                 switch (vp->attribute) {
491                 case PW_CLEARTEXT_PASSWORD:
492                         auth_func = &pap_auth_clear;
493                         break;
494
495                 case PW_CRYPT_PASSWORD:
496                         auth_func = &pap_auth_crypt;
497                         break;
498
499                 case PW_MD5_PASSWORD:
500                         auth_func = &pap_auth_md5;
501                         break;
502
503                 case PW_SMD5_PASSWORD:
504                         auth_func = &pap_auth_smd5;
505                         break;
506
507                 case PW_SHA_PASSWORD:
508                         auth_func = &pap_auth_sha;
509                         break;
510
511                 case PW_SSHA_PASSWORD:
512                         auth_func = &pap_auth_ssha;
513                         break;
514
515                 case PW_NT_PASSWORD:
516                         auth_func = &pap_auth_nt;
517                         break;
518
519                 case PW_LM_PASSWORD:
520                         auth_func = &pap_auth_lm;
521                         break;
522
523                 case PW_NS_MTA_MD5_PASSWORD:
524                         auth_func = &pap_auth_ns_mta_md5;
525                         break;
526
527                 default:
528                         break;
529                 }
530
531                 if (auth_func != NULL) break;
532         }
533
534         /*
535          *      No attribute was found that looked like a password to match.
536          */
537         if (auth_func == NULL) {
538                 RDEBUG("No password configured for the user.  Cannot do authentication");
539                 return RLM_MODULE_FAIL;
540         }
541
542         /*
543          *      Authenticate, and return.
544          */
545         rc = auth_func(request, vp, module_fmsg);
546
547         if (rc == RLM_MODULE_REJECT) {
548                 RDEBUG("Passwords don't match");
549                 module_fmsg_vp = pairmake("Module-Failure-Message",
550                                           module_fmsg, T_OP_EQ);
551                 pairadd(&request->packet->vps, module_fmsg_vp);
552         }
553
554         if (rc == RLM_MODULE_OK) {
555                 RDEBUG("User authenticated successfully");
556         }
557
558         return rc;
559 }
560
561
562 /*
563  *      PAP authentication functions
564  */
565
566 static int pap_auth_clear(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
567 {
568         RDEBUG("Using clear text password \"%s\"", vp->vp_strvalue);
569
570         if ((vp->length != request->password->length) ||
571             (rad_digest_cmp(vp->vp_octets,
572                             request->password->vp_octets,
573                             vp->length) != 0)) {
574                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
575                         "rlm_pap: CLEAR TEXT password check failed");
576                 return RLM_MODULE_REJECT;
577         }
578         return RLM_MODULE_OK;
579 }
580
581 static int pap_auth_crypt(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
582 {
583         RDEBUG("Using CRYPT password \"%s\"", vp->vp_strvalue);
584
585         if (fr_crypt_check(request->password->vp_strvalue,
586                            vp->vp_strvalue) != 0) {
587                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
588                         "rlm_pap: CRYPT password check failed");
589                 return RLM_MODULE_REJECT;
590         }
591         return RLM_MODULE_OK;
592 }
593
594 static int pap_auth_md5(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
595 {
596         FR_MD5_CTX md5_context;
597         uint8_t binbuf[128];
598
599         RDEBUG("Using MD5 encryption.");
600
601         normify(request, vp, 16);
602         if (vp->length != 16) {
603                 RDEBUG("Configured MD5 password has incorrect length");
604                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
605                         "rlm_pap: Configured MD5 password has incorrect length");
606                 return RLM_MODULE_REJECT;
607         }
608
609         fr_MD5Init(&md5_context);
610         fr_MD5Update(&md5_context, request->password->vp_octets,
611                      request->password->length);
612         fr_MD5Final(binbuf, &md5_context);
613
614         if (rad_digest_cmp(binbuf, vp->vp_octets, vp->length) != 0) {
615                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
616                         "rlm_pap: MD5 password check failed");
617                 return RLM_MODULE_REJECT;
618         }
619
620         return RLM_MODULE_OK;
621 }
622
623
624 static int pap_auth_smd5(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
625 {
626         FR_MD5_CTX md5_context;
627         uint8_t binbuf[128];
628
629         RDEBUG("Using SMD5 encryption.");
630
631         normify(request, vp, 16);
632         if (vp->length <= 16) {
633                 RDEBUG("Configured SMD5 password has incorrect length");
634                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
635                         "rlm_pap: Configured SMD5 password has incorrect length");
636                 return RLM_MODULE_REJECT;
637         }
638
639         fr_MD5Init(&md5_context);
640         fr_MD5Update(&md5_context, request->password->vp_octets,
641                      request->password->length);
642         fr_MD5Update(&md5_context, &vp->vp_octets[16], vp->length - 16);
643         fr_MD5Final(binbuf, &md5_context);
644
645         /*
646          *      Compare only the MD5 hash results, not the salt.
647          */
648         if (rad_digest_cmp(binbuf, vp->vp_octets, 16) != 0) {
649                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
650                         "rlm_pap: SMD5 password check failed");
651                 return RLM_MODULE_REJECT;
652         }
653
654         return RLM_MODULE_OK;
655 }
656
657 static int pap_auth_sha(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
658 {
659         fr_SHA1_CTX sha1_context;
660         uint8_t binbuf[128];
661
662         RDEBUG("Using SHA1 encryption.");
663
664         normify(request, vp, 20);
665         if (vp->length != 20) {
666                 RDEBUG("Configured SHA1 password has incorrect length");
667                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
668                         "rlm_pap: Configured SHA1 password has incorrect length");
669                 return RLM_MODULE_REJECT;
670         }
671
672         fr_SHA1Init(&sha1_context);
673         fr_SHA1Update(&sha1_context, request->password->vp_octets,
674                       request->password->length);
675         fr_SHA1Final(binbuf,&sha1_context);
676
677         if (rad_digest_cmp(binbuf, vp->vp_octets, vp->length) != 0) {
678                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
679                         "rlm_pap: SHA1 password check failed");
680                 return RLM_MODULE_REJECT;
681         }
682
683         return RLM_MODULE_OK;
684 }
685
686 static int pap_auth_ssha(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
687 {
688         fr_SHA1_CTX sha1_context;
689         uint8_t binbuf[128];
690
691         RDEBUG("Using SSHA encryption.");
692
693         normify(request, vp, 20);
694         if (vp->length <= 20) {
695                 RDEBUG("Configured SSHA password has incorrect length");
696                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
697                         "rlm_pap: Configured SHA password has incorrect length");
698                 return RLM_MODULE_REJECT;
699         }
700
701         fr_SHA1Init(&sha1_context);
702         fr_SHA1Update(&sha1_context, request->password->vp_octets,
703                       request->password->length);
704         fr_SHA1Update(&sha1_context, &vp->vp_octets[20], vp->length - 20);
705         fr_SHA1Final(binbuf,&sha1_context);
706
707         if (rad_digest_cmp(binbuf, vp->vp_octets, 20) != 0) {
708                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
709                         "rlm_pap: SSHA password check failed");
710                 return RLM_MODULE_REJECT;
711         }
712
713         return RLM_MODULE_OK;
714 }
715
716 static int pap_auth_nt(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
717 {
718         uint8_t binbuf[128];
719         char charbuf[128];
720         char buff2[MAX_STRING_LEN + 50];
721
722         RDEBUG("Using NT encryption.");
723
724         normify(request, vp, 16);
725         if (vp->length != 16) {
726                 RDEBUG("Configured NT-Password has incorrect length");
727                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
728                         "rlm_pap: Configured NT-Password has incorrect length");
729                 return RLM_MODULE_REJECT;
730         }
731
732         strlcpy(buff2, "%{mschap:NT-Hash %{User-Password}}", sizeof(buff2));
733         if (!radius_xlat(charbuf, sizeof(charbuf),buff2,request,NULL)){
734                 RDEBUG("mschap xlat failed");
735                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
736                         "rlm_pap: mschap xlat failed");
737                 return RLM_MODULE_REJECT;
738         }
739
740         if ((fr_hex2bin(charbuf, binbuf, 16) != vp->length) ||
741             (rad_digest_cmp(binbuf, vp->vp_octets, vp->length) != 0)) {
742                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
743                         "rlm_pap: NT password check failed");
744                 return RLM_MODULE_REJECT;
745         }
746
747         return RLM_MODULE_OK;
748 }
749
750
751 static int pap_auth_lm(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
752 {
753         uint8_t binbuf[128];
754         char charbuf[128];
755         char buff2[MAX_STRING_LEN + 50];
756
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(fmsg, sizeof(char[MAX_STRING_LEN]),
763                         "rlm_pap: Configured LM-Password has incorrect length");
764                 return RLM_MODULE_REJECT;
765         }
766
767         strlcpy(buff2, "%{mschap:LM-Hash %{User-Password}}", sizeof(buff2));
768         if (!radius_xlat(charbuf,sizeof(charbuf),buff2,request,NULL)){
769                 RDEBUG("mschap xlat failed");
770                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
771                         "rlm_pap: mschap xlat failed");
772                 return RLM_MODULE_REJECT;
773         }
774
775         if ((fr_hex2bin(charbuf, binbuf, 16) != vp->length) ||
776             (rad_digest_cmp(binbuf, vp->vp_octets, vp->length) != 0)) {
777                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
778                         "rlm_pap: LM password check failed");
779                 return RLM_MODULE_REJECT;
780         }
781
782         return RLM_MODULE_OK;
783 }
784
785 static int pap_auth_ns_mta_md5(REQUEST *request, VALUE_PAIR *vp, char *fmsg)
786 {
787         FR_MD5_CTX md5_context;
788         uint8_t binbuf[128];
789         uint8_t buff[MAX_STRING_LEN];
790         char buff2[MAX_STRING_LEN + 50];
791
792         RDEBUG("Using NT-MTA-MD5 password");
793
794         if (vp->length != 64) {
795                 RDEBUG("Configured NS-MTA-MD5-Password has incorrect length");
796                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
797                         "rlm_pap: Configured NS-MTA-MD5-Password has incorrect length");
798                 return RLM_MODULE_REJECT;
799         }
800
801         /*
802          *      Sanity check the value of NS-MTA-MD5-Password
803          */
804         if (fr_hex2bin(vp->vp_strvalue, binbuf, 32) != 16) {
805                 RDEBUG("Configured NS-MTA-MD5-Password has invalid value");
806                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
807                         "rlm_pap: Configured NS-MTA-MD5-Password has invalid value");
808                 return RLM_MODULE_REJECT;
809         }
810
811         /*
812          *      Ensure we don't have buffer overflows.
813          *
814          *      This really: sizeof(buff) - 2 - 2*32 - strlen(passwd)
815          */
816         if (strlen(request->password->vp_strvalue) >= (sizeof(buff) - 2 - 2 * 32)) {
817                 RDEBUG("Configured password is too long");
818                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
819                         "rlm_pap: password is too long");
820                 return RLM_MODULE_REJECT;
821         }
822
823         /*
824          *      Set up the algorithm.
825          */
826         {
827                 char *p = buff2;
828
829                 memcpy(p, &vp->vp_octets[32], 32);
830                 p += 32;
831                 *(p++) = 89;
832                 strcpy(p, request->password->vp_strvalue);
833                 p += strlen(p);
834                 *(p++) = 247;
835                 memcpy(p, &vp->vp_octets[32], 32);
836                 p += 32;
837
838                 fr_MD5Init(&md5_context);
839                 fr_MD5Update(&md5_context, (uint8_t *) buff2, p - buff2);
840                 fr_MD5Final(buff, &md5_context);
841         }
842
843         if (rad_digest_cmp(binbuf, buff, 16) != 0) {
844                 snprintf(fmsg, sizeof(char[MAX_STRING_LEN]),
845                         "rlm_pap: NS-MTA-MD5 password check failed");
846                 return RLM_MODULE_REJECT;
847         }
848
849         return RLM_MODULE_OK;
850 }
851
852 /*
853  *      The module name should be the only globally exported symbol.
854  *      That is, everything else should be 'static'.
855  *
856  *      If the module needs to temporarily modify it's instantiation
857  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
858  *      The server will then take care of ensuring that the module
859  *      is single-threaded.
860  */
861 module_t rlm_pap = {
862         RLM_MODULE_INIT,
863         "PAP",
864         RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,         /* type */
865         pap_instantiate,                /* instantiation */
866         pap_detach,                     /* detach */
867         {
868                 pap_authenticate,       /* authentication */
869                 pap_authorize,          /* authorization */
870                 NULL,                   /* preaccounting */
871                 NULL,                   /* accounting */
872                 NULL,                   /* checksimul */
873                 NULL,                   /* pre-proxy */
874                 NULL,                   /* post-proxy */
875                 NULL                    /* post-auth */
876         },
877 };