Merge tag 'release_3_0_12' into branch moonshot-fr-3.0.12-upgrade.
[freeradius.git] / src / modules / rlm_pap / rlm_pap.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or (at
5  *   your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16
17 /**
18  * $Id$
19  * @file rlm_pap.c
20  * @brief Hashes plaintext passwords to compare against a prehashed reference.
21  *
22  * @copyright 2001-2012  The FreeRADIUS server project.
23  * @copyright 2012       Matthew Newton <matthew@newtoncomputing.co.uk>
24  * @copyright 2001       Kostas Kalevras <kkalev@noc.ntua.gr>
25  */
26 RCSID("$Id$")
27 USES_APPLE_DEPRECATED_API
28
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/modules.h>
31 #include <freeradius-devel/base64.h>
32 #include <freeradius-devel/rad_assert.h>
33
34 #include <ctype.h>
35
36 #include "../../include/md5.h"
37 #include "../../include/sha1.h"
38
39 #ifdef HAVE_OPENSSL_EVP_H
40 #  include <openssl/evp.h>
41 #endif
42
43 /*
44  *      Define a structure for our module configuration.
45  *
46  *      These variables do not need to be in a structure, but it's
47  *      a lot cleaner to do so, and a pointer to the structure can
48  *      be used as the instance handle.
49  */
50 typedef struct rlm_pap_t {
51         char const      *name;  /* CONF_SECTION->name, not strdup'd */
52         int             auth_type;
53         bool            normify;
54 } rlm_pap_t;
55
56 /*
57  *      A mapping of configuration file names to internal variables.
58  *
59  *      Note that the string is dynamically allocated, so it MUST
60  *      be freed.  When the configuration file parse re-reads the string,
61  *      it free's the old one, and strdup's the new one, placing the pointer
62  *      to the strdup'd string into 'config.string'.  This gets around
63  *      buffer over-flows.
64  */
65 static const CONF_PARSER module_config[] = {
66         { "normalise", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_pap_t, normify), "yes" },
67         CONF_PARSER_TERMINATOR
68 };
69
70
71 /*
72  *      For auto-header discovery.
73  *
74  *      @note Header comparison is case insensitive.
75  */
76 static const FR_NAME_NUMBER header_names[] = {
77         { "{clear}",            PW_CLEARTEXT_PASSWORD },
78         { "{cleartext}",        PW_CLEARTEXT_PASSWORD },
79         { "{md5}",              PW_MD5_PASSWORD },
80         { "{base64_md5}",       PW_MD5_PASSWORD },
81         { "{smd5}",             PW_SMD5_PASSWORD },
82         { "{crypt}",            PW_CRYPT_PASSWORD },
83 #ifdef HAVE_OPENSSL_EVP_H
84         /*
85          *      It'd make more sense for the headers to be
86          *      ssha2-* with SHA3 coming soon but we're at
87          *      the mercy of directory implementors.
88          */
89         { "{sha2}",             PW_SHA2_PASSWORD },
90         { "{sha224}",           PW_SHA2_PASSWORD },
91         { "{sha256}",           PW_SHA2_PASSWORD },
92         { "{sha384}",           PW_SHA2_PASSWORD },
93         { "{sha512}",           PW_SHA2_PASSWORD },
94         { "{ssha224}",          PW_SSHA2_224_PASSWORD },
95         { "{ssha256}",          PW_SSHA2_256_PASSWORD },
96         { "{ssha384}",          PW_SSHA2_384_PASSWORD },
97         { "{ssha512}",          PW_SSHA2_512_PASSWORD },
98 #endif
99         { "{sha}",              PW_SHA_PASSWORD },
100         { "{ssha}",             PW_SSHA_PASSWORD },
101         { "{md4}",              PW_NT_PASSWORD },
102         { "{nt}",               PW_NT_PASSWORD },
103         { "{nthash}",           PW_NT_PASSWORD },
104         { "{x-nthash}",         PW_NT_PASSWORD },
105         { "{ns-mta-md5}",       PW_NS_MTA_MD5_PASSWORD },
106         { "{x- orcllmv}",       PW_LM_PASSWORD },
107         { "{X- orclntv}",       PW_NT_PASSWORD },
108         { NULL, 0 }
109 };
110
111 static int mod_instantiate(CONF_SECTION *conf, void *instance)
112 {
113         rlm_pap_t *inst = instance;
114         DICT_VALUE *dval;
115
116         inst->name = cf_section_name2(conf);
117         if (!inst->name) {
118                 inst->name = cf_section_name1(conf);
119         }
120
121         dval = dict_valbyname(PW_AUTH_TYPE, 0, inst->name);
122         if (dval) {
123                 inst->auth_type = dval->value;
124         } else {
125                 inst->auth_type = 0;
126         }
127
128         return 0;
129 }
130
131 /** Hex or base64 or bin auto-discovery
132  *
133  * Here we try and autodiscover what encoding was used for the password/hash, and
134  * convert it back to binary or plaintext.
135  *
136  * @note Earlier versions used a 0x prefix as a hard indicator that the string was
137  *       hex encoded, and would fail if the 0x was present but the string didn't
138  *       consist of hexits. The base64 char set is a superset of hex, and it was
139  *       observed in the wild, that occasionally base64 encoded data really could
140  *       start with 0x. That's why min_len (and decodability) are used as the
141  *       only heuristics now.
142  *
143  * @param[in] request Current request.
144  * @param[in,out] vp to normify.
145  * @param[in] min_len we expect the decoded version to be.
146  */
147 static void normify(REQUEST *request, VALUE_PAIR *vp, size_t min_len)
148 {
149         uint8_t buffer[256];
150
151         if (min_len >= sizeof(buffer)) return; /* paranoia */
152
153         rad_assert((vp->da->type == PW_TYPE_OCTETS) || (vp->da->type == PW_TYPE_STRING));
154
155         /*
156          *      Hex encoding. Length is even, and it's greater than
157          *      twice the minimum length.
158          */
159         if (!(vp->vp_length & 0x01) && vp->vp_length >= (2 * min_len)) {
160                 size_t decoded;
161
162                 decoded = fr_hex2bin(buffer, sizeof(buffer), vp->vp_strvalue, vp->vp_length);
163                 if (decoded == (vp->vp_length >> 1)) {
164                         RDEBUG2("Normalizing %s from hex encoding, %zu bytes -> %zu bytes",
165                                 vp->da->name, vp->vp_length, decoded);
166                         fr_pair_value_memcpy(vp, buffer, decoded);
167                         return;
168                 }
169         }
170
171         /*
172          *      Base 64 encoding.  It's at least 4/3 the original size,
173          *      and we want to avoid division...
174          */
175         if ((vp->vp_length * 3) >= ((min_len * 4))) {
176                 ssize_t decoded;
177                 decoded = fr_base64_decode(buffer, sizeof(buffer), vp->vp_strvalue, vp->vp_length);
178                 if (decoded < 0) return;
179                 if (decoded >= (ssize_t) min_len) {
180                         RDEBUG2("Normalizing %s from base64 encoding, %zu bytes -> %zu bytes",
181                                 vp->da->name, vp->vp_length, decoded);
182                         fr_pair_value_memcpy(vp, buffer, decoded);
183                         return;
184                 }
185         }
186
187         /*
188          *      Else unknown encoding, or already binary.  Leave it.
189          */
190 }
191
192 /** Convert a Password-With-Header attribute to the correct type
193  *
194  * Attribute may be base64 encoded, in which case it will be decoded
195  * first, then evaluated.
196  *
197  * @note The buffer for octets types\ attributes is extended by one byte
198  *      and '\0' terminated, to allow it to be used as a char buff.
199  *
200  * @param request Current request.
201  * @param vp Password-With-Header attribute to convert.
202  * @return a new VALUE_PAIR on success, NULL on error.
203  */
204 static VALUE_PAIR *normify_with_header(REQUEST *request, VALUE_PAIR *vp)
205 {
206         int             attr;
207         char const      *p, *q;
208         size_t          len;
209
210         uint8_t         digest[257];    /* +1 for \0 */
211         ssize_t         decoded;
212
213         char            buffer[256];
214
215         VALUE_PAIR      *new;
216
217         VERIFY_VP(vp);
218
219         /*
220          *      Ensure this is only ever called with a
221          *      string type attribute.
222          */
223         rad_assert(vp->da->type == PW_TYPE_STRING);
224
225 redo:
226         p = vp->vp_strvalue;
227         len = vp->vp_length;
228
229         /*
230          *      Has a header {...} prefix
231          */
232         q = strchr(p, '}');
233         if (q) {
234                 size_t hlen;
235
236                 hlen = (q + 1) - p;
237                 if (hlen >= sizeof(buffer)) {
238                         REDEBUG("Password header too long.  Got %zu bytes must be less than %zu bytes",
239                                 hlen, sizeof(buffer));
240                         return NULL;
241                 }
242
243                 memcpy(buffer, p, hlen);
244                 buffer[hlen] = '\0';
245
246                 attr = fr_str2int(header_names, buffer, 0);
247                 if (!attr) {
248                         if (RDEBUG_ENABLED3) {
249                                 RDEBUG3("Unknown header {%s} in Password-With-Header = \"%s\", re-writing to "
250                                         "Cleartext-Password", buffer, vp->vp_strvalue);
251                         } else {
252                                 RDEBUG("Unknown header {%s} in Password-With-Header, re-writing to "
253                                        "Cleartext-Password", buffer);
254                         }
255                         goto unknown_header;
256                 }
257
258                 /*
259                  *      The data after the '}' may be binary, so we copy it via
260                  *      memcpy.  BUT it might be a string (or used as one), so
261                  *      we ensure that there's a trailing zero, too.
262                  */
263                 new = fr_pair_afrom_num(request, attr, 0);
264                 if (new->da->type == PW_TYPE_OCTETS) {
265                         fr_pair_value_memcpy(new, (uint8_t const *) q + 1, (len - hlen) + 1);
266                         new->vp_length = (len - hlen);  /* lie about the length */
267                 } else {
268                         fr_pair_value_strcpy(new, q + 1);
269                 }
270
271                 if (RDEBUG_ENABLED3) {
272                         char *old_value, *new_value;
273
274                         old_value = vp_aprints_value(request, vp, '\'');
275                         new_value = vp_aprints_value(request, new, '\'');
276                         RDEBUG3("Converted: &control:%s = '%s' -> &control:%s = '%s'",
277                                 vp->da->name, old_value, new->da->name, new_value);
278                         talloc_free(old_value);
279                         talloc_free(new_value);
280                 } else {
281                         RDEBUG2("Converted: &control:%s -> &control:%s", vp->da->name, new->da->name);
282                 }
283
284                 return new;
285         }
286
287         /*
288          *      Doesn't have a header {...} prefix
289          *
290          *      See if it's base64, if it is, decode it and check again!
291          */
292         decoded = fr_base64_decode(digest, sizeof(digest) - 1, vp->vp_strvalue, len);
293         if ((decoded > 0) && (digest[0] == '{') && (memchr(digest, '}', decoded) != NULL)) {
294                 RDEBUG2("Normalizing %s from base64 encoding, %zu bytes -> %zu bytes",
295                         vp->da->name, vp->vp_length, decoded);
296                 /*
297                  *      Password-With-Header is a string attribute.
298                  *      Even though we're handling binary data, the buffer
299                  *      must be \0 terminated.
300                  */
301                 digest[decoded] = '\0';
302                 fr_pair_value_memcpy(vp, digest, decoded + 1);
303                 vp->vp_length = decoded;                /* lie about the length */
304
305                 goto redo;
306         }
307
308         if (RDEBUG_ENABLED3) {
309                 RDEBUG3("No {...} in Password-With-Header = \"%s\", re-writing to "
310                         "Cleartext-Password", vp->vp_strvalue);
311         } else {
312                 RDEBUG("No {...} in Password-With-Header, re-writing to Cleartext-Password");
313         }
314
315 unknown_header:
316         new = fr_pair_afrom_num(request, PW_CLEARTEXT_PASSWORD, 0);
317         fr_pair_value_strcpy(new, vp->vp_strvalue);
318
319         return new;
320 }
321
322 /*
323  *      Authorize the user for PAP authentication.
324  *
325  *      This isn't strictly necessary, but it does make the
326  *      server simpler to configure.
327  */
328 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
329 {
330         rlm_pap_t *inst = instance;
331         bool auth_type = false;
332         bool found_pw = false;
333         VALUE_PAIR *vp;
334         vp_cursor_t cursor;
335
336         for (vp = fr_cursor_init(&cursor, &request->config);
337              vp;
338              vp = fr_cursor_next(&cursor)) {
339                 VERIFY_VP(vp);
340         next:
341                 switch (vp->da->attr) {
342                 case PW_USER_PASSWORD: /* deprecated */
343                         RWDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
344                         RWDEBUG("!!! Ignoring control:User-Password.  Update your        !!!");
345                         RWDEBUG("!!! configuration so that the \"known good\" clear text !!!");
346                         RWDEBUG("!!! password is in Cleartext-Password and NOT in        !!!");
347                         RWDEBUG("!!! User-Password.                                      !!!");
348                         RWDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
349                         break;
350
351                 case PW_PASSWORD_WITH_HEADER:   /* preferred */
352                 {
353                         VALUE_PAIR *new;
354
355                         /*
356                          *      Password already exists: use that instead of this one.
357                          */
358                         if (fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
359                                 RWDEBUG("Config already contains a \"known good\" password "
360                                         "(&control:Cleartext-Password).  Ignoring &config:Password-With-Header");
361                                 break;
362                         }
363
364                         new = normify_with_header(request, vp);
365                         if (new) fr_cursor_insert(&cursor, new); /* inserts at the end of the list */
366
367                         RDEBUG2("Removing &control:Password-With-Header");
368                         vp = fr_cursor_remove(&cursor); /* advances the cursor for us */
369                         talloc_free(vp);
370
371                         found_pw = true;
372
373                         vp = fr_cursor_current(&cursor);
374                         if (vp) goto next;
375                 }
376                         break;
377
378                 case PW_CLEARTEXT_PASSWORD:
379                 case PW_CRYPT_PASSWORD:
380                 case PW_NS_MTA_MD5_PASSWORD:
381                         found_pw = true;
382                         break;  /* don't touch these */
383
384                 case PW_MD5_PASSWORD:
385                 case PW_SMD5_PASSWORD:
386                 case PW_NT_PASSWORD:
387                 case PW_LM_PASSWORD:
388                         if (inst->normify) {
389                                 normify(request, vp, 16); /* ensure it's in the right format */
390                         }
391                         found_pw = true;
392                         break;
393
394 #ifdef HAVE_OPENSSL_EVP_H
395                 case PW_SHA2_PASSWORD:
396                         if (inst->normify) {
397                                 normify(request, vp, 28); /* ensure it's in the right format */
398                         }
399                         found_pw = true;
400                         break;
401
402                 case PW_SSHA2_224_PASSWORD:
403                         if (inst->normify) {
404                                 normify(request, vp, 28); /* ensure it's in the right format */
405                         }
406                         found_pw = true;
407                         break;
408
409                 case PW_SSHA2_256_PASSWORD:
410                         if (inst->normify) {
411                                 normify(request, vp, 32); /* ensure it's in the right format */
412                         }
413                         found_pw = true;
414                         break;
415
416                 case PW_SSHA2_384_PASSWORD:
417                         if (inst->normify) {
418                                 normify(request, vp, 48); /* ensure it's in the right format */
419                         }
420                         found_pw = true;
421                         break;
422
423                 case PW_SSHA2_512_PASSWORD:
424                         if (inst->normify) {
425                                 normify(request, vp, 64); /* ensure it's in the right format */
426                         }
427                         found_pw = true;
428                         break;
429 #endif
430
431                 case PW_SHA_PASSWORD:
432                 case PW_SSHA_PASSWORD:
433                         if (inst->normify) {
434                                 normify(request, vp, 20); /* ensure it's in the right format */
435                         }
436                         found_pw = true;
437                         break;
438
439                         /*
440                          *      If it's proxied somewhere, don't complain
441                          *      about not having passwords or Auth-Type.
442                          */
443                 case PW_PROXY_TO_REALM:
444                 {
445                         REALM *realm = realm_find(vp->vp_strvalue);
446                         if (realm && realm->auth_pool) {
447                                 return RLM_MODULE_NOOP;
448                         }
449                         break;
450                 }
451
452                 case PW_AUTH_TYPE:
453                         auth_type = true;
454
455                         /*
456                          *      Auth-Type := Accept
457                          *      Auth-Type := Reject
458                          */
459                         if ((vp->vp_integer == 254) ||
460                             (vp->vp_integer == 4)) {
461                             found_pw = true;
462                         }
463                         break;
464
465                 default:
466                         break;  /* ignore it */
467
468                 }
469         }
470
471         /*
472          *      Print helpful warnings if there was no password.
473          */
474         if (!found_pw) {
475                 /*
476                  *      Likely going to be proxied.  Avoid printing
477                  *      warning message.
478                  */
479                 if (fr_pair_find_by_num(request->config, PW_REALM, 0, TAG_ANY) ||
480                     (fr_pair_find_by_num(request->config, PW_PROXY_TO_REALM, 0, TAG_ANY))) {
481                         return RLM_MODULE_NOOP;
482                 }
483
484                 /*
485                  *      The TLS types don't need passwords.
486                  */
487                 vp = fr_pair_find_by_num(request->packet->vps, PW_EAP_TYPE, 0, TAG_ANY);
488                 if (vp &&
489                     ((vp->vp_integer == 13) || /* EAP-TLS */
490                      (vp->vp_integer == 21) || /* EAP-TTLS */
491                      (vp->vp_integer == 25))) { /* PEAP */
492                         return RLM_MODULE_NOOP;
493                 }
494
495                 RWDEBUG("No \"known good\" password found for the user.  Not setting Auth-Type");
496                 RWDEBUG("Authentication will fail unless a \"known good\" password is available");
497                 return RLM_MODULE_NOOP;
498         }
499
500         /*
501          *      Don't touch existing Auth-Types.
502          */
503         if (auth_type) {
504                 if (auth_type != inst->auth_type) RWDEBUG2("Auth-Type already set.  Not setting to PAP");
505                 return RLM_MODULE_NOOP;
506         }
507
508         /*
509          *      Can't do PAP if there's no password.
510          */
511         if (!request->password ||
512             (request->password->da->attr != PW_USER_PASSWORD)) {
513                 RDEBUG2("No User-Password attribute in the request.  Cannot do PAP");
514                 return RLM_MODULE_NOOP;
515         }
516
517         if (inst->auth_type) {
518                 vp = radius_pair_create(request, &request->config,
519                                        PW_AUTH_TYPE, 0);
520                 vp->vp_integer = inst->auth_type;
521         }
522
523         return RLM_MODULE_UPDATED;
524 }
525
526 /*
527  *      PAP authentication functions
528  */
529
530 static rlm_rcode_t CC_HINT(nonnull) pap_auth_clear(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
531 {
532         if (RDEBUG_ENABLED3) {
533                 RDEBUG3("Comparing with \"known good\" Cleartext-Password \"%s\" (%zd)", vp->vp_strvalue, vp->vp_length);
534         } else {
535                 RDEBUG("Comparing with \"known good\" Cleartext-Password");
536         }
537
538         if ((vp->vp_length != request->password->vp_length) ||
539             (rad_digest_cmp(vp->vp_octets,
540                             request->password->vp_octets,
541                             vp->vp_length) != 0)) {
542                 REDEBUG("Cleartext password \"%s\" does not match \"known good\" password",
543                         request->password->vp_strvalue);
544                 return RLM_MODULE_REJECT;
545         }
546         return RLM_MODULE_OK;
547 }
548
549 static rlm_rcode_t CC_HINT(nonnull) pap_auth_crypt(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
550 {
551         if (RDEBUG_ENABLED3) {
552                 RDEBUG3("Comparing with \"known good\" Crypt-Password \"%s\"", vp->vp_strvalue);
553         } else {
554                 RDEBUG("Comparing with \"known-good\" Crypt-password");
555         }
556
557         if (fr_crypt_check(request->password->vp_strvalue,
558                            vp->vp_strvalue) != 0) {
559                 REDEBUG("Crypt digest does not match \"known good\" digest");
560                 return RLM_MODULE_REJECT;
561         }
562         return RLM_MODULE_OK;
563 }
564
565 static rlm_rcode_t CC_HINT(nonnull) pap_auth_md5(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
566 {
567         FR_MD5_CTX md5_context;
568         uint8_t digest[128];
569
570         RDEBUG("Comparing with \"known-good\" MD5-Password");
571
572         if (inst->normify) {
573                 normify(request, vp, 16);
574         }
575         if (vp->vp_length != 16) {
576                 REDEBUG("\"known-good\" MD5 password has incorrect length");
577                 return RLM_MODULE_INVALID;
578         }
579
580         fr_md5_init(&md5_context);
581         fr_md5_update(&md5_context, request->password->vp_octets,
582                      request->password->vp_length);
583         fr_md5_final(digest, &md5_context);
584
585         if (rad_digest_cmp(digest, vp->vp_octets, vp->vp_length) != 0) {
586                 REDEBUG("MD5 digest does not match \"known good\" digest");
587                 return RLM_MODULE_REJECT;
588         }
589
590         return RLM_MODULE_OK;
591 }
592
593
594 static rlm_rcode_t CC_HINT(nonnull) pap_auth_smd5(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
595 {
596         FR_MD5_CTX md5_context;
597         uint8_t digest[128];
598
599         RDEBUG("Comparing with \"known-good\" SMD5-Password");
600
601         if (inst->normify) {
602                 normify(request, vp, 16);
603         }
604         if (vp->vp_length <= 16) {
605                 REDEBUG("\"known-good\" SMD5-Password has incorrect length");
606                 return RLM_MODULE_INVALID;
607         }
608
609         fr_md5_init(&md5_context);
610         fr_md5_update(&md5_context, request->password->vp_octets,
611                      request->password->vp_length);
612         fr_md5_update(&md5_context, &vp->vp_octets[16], vp->vp_length - 16);
613         fr_md5_final(digest, &md5_context);
614
615         /*
616          *      Compare only the MD5 hash results, not the salt.
617          */
618         if (rad_digest_cmp(digest, vp->vp_octets, 16) != 0) {
619                 REDEBUG("SMD5 digest does not match \"known good\" digest");
620                 return RLM_MODULE_REJECT;
621         }
622
623         return RLM_MODULE_OK;
624 }
625
626 static rlm_rcode_t CC_HINT(nonnull) pap_auth_sha(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
627 {
628         fr_sha1_ctx sha1_context;
629         uint8_t digest[128];
630
631         RDEBUG("Comparing with \"known-good\" SHA-Password");
632
633         if (inst->normify) {
634                 normify(request, vp, 20);
635         }
636         if (vp->vp_length != 20) {
637                 REDEBUG("\"known-good\" SHA1-password has incorrect length");
638                 return RLM_MODULE_INVALID;
639         }
640
641         fr_sha1_init(&sha1_context);
642         fr_sha1_update(&sha1_context, request->password->vp_octets,
643                       request->password->vp_length);
644         fr_sha1_final(digest,&sha1_context);
645
646         if (rad_digest_cmp(digest, vp->vp_octets, vp->vp_length) != 0) {
647                 REDEBUG("SHA1 digest does not match \"known good\" digest");
648                 return RLM_MODULE_REJECT;
649         }
650
651         return RLM_MODULE_OK;
652 }
653
654 static rlm_rcode_t CC_HINT(nonnull) pap_auth_ssha(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
655 {
656         fr_sha1_ctx sha1_context;
657         uint8_t digest[128];
658
659         RDEBUG("Comparing with \"known-good\" SSHA-Password");
660
661         if (inst->normify) {
662                 normify(request, vp, 20);
663         }
664         if (vp->vp_length <= 20) {
665                 REDEBUG("\"known-good\" SSHA-Password has incorrect length");
666                 return RLM_MODULE_INVALID;
667         }
668
669         fr_sha1_init(&sha1_context);
670         fr_sha1_update(&sha1_context, request->password->vp_octets, request->password->vp_length);
671
672         fr_sha1_update(&sha1_context, &vp->vp_octets[20], vp->vp_length - 20);
673         fr_sha1_final(digest, &sha1_context);
674
675         if (rad_digest_cmp(digest, vp->vp_octets, 20) != 0) {
676                 REDEBUG("SSHA digest does not match \"known good\" digest");
677                 return RLM_MODULE_REJECT;
678         }
679
680         return RLM_MODULE_OK;
681 }
682
683 #ifdef HAVE_OPENSSL_EVP_H
684 static rlm_rcode_t CC_HINT(nonnull) pap_auth_sha2(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
685 {
686         EVP_MD_CTX *ctx;
687         EVP_MD const *md;
688         char const *name;
689         uint8_t digest[EVP_MAX_MD_SIZE];
690         unsigned int digest_len;
691
692         RDEBUG("Comparing with \"known-good\" SHA2-Password");
693
694         if (inst->normify) normify(request, vp, 28);
695
696         /*
697          *      All the SHA-2 algorithms produce digests of different lengths,
698          *      so it's trivial to determine which EVP_MD to use.
699          */
700         switch (vp->vp_length) {
701         /* SHA-224 */
702         case 28:
703                 name = "SHA2-224";
704                 md = EVP_sha224();
705                 break;
706
707         /* SHA-256 */
708         case 32:
709                 name = "SHA2-256";
710                 md = EVP_sha256();
711                 break;
712
713         /* SHA-384 */
714         case 48:
715                 name = "SHA2-384";
716                 md = EVP_sha384();
717                 break;
718
719         /* SHA-512 */
720         case 64:
721                 name = "SHA2-512";
722                 md = EVP_sha512();
723                 break;
724
725         default:
726                 REDEBUG("\"known good\" digest length (%zu) does not match output length of any SHA-2 digests",
727                         vp->vp_length);
728                 return RLM_MODULE_INVALID;
729         }
730
731         ctx = EVP_MD_CTX_create();
732         EVP_DigestInit_ex(ctx, md, NULL);
733         EVP_DigestUpdate(ctx, request->password->vp_octets, request->password->vp_length);
734         EVP_DigestFinal_ex(ctx, digest, &digest_len);
735         EVP_MD_CTX_destroy(ctx);
736
737         rad_assert((size_t) digest_len == vp->vp_length);       /* This would be an OpenSSL bug... */
738
739         if (rad_digest_cmp(digest, vp->vp_octets, vp->vp_length) != 0) {
740                 REDEBUG("%s digest does not match \"known good\" digest", name);
741                 return RLM_MODULE_REJECT;
742         }
743
744         return RLM_MODULE_OK;
745 }
746
747 static rlm_rcode_t CC_HINT(nonnull) pap_auth_ssha2(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
748 {
749         EVP_MD_CTX *ctx;
750         EVP_MD const *md = NULL;
751         char const *name = NULL;
752         uint8_t digest[EVP_MAX_MD_SIZE];
753         unsigned int digest_len, min_len = 0;
754
755         switch (vp->da->attr) {
756         case PW_SSHA2_224_PASSWORD:
757                 name = "SSHA2-224";
758                 md = EVP_sha224();
759                 min_len = 28;
760                 break;
761
762         case PW_SSHA2_256_PASSWORD:
763                 name = "SSHA2-256";
764                 md = EVP_sha256();
765                 min_len = 32;
766                 break;
767
768         case PW_SSHA2_384_PASSWORD:
769                 name = "SSHA2-384";
770                 md = EVP_sha384();
771                 min_len = 48;
772                 break;
773
774         case PW_SSHA2_512_PASSWORD:
775                 name = "SSHA2-512";
776                 min_len = 64;
777                 md = EVP_sha512();
778                 break;
779
780         default:
781                 rad_assert(0);
782         }
783
784         RDEBUG("Comparing with \"known-good\" %s-Password", name);
785
786         /*
787          *      Unlike plain SHA2 we already know what length
788          *      to expect, so can be more specific with the
789          *      minimum digest length.
790          */
791         if (inst->normify) normify(request, vp, min_len + 1);
792
793         if (vp->vp_length <= min_len) {
794                 REDEBUG("\"known-good\" %s-Password has incorrect length, got %zu bytes, need at least %u bytes",
795                         name, vp->vp_length, min_len + 1);
796                 return RLM_MODULE_INVALID;
797         }
798
799         ctx = EVP_MD_CTX_create();
800         EVP_DigestInit_ex(ctx, md, NULL);
801         EVP_DigestUpdate(ctx, request->password->vp_octets, request->password->vp_length);
802         EVP_DigestUpdate(ctx, &vp->vp_octets[min_len], vp->vp_length - min_len);
803         EVP_DigestFinal_ex(ctx, digest, &digest_len);
804         EVP_MD_CTX_destroy(ctx);
805
806         rad_assert((size_t) digest_len == min_len);     /* This would be an OpenSSL bug... */
807
808         /*
809          *      Only compare digest_len bytes, the rest is salt.
810          */
811         if (rad_digest_cmp(digest, vp->vp_octets, (size_t)digest_len) != 0) {
812                 REDEBUG("%s digest does not match \"known good\" digest", name);
813                 return RLM_MODULE_REJECT;
814         }
815
816         return RLM_MODULE_OK;
817 }
818 #endif
819
820 static rlm_rcode_t CC_HINT(nonnull) pap_auth_nt(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
821 {
822         ssize_t len;
823         uint8_t digest[16];
824         uint8_t ucs2_password[512];
825
826         RDEBUG("Comparing with \"known-good\" NT-Password");
827
828         rad_assert(request->password != NULL);
829         rad_assert(request->password->da->attr == PW_USER_PASSWORD);
830
831         if (inst->normify) {
832                 normify(request, vp, 16);
833         }
834
835         if (vp->vp_length != 16) {
836                 REDEBUG("\"known good\" NT-Password has incorrect length");
837                 return RLM_MODULE_INVALID;
838         }
839
840         len = fr_utf8_to_ucs2(ucs2_password, sizeof(ucs2_password), request->password->vp_strvalue, request->password->vp_length);
841         if (len < 0) {
842                 REDEBUG("User-Password is not in UCS2 format");
843                 return RLM_MODULE_INVALID;
844         }
845
846         fr_md4_calc(digest, (uint8_t *) ucs2_password, len);
847
848         if (rad_digest_cmp(digest, vp->vp_octets, vp->vp_length) != 0) {
849                 REDEBUG("NT digest does not match \"known good\" digest");
850                 return RLM_MODULE_REJECT;
851         }
852
853         return RLM_MODULE_OK;
854 }
855
856
857 static rlm_rcode_t CC_HINT(nonnull) pap_auth_lm(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
858 {
859         uint8_t digest[16];
860         char charbuf[32 + 1];
861         ssize_t len;
862
863         RDEBUG("Comparing with \"known-good\" LM-Password");
864
865         if (inst->normify) {
866                 normify(request, vp, 16);
867         }
868         if (vp->vp_length != 16) {
869                 REDEBUG("\"known good\" LM-Password has incorrect length");
870                 return RLM_MODULE_INVALID;
871         }
872
873         len = radius_xlat(charbuf, sizeof(charbuf), request, "%{mschap:LM-Hash %{User-Password}}", NULL, NULL);
874         if (len < 0){
875                 return RLM_MODULE_FAIL;
876         }
877
878         if ((fr_hex2bin(digest, sizeof(digest), charbuf, len) != vp->vp_length) ||
879             (rad_digest_cmp(digest, vp->vp_octets, vp->vp_length) != 0)) {
880                 REDEBUG("LM digest does not match \"known good\" digest");
881                 return RLM_MODULE_REJECT;
882         }
883
884         return RLM_MODULE_OK;
885 }
886
887 static rlm_rcode_t CC_HINT(nonnull) pap_auth_ns_mta_md5(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
888 {
889         FR_MD5_CTX md5_context;
890         uint8_t digest[128];
891         uint8_t buff[MAX_STRING_LEN];
892         char buff2[MAX_STRING_LEN + 50];
893
894         RDEBUG("Using NT-MTA-MD5-Password");
895
896         if (vp->vp_length != 64) {
897                 REDEBUG("\"known good\" NS-MTA-MD5-Password has incorrect length");
898                 return RLM_MODULE_INVALID;
899         }
900
901         /*
902          *      Sanity check the value of NS-MTA-MD5-Password
903          */
904         if (fr_hex2bin(digest, sizeof(digest), vp->vp_strvalue, vp->vp_length) != 16) {
905                 REDEBUG("\"known good\" NS-MTA-MD5-Password has invalid value");
906                 return RLM_MODULE_INVALID;
907         }
908
909         /*
910          *      Ensure we don't have buffer overflows.
911          *
912          *      This really: sizeof(buff) - 2 - 2*32 - strlen(passwd)
913          */
914         if (request->password->vp_length >= (sizeof(buff) - 2 - 2 * 32)) {
915                 REDEBUG("\"known good\" NS-MTA-MD5-Password is too long");
916                 return RLM_MODULE_INVALID;
917         }
918
919         /*
920          *      Set up the algorithm.
921          */
922         {
923                 char *p = buff2;
924
925                 memcpy(p, &vp->vp_octets[32], 32);
926                 p += 32;
927                 *(p++) = 89;
928                 strcpy(p, request->password->vp_strvalue);
929                 p += strlen(p);
930                 *(p++) = 247;
931                 memcpy(p, &vp->vp_octets[32], 32);
932                 p += 32;
933
934                 fr_md5_init(&md5_context);
935                 fr_md5_update(&md5_context, (uint8_t *) buff2, p - buff2);
936                 fr_md5_final(buff, &md5_context);
937         }
938
939         if (rad_digest_cmp(digest, buff, 16) != 0) {
940                 REDEBUG("NS-MTA-MD5 digest does not match \"known good\" digest");
941                 return RLM_MODULE_REJECT;
942         }
943
944         return RLM_MODULE_OK;
945 }
946
947
948 /*
949  *      Authenticate the user via one of any well-known password.
950  */
951 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
952 {
953         rlm_pap_t *inst = instance;
954         VALUE_PAIR *vp;
955         rlm_rcode_t rc = RLM_MODULE_INVALID;
956         vp_cursor_t cursor;
957         rlm_rcode_t (*auth_func)(rlm_pap_t *, REQUEST *, VALUE_PAIR *) = NULL;
958
959         if (!request->password ||
960             (request->password->da->vendor != 0) ||
961             (request->password->da->attr != PW_USER_PASSWORD)) {
962                 REDEBUG("You set 'Auth-Type = PAP' for a request that does not contain a User-Password attribute!");
963                 return RLM_MODULE_INVALID;
964         }
965
966         /*
967          *      The user MUST supply a non-zero-length password.
968          */
969         if (request->password->vp_length == 0) {
970                 REDEBUG("Password must not be empty");
971                 return RLM_MODULE_INVALID;
972         }
973
974         if (RDEBUG_ENABLED3) {
975                 RDEBUG3("Login attempt with password \"%s\" (%zd)", request->password->vp_strvalue, request->password->vp_length);
976         } else {
977                 RDEBUG("Login attempt with password");
978         }
979
980         /*
981          *      Auto-detect passwords, by attribute in the
982          *      config items, to find out which authentication
983          *      function to call.
984          */
985         for (vp = fr_cursor_init(&cursor, &request->config);
986              vp;
987              vp = fr_cursor_next(&cursor)) {
988                 if (!vp->da->vendor) switch (vp->da->attr) {
989                 case PW_CLEARTEXT_PASSWORD:
990                         auth_func = &pap_auth_clear;
991                         break;
992
993                 case PW_CRYPT_PASSWORD:
994                         auth_func = &pap_auth_crypt;
995                         break;
996
997                 case PW_MD5_PASSWORD:
998                         auth_func = &pap_auth_md5;
999                         break;
1000
1001                 case PW_SMD5_PASSWORD:
1002                         auth_func = &pap_auth_smd5;
1003                         break;
1004
1005 #ifdef HAVE_OPENSSL_EVP_H
1006                 case PW_SHA2_PASSWORD:
1007                         auth_func = &pap_auth_sha2;
1008                         break;
1009
1010                 case PW_SSHA2_224_PASSWORD:
1011                 case PW_SSHA2_256_PASSWORD:
1012                 case PW_SSHA2_384_PASSWORD:
1013                 case PW_SSHA2_512_PASSWORD:
1014                         auth_func = &pap_auth_ssha2;
1015                         break;
1016 #endif
1017
1018                 case PW_SHA_PASSWORD:
1019                         auth_func = &pap_auth_sha;
1020                         break;
1021
1022                 case PW_SSHA_PASSWORD:
1023                         auth_func = &pap_auth_ssha;
1024                         break;
1025
1026                 case PW_NT_PASSWORD:
1027                         auth_func = &pap_auth_nt;
1028                         break;
1029
1030                 case PW_LM_PASSWORD:
1031                         auth_func = &pap_auth_lm;
1032                         break;
1033
1034                 case PW_NS_MTA_MD5_PASSWORD:
1035                         auth_func = &pap_auth_ns_mta_md5;
1036                         break;
1037
1038                 default:
1039                         break;
1040                 }
1041
1042                 if (auth_func != NULL) break;
1043         }
1044
1045         /*
1046          *      No attribute was found that looked like a password to match.
1047          */
1048         if (!auth_func) {
1049                 RDEBUG("No password configured for the user.  Cannot do authentication");
1050                 return RLM_MODULE_FAIL;
1051         }
1052
1053         /*
1054          *      Authenticate, and return.
1055          */
1056         rc = auth_func(inst, request, vp);
1057
1058         if (rc == RLM_MODULE_REJECT) {
1059                 RDEBUG("Passwords don't match");
1060         }
1061
1062         if (rc == RLM_MODULE_OK) {
1063                 RDEBUG("User authenticated successfully");
1064         }
1065
1066         return rc;
1067 }
1068
1069
1070 /*
1071  *      The module name should be the only globally exported symbol.
1072  *      That is, everything else should be 'static'.
1073  *
1074  *      If the module needs to temporarily modify it's instantiation
1075  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
1076  *      The server will then take care of ensuring that the module
1077  *      is single-threaded.
1078  */
1079 extern module_t rlm_pap;
1080 module_t rlm_pap = {
1081         .magic          = RLM_MODULE_INIT,
1082         .name           = "pap",
1083         .type           = RLM_TYPE_HUP_SAFE,
1084         .inst_size      = sizeof(rlm_pap_t),
1085         .config         = module_config,
1086         .instantiate    = mod_instantiate,
1087         .methods = {
1088                 [MOD_AUTHENTICATE]      = mod_authenticate,
1089                 [MOD_AUTHORIZE]         = mod_authorize
1090         },
1091 };