Don't need this.
[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         { NULL, -1, 0, NULL, NULL }
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                         pairmemcpy(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                         pairmemcpy(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 = paircreate(request, attr, 0);
264                 if (new->da->type == PW_TYPE_OCTETS) {
265                         pairmemcpy(new, (uint8_t const *) q + 1, (len - hlen) + 1);
266                         new->vp_length = (len - hlen);  /* lie about the length */
267                 } else {
268                         pairstrcpy(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: %s = '%s' -> %s = '%s'", vp->da->name, old_value, new->da->name, new_value);
277                         talloc_free(old_value);
278                         talloc_free(new_value);
279                 } else {
280                         RDEBUG2("Converted: %s -> %s", vp->da->name, new->da->name);
281                 }
282
283                 return new;
284         }
285
286         /*
287          *      Doesn't have a header {...} prefix
288          *
289          *      See if it's base64, if it is, decode it and check again!
290          */
291         decoded = fr_base64_decode(digest, sizeof(digest) - 1, vp->vp_strvalue, len);
292         if ((decoded > 0) && (digest[0] == '{') && (memchr(digest, '}', decoded) != NULL)) {
293                 RDEBUG2("Normalizing %s from base64 encoding, %zu bytes -> %zu bytes",
294                         vp->da->name, vp->vp_length, decoded);
295                 /*
296                  *      Password-With-Header is a string attribute.
297                  *      Even though we're handling binary data, the buffer
298                  *      must be \0 terminated.
299                  */
300                 digest[decoded] = '\0';
301                 pairmemcpy(vp, digest, decoded + 1);
302                 vp->vp_length = decoded;                /* lie about the length */
303
304                 goto redo;
305         }
306
307         if (RDEBUG_ENABLED3) {
308                 RDEBUG3("No {...} in Password-With-Header = \"%s\", re-writing to "
309                         "Cleartext-Password", vp->vp_strvalue);
310         } else {
311                 RDEBUG("No {...} in Password-With-Header, re-writing to Cleartext-Password");
312         }
313
314 unknown_header:
315         new = paircreate(request, PW_CLEARTEXT_PASSWORD, 0);
316         pairstrcpy(new, vp->vp_strvalue);
317
318         return new;
319 }
320
321 /*
322  *      Authorize the user for PAP authentication.
323  *
324  *      This isn't strictly necessary, but it does make the
325  *      server simpler to configure.
326  */
327 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
328 {
329         rlm_pap_t *inst = instance;
330         bool auth_type = false;
331         bool found_pw = false;
332         VALUE_PAIR *vp;
333         vp_cursor_t cursor;
334
335         for (vp = fr_cursor_init(&cursor, &request->config);
336              vp;
337              vp = fr_cursor_next(&cursor)) {
338                 VERIFY_VP(vp);
339         next:
340                 switch (vp->da->attr) {
341                 case PW_USER_PASSWORD: /* deprecated */
342                         RWDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
343                         RWDEBUG("!!! Ignoring control:User-Password.  Update your        !!!");
344                         RWDEBUG("!!! configuration so that the \"known good\" clear text !!!");
345                         RWDEBUG("!!! password is in Cleartext-Password and NOT in        !!!");
346                         RWDEBUG("!!! User-Password.                                      !!!");
347                         RWDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
348                         break;
349
350                 case PW_PASSWORD_WITH_HEADER:   /* preferred */
351                 {
352                         VALUE_PAIR *new;
353
354                         /*
355                          *      Password already exists: use that instead of this one.
356                          */
357                         if (pairfind(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
358                                 RWDEBUG("Config already contains a \"known good\" password "
359                                         "(&control:Cleartext-Password).  Ignoring &config:Password-With-Header");
360                                 break;
361                         }
362
363                         new = normify_with_header(request, vp);
364                         if (new) fr_cursor_insert(&cursor, new); /* inserts at the end of the list */
365
366                         RDEBUG2("Removing &control:Password-With-Header");
367                         vp = fr_cursor_remove(&cursor); /* advances the cursor for us */
368                         talloc_free(vp);
369
370                         found_pw = true;
371
372                         vp = fr_cursor_current(&cursor);
373                         if (vp) goto next;
374                 }
375                         break;
376
377                 case PW_CLEARTEXT_PASSWORD:
378                 case PW_CRYPT_PASSWORD:
379                 case PW_NS_MTA_MD5_PASSWORD:
380                         found_pw = true;
381                         break;  /* don't touch these */
382
383                 case PW_MD5_PASSWORD:
384                 case PW_SMD5_PASSWORD:
385                 case PW_NT_PASSWORD:
386                 case PW_LM_PASSWORD:
387                         if (inst->normify) {
388                                 normify(request, vp, 16); /* ensure it's in the right format */
389                         }
390                         found_pw = true;
391                         break;
392
393 #ifdef HAVE_OPENSSL_EVP_H
394                 case PW_SHA2_PASSWORD:
395                         if (inst->normify) {
396                                 normify(request, vp, 28); /* ensure it's in the right format */
397                         }
398                         found_pw = true;
399                         break;
400
401                 case PW_SSHA2_224_PASSWORD:
402                         if (inst->normify) {
403                                 normify(request, vp, 28); /* ensure it's in the right format */
404                         }
405                         found_pw = true;
406                         break;
407
408                 case PW_SSHA2_256_PASSWORD:
409                         if (inst->normify) {
410                                 normify(request, vp, 32); /* ensure it's in the right format */
411                         }
412                         found_pw = true;
413                         break;
414
415                 case PW_SSHA2_384_PASSWORD:
416                         if (inst->normify) {
417                                 normify(request, vp, 48); /* ensure it's in the right format */
418                         }
419                         found_pw = true;
420                         break;
421
422                 case PW_SSHA2_512_PASSWORD:
423                         if (inst->normify) {
424                                 normify(request, vp, 64); /* ensure it's in the right format */
425                         }
426                         found_pw = true;
427                         break;
428 #endif
429
430                 case PW_SHA_PASSWORD:
431                 case PW_SSHA_PASSWORD:
432                         if (inst->normify) {
433                                 normify(request, vp, 20); /* ensure it's in the right format */
434                         }
435                         found_pw = true;
436                         break;
437
438                         /*
439                          *      If it's proxied somewhere, don't complain
440                          *      about not having passwords or Auth-Type.
441                          */
442                 case PW_PROXY_TO_REALM:
443                 {
444                         REALM *realm = realm_find(vp->vp_strvalue);
445                         if (realm && realm->auth_pool) {
446                                 return RLM_MODULE_NOOP;
447                         }
448                         break;
449                 }
450
451                 case PW_AUTH_TYPE:
452                         auth_type = true;
453
454                         /*
455                          *      Auth-Type := Accept
456                          *      Auth-Type := Reject
457                          */
458                         if ((vp->vp_integer == 254) ||
459                             (vp->vp_integer == 4)) {
460                             found_pw = true;
461                         }
462                         break;
463
464                 default:
465                         break;  /* ignore it */
466
467                 }
468         }
469
470         /*
471          *      Print helpful warnings if there was no password.
472          */
473         if (!found_pw) {
474                 /*
475                  *      Likely going to be proxied.  Avoid printing
476                  *      warning message.
477                  */
478                 if (pairfind(request->config, PW_REALM, 0, TAG_ANY) ||
479                     (pairfind(request->config, PW_PROXY_TO_REALM, 0, TAG_ANY))) {
480                         return RLM_MODULE_NOOP;
481                 }
482
483                 /*
484                  *      The TLS types don't need passwords.
485                  */
486                 vp = pairfind(request->packet->vps, PW_EAP_TYPE, 0, TAG_ANY);
487                 if (vp &&
488                     ((vp->vp_integer == 13) || /* EAP-TLS */
489                      (vp->vp_integer == 21) || /* EAP-TTLS */
490                      (vp->vp_integer == 25))) { /* PEAP */
491                         return RLM_MODULE_NOOP;
492                 }
493
494                 RWDEBUG("No \"known good\" password found for the user.  Not setting Auth-Type");
495                 RWDEBUG("Authentication will fail unless a \"known good\" password is available");
496                 return RLM_MODULE_NOOP;
497         }
498
499         /*
500          *      Don't touch existing Auth-Types.
501          */
502         if (auth_type) {
503                 if (auth_type != inst->auth_type) RWDEBUG2("Auth-Type already set.  Not setting to PAP");
504                 return RLM_MODULE_NOOP;
505         }
506
507         /*
508          *      Can't do PAP if there's no password.
509          */
510         if (!request->password ||
511             (request->password->da->attr != PW_USER_PASSWORD)) {
512                 RDEBUG2("No User-Password attribute in the request.  Cannot do PAP");
513                 return RLM_MODULE_NOOP;
514         }
515
516         if (inst->auth_type) {
517                 vp = radius_paircreate(request, &request->config,
518                                        PW_AUTH_TYPE, 0);
519                 vp->vp_integer = inst->auth_type;
520         }
521
522         return RLM_MODULE_UPDATED;
523 }
524
525 /*
526  *      PAP authentication functions
527  */
528
529 static rlm_rcode_t CC_HINT(nonnull) pap_auth_clear(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
530 {
531         if (RDEBUG_ENABLED3) {
532                 RDEBUG3("Comparing with \"known good\" Cleartext-Password \"%s\" (%zd)", vp->vp_strvalue, vp->vp_length);
533         } else {
534                 RDEBUG("Comparing with \"known good\" Cleartext-Password");
535         }
536
537         if ((vp->vp_length != request->password->vp_length) ||
538             (rad_digest_cmp(vp->vp_octets,
539                             request->password->vp_octets,
540                             vp->vp_length) != 0)) {
541                 REDEBUG("Cleartext password does not match \"known good\" password");
542                 return RLM_MODULE_REJECT;
543         }
544         return RLM_MODULE_OK;
545 }
546
547 static rlm_rcode_t CC_HINT(nonnull) pap_auth_crypt(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
548 {
549         if (RDEBUG_ENABLED3) {
550                 RDEBUG3("Comparing with \"known good\" Crypt-Password \"%s\"", vp->vp_strvalue);
551         } else {
552                 RDEBUG("Comparing with \"known-good\" Crypt-password");
553         }
554
555         if (fr_crypt_check(request->password->vp_strvalue,
556                            vp->vp_strvalue) != 0) {
557                 REDEBUG("Crypt digest does not match \"known good\" digest");
558                 return RLM_MODULE_REJECT;
559         }
560         return RLM_MODULE_OK;
561 }
562
563 static rlm_rcode_t CC_HINT(nonnull) pap_auth_md5(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
564 {
565         FR_MD5_CTX md5_context;
566         uint8_t digest[128];
567
568         RDEBUG("Comparing with \"known-good\" MD5-Password");
569
570         if (inst->normify) {
571                 normify(request, vp, 16);
572         }
573         if (vp->vp_length != 16) {
574                 REDEBUG("\"known-good\" MD5 password has incorrect length");
575                 return RLM_MODULE_INVALID;
576         }
577
578         fr_md5_init(&md5_context);
579         fr_md5_update(&md5_context, request->password->vp_octets,
580                      request->password->vp_length);
581         fr_md5_final(digest, &md5_context);
582
583         if (rad_digest_cmp(digest, vp->vp_octets, vp->vp_length) != 0) {
584                 REDEBUG("MD5 digest does not match \"known good\" digest");
585                 return RLM_MODULE_REJECT;
586         }
587
588         return RLM_MODULE_OK;
589 }
590
591
592 static rlm_rcode_t CC_HINT(nonnull) pap_auth_smd5(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
593 {
594         FR_MD5_CTX md5_context;
595         uint8_t digest[128];
596
597         RDEBUG("Comparing with \"known-good\" SMD5-Password");
598
599         if (inst->normify) {
600                 normify(request, vp, 16);
601         }
602         if (vp->vp_length <= 16) {
603                 REDEBUG("\"known-good\" SMD5-Password has incorrect length");
604                 return RLM_MODULE_INVALID;
605         }
606
607         fr_md5_init(&md5_context);
608         fr_md5_update(&md5_context, request->password->vp_octets,
609                      request->password->vp_length);
610         fr_md5_update(&md5_context, &vp->vp_octets[16], vp->vp_length - 16);
611         fr_md5_final(digest, &md5_context);
612
613         /*
614          *      Compare only the MD5 hash results, not the salt.
615          */
616         if (rad_digest_cmp(digest, vp->vp_octets, 16) != 0) {
617                 REDEBUG("SMD5 digest does not match \"known good\" digest");
618                 return RLM_MODULE_REJECT;
619         }
620
621         return RLM_MODULE_OK;
622 }
623
624 static rlm_rcode_t CC_HINT(nonnull) pap_auth_sha(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
625 {
626         fr_SHA1_CTX sha1_context;
627         uint8_t digest[128];
628
629         RDEBUG("Comparing with \"known-good\" SHA-Password");
630
631         if (inst->normify) {
632                 normify(request, vp, 20);
633         }
634         if (vp->vp_length != 20) {
635                 REDEBUG("\"known-good\" SHA1-password has incorrect length");
636                 return RLM_MODULE_INVALID;
637         }
638
639         fr_sha1_init(&sha1_context);
640         fr_sha1_update(&sha1_context, request->password->vp_octets,
641                       request->password->vp_length);
642         fr_sha1_final(digest,&sha1_context);
643
644         if (rad_digest_cmp(digest, vp->vp_octets, vp->vp_length) != 0) {
645                 REDEBUG("SHA1 digest does not match \"known good\" digest");
646                 return RLM_MODULE_REJECT;
647         }
648
649         return RLM_MODULE_OK;
650 }
651
652 static rlm_rcode_t CC_HINT(nonnull) pap_auth_ssha(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
653 {
654         fr_SHA1_CTX sha1_context;
655         uint8_t digest[128];
656
657         RDEBUG("Comparing with \"known-good\" SSHA-Password");
658
659         if (inst->normify) {
660                 normify(request, vp, 20);
661         }
662         if (vp->vp_length <= 20) {
663                 REDEBUG("\"known-good\" SSHA-Password has incorrect length");
664                 return RLM_MODULE_INVALID;
665         }
666
667         fr_sha1_init(&sha1_context);
668         fr_sha1_update(&sha1_context, request->password->vp_octets, request->password->vp_length);
669
670         fr_sha1_update(&sha1_context, &vp->vp_octets[20], vp->vp_length - 20);
671         fr_sha1_final(digest, &sha1_context);
672
673         if (rad_digest_cmp(digest, vp->vp_octets, 20) != 0) {
674                 REDEBUG("SSHA digest does not match \"known good\" digest");
675                 return RLM_MODULE_REJECT;
676         }
677
678         return RLM_MODULE_OK;
679 }
680
681 #ifdef HAVE_OPENSSL_EVP_H
682 static rlm_rcode_t CC_HINT(nonnull) pap_auth_sha2(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
683 {
684         EVP_MD_CTX *ctx;
685         EVP_MD const *md;
686         char const *name;
687         uint8_t digest[EVP_MAX_MD_SIZE];
688         unsigned int digest_len;
689
690         RDEBUG("Comparing with \"known-good\" SHA2-Password");
691
692         if (inst->normify) normify(request, vp, 28);
693
694         /*
695          *      All the SHA-2 algorithms produce digests of different lengths,
696          *      so it's trivial to determine which EVP_MD to use.
697          */
698         switch (vp->vp_length) {
699         /* SHA-224 */
700         case 28:
701                 name = "SHA2-224";
702                 md = EVP_sha224();
703                 break;
704
705         /* SHA-256 */
706         case 32:
707                 name = "SHA2-256";
708                 md = EVP_sha256();
709                 break;
710
711         /* SHA-384 */
712         case 48:
713                 name = "SHA2-384";
714                 md = EVP_sha384();
715                 break;
716
717         /* SHA-512 */
718         case 64:
719                 name = "SHA2-512";
720                 md = EVP_sha512();
721                 break;
722
723         default:
724                 REDEBUG("\"known good\" digest length (%zu) does not match output length of any SHA-2 digests",
725                         vp->vp_length);
726                 return RLM_MODULE_INVALID;
727         }
728
729         ctx = EVP_MD_CTX_create();
730         EVP_DigestInit_ex(ctx, md, NULL);
731         EVP_DigestUpdate(ctx, request->password->vp_octets, request->password->vp_length);
732         EVP_DigestFinal_ex(ctx, digest, &digest_len);
733         EVP_MD_CTX_destroy(ctx);
734
735         rad_assert((size_t) digest_len == vp->vp_length);       /* This would be an OpenSSL bug... */
736
737         if (rad_digest_cmp(digest, vp->vp_octets, vp->vp_length) != 0) {
738                 REDEBUG("%s digest does not match \"known good\" digest", name);
739                 return RLM_MODULE_REJECT;
740         }
741
742         return RLM_MODULE_OK;
743 }
744
745 static rlm_rcode_t CC_HINT(nonnull) pap_auth_ssha2(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
746 {
747         EVP_MD_CTX *ctx;
748         EVP_MD const *md = NULL;
749         char const *name = NULL;
750         uint8_t digest[EVP_MAX_MD_SIZE];
751         unsigned int digest_len, min_len = 0;
752
753         switch (vp->da->attr) {
754         case PW_SSHA2_224_PASSWORD:
755                 name = "SSHA2-224";
756                 md = EVP_sha224();
757                 min_len = 28;
758                 break;
759
760         case PW_SSHA2_256_PASSWORD:
761                 name = "SSHA2-256";
762                 md = EVP_sha256();
763                 min_len = 32;
764                 break;
765
766         case PW_SSHA2_384_PASSWORD:
767                 name = "SSHA2-384";
768                 md = EVP_sha384();
769                 min_len = 48;
770                 break;
771
772         case PW_SSHA2_512_PASSWORD:
773                 name = "SSHA2-512";
774                 min_len = 64;
775                 md = EVP_sha512();
776                 break;
777
778         default:
779                 rad_assert(0);
780         }
781
782         RDEBUG("Comparing with \"known-good\" %s-Password", name);
783
784         /*
785          *      Unlike plain SHA2 we already know what length
786          *      to expect, so can be more specific with the
787          *      minimum digest length.
788          */
789         if (inst->normify) normify(request, vp, min_len + 1);
790
791         if (vp->vp_length <= min_len) {
792                 REDEBUG("\"known-good\" %s-Password has incorrect length, got %zu bytes, need at least %u bytes",
793                         name, vp->vp_length, min_len + 1);
794                 return RLM_MODULE_INVALID;
795         }
796
797         ctx = EVP_MD_CTX_create();
798         EVP_DigestInit_ex(ctx, md, NULL);
799         EVP_DigestUpdate(ctx, request->password->vp_octets, request->password->vp_length);
800         EVP_DigestUpdate(ctx, &vp->vp_octets[min_len], vp->vp_length - min_len);
801         EVP_DigestFinal_ex(ctx, digest, &digest_len);
802         EVP_MD_CTX_destroy(ctx);
803
804         rad_assert((size_t) digest_len == min_len);     /* This would be an OpenSSL bug... */
805
806         /*
807          *      Only compare digest_len bytes, the rest is salt.
808          */
809         if (rad_digest_cmp(digest, vp->vp_octets, (size_t)digest_len) != 0) {
810                 REDEBUG("%s digest does not match \"known good\" digest", name);
811                 return RLM_MODULE_REJECT;
812         }
813
814         return RLM_MODULE_OK;
815 }
816 #endif
817
818 static rlm_rcode_t CC_HINT(nonnull) pap_auth_nt(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
819 {
820         ssize_t len;
821         uint8_t digest[16];
822         uint8_t ucs2_password[512];
823
824         RDEBUG("Comparing with \"known-good\" NT-Password");
825
826         rad_assert(request->password != NULL);
827         rad_assert(request->password->da->attr == PW_USER_PASSWORD);
828
829         if (inst->normify) {
830                 normify(request, vp, 16);
831         }
832
833         if (vp->vp_length != 16) {
834                 REDEBUG("\"known good\" NT-Password has incorrect length");
835                 return RLM_MODULE_INVALID;
836         }
837
838         len = fr_utf8_to_ucs2(ucs2_password, sizeof(ucs2_password), request->password->vp_strvalue, request->password->vp_length);
839         if (len < 0) {
840                 REDEBUG("User-Password is not in UCS2 format");
841                 return RLM_MODULE_INVALID;
842         }
843
844         fr_md4_calc(digest, (uint8_t *) ucs2_password, len);
845
846         if (rad_digest_cmp(digest, vp->vp_octets, vp->vp_length) != 0) {
847                 REDEBUG("NT digest does not match \"known good\" digest");
848                 return RLM_MODULE_REJECT;
849         }
850
851         return RLM_MODULE_OK;
852 }
853
854
855 static rlm_rcode_t CC_HINT(nonnull) pap_auth_lm(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
856 {
857         uint8_t digest[16];
858         char charbuf[32 + 1];
859         ssize_t len;
860
861         RDEBUG("Comparing with \"known-good\" LM-Password");
862
863         if (inst->normify) {
864                 normify(request, vp, 16);
865         }
866         if (vp->vp_length != 16) {
867                 REDEBUG("\"known good\" LM-Password has incorrect length");
868                 return RLM_MODULE_INVALID;
869         }
870
871         len = radius_xlat(charbuf, sizeof(charbuf), request, "%{mschap:LM-Hash %{User-Password}}", NULL, NULL);
872         if (len < 0){
873                 return RLM_MODULE_FAIL;
874         }
875
876         if ((fr_hex2bin(digest, sizeof(digest), charbuf, len) != vp->vp_length) ||
877             (rad_digest_cmp(digest, vp->vp_octets, vp->vp_length) != 0)) {
878                 REDEBUG("LM digest does not match \"known good\" digest");
879                 return RLM_MODULE_REJECT;
880         }
881
882         return RLM_MODULE_OK;
883 }
884
885 static rlm_rcode_t CC_HINT(nonnull) pap_auth_ns_mta_md5(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
886 {
887         FR_MD5_CTX md5_context;
888         uint8_t digest[128];
889         uint8_t buff[MAX_STRING_LEN];
890         char buff2[MAX_STRING_LEN + 50];
891
892         RDEBUG("Using NT-MTA-MD5-Password");
893
894         if (vp->vp_length != 64) {
895                 REDEBUG("\"known good\" NS-MTA-MD5-Password has incorrect length");
896                 return RLM_MODULE_INVALID;
897         }
898
899         /*
900          *      Sanity check the value of NS-MTA-MD5-Password
901          */
902         if (fr_hex2bin(digest, sizeof(digest), vp->vp_strvalue, vp->vp_length) != 16) {
903                 REDEBUG("\"known good\" NS-MTA-MD5-Password has invalid value");
904                 return RLM_MODULE_INVALID;
905         }
906
907         /*
908          *      Ensure we don't have buffer overflows.
909          *
910          *      This really: sizeof(buff) - 2 - 2*32 - strlen(passwd)
911          */
912         if (request->password->vp_length >= (sizeof(buff) - 2 - 2 * 32)) {
913                 REDEBUG("\"known good\" NS-MTA-MD5-Password is too long");
914                 return RLM_MODULE_INVALID;
915         }
916
917         /*
918          *      Set up the algorithm.
919          */
920         {
921                 char *p = buff2;
922
923                 memcpy(p, &vp->vp_octets[32], 32);
924                 p += 32;
925                 *(p++) = 89;
926                 strcpy(p, request->password->vp_strvalue);
927                 p += strlen(p);
928                 *(p++) = 247;
929                 memcpy(p, &vp->vp_octets[32], 32);
930                 p += 32;
931
932                 fr_md5_init(&md5_context);
933                 fr_md5_update(&md5_context, (uint8_t *) buff2, p - buff2);
934                 fr_md5_final(buff, &md5_context);
935         }
936
937         if (rad_digest_cmp(digest, buff, 16) != 0) {
938                 REDEBUG("NS-MTA-MD5 digest does not match \"known good\" digest");
939                 return RLM_MODULE_REJECT;
940         }
941
942         return RLM_MODULE_OK;
943 }
944
945
946 /*
947  *      Authenticate the user via one of any well-known password.
948  */
949 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
950 {
951         rlm_pap_t *inst = instance;
952         VALUE_PAIR *vp;
953         rlm_rcode_t rc = RLM_MODULE_INVALID;
954         vp_cursor_t cursor;
955         rlm_rcode_t (*auth_func)(rlm_pap_t *, REQUEST *, VALUE_PAIR *) = NULL;
956
957         if (!request->password ||
958             (request->password->da->vendor != 0) ||
959             (request->password->da->attr != PW_USER_PASSWORD)) {
960                 REDEBUG("You set 'Auth-Type = PAP' for a request that does not contain a User-Password attribute!");
961                 return RLM_MODULE_INVALID;
962         }
963
964         /*
965          *      The user MUST supply a non-zero-length password.
966          */
967         if (request->password->vp_length == 0) {
968                 REDEBUG("Password must not be empty");
969                 return RLM_MODULE_INVALID;
970         }
971
972         if (RDEBUG_ENABLED3) {
973                 RDEBUG3("Login attempt with password \"%s\" (%zd)", request->password->vp_strvalue, request->password->vp_length);
974         } else {
975                 RDEBUG("Login attempt with password");
976         }
977
978         /*
979          *      Auto-detect passwords, by attribute in the
980          *      config items, to find out which authentication
981          *      function to call.
982          */
983         for (vp = fr_cursor_init(&cursor, &request->config);
984              vp;
985              vp = fr_cursor_next(&cursor)) {
986                 if (!vp->da->vendor) switch (vp->da->attr) {
987                 case PW_CLEARTEXT_PASSWORD:
988                         auth_func = &pap_auth_clear;
989                         break;
990
991                 case PW_CRYPT_PASSWORD:
992                         auth_func = &pap_auth_crypt;
993                         break;
994
995                 case PW_MD5_PASSWORD:
996                         auth_func = &pap_auth_md5;
997                         break;
998
999                 case PW_SMD5_PASSWORD:
1000                         auth_func = &pap_auth_smd5;
1001                         break;
1002
1003 #ifdef HAVE_OPENSSL_EVP_H
1004                 case PW_SHA2_PASSWORD:
1005                         auth_func = &pap_auth_sha2;
1006                         break;
1007
1008                 case PW_SSHA2_224_PASSWORD:
1009                 case PW_SSHA2_256_PASSWORD:
1010                 case PW_SSHA2_384_PASSWORD:
1011                 case PW_SSHA2_512_PASSWORD:
1012                         auth_func = &pap_auth_ssha2;
1013                         break;
1014 #endif
1015
1016                 case PW_SHA_PASSWORD:
1017                         auth_func = &pap_auth_sha;
1018                         break;
1019
1020                 case PW_SSHA_PASSWORD:
1021                         auth_func = &pap_auth_ssha;
1022                         break;
1023
1024                 case PW_NT_PASSWORD:
1025                         auth_func = &pap_auth_nt;
1026                         break;
1027
1028                 case PW_LM_PASSWORD:
1029                         auth_func = &pap_auth_lm;
1030                         break;
1031
1032                 case PW_NS_MTA_MD5_PASSWORD:
1033                         auth_func = &pap_auth_ns_mta_md5;
1034                         break;
1035
1036                 default:
1037                         break;
1038                 }
1039
1040                 if (auth_func != NULL) break;
1041         }
1042
1043         /*
1044          *      No attribute was found that looked like a password to match.
1045          */
1046         if (!auth_func) {
1047                 RDEBUG("No password configured for the user.  Cannot do authentication");
1048                 return RLM_MODULE_FAIL;
1049         }
1050
1051         /*
1052          *      Authenticate, and return.
1053          */
1054         rc = auth_func(inst, request, vp);
1055
1056         if (rc == RLM_MODULE_REJECT) {
1057                 RDEBUG("Passwords don't match");
1058         }
1059
1060         if (rc == RLM_MODULE_OK) {
1061                 RDEBUG("User authenticated successfully");
1062         }
1063
1064         return rc;
1065 }
1066
1067
1068 /*
1069  *      The module name should be the only globally exported symbol.
1070  *      That is, everything else should be 'static'.
1071  *
1072  *      If the module needs to temporarily modify it's instantiation
1073  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
1074  *      The server will then take care of ensuring that the module
1075  *      is single-threaded.
1076  */
1077 extern module_t rlm_pap;
1078 module_t rlm_pap = {
1079         .magic          = RLM_MODULE_INIT,
1080         .name           = "pap",
1081         .type           = RLM_TYPE_HUP_SAFE,
1082         .inst_size      = sizeof(rlm_pap_t),
1083         .config         = module_config,
1084         .instantiate    = mod_instantiate,
1085         .methods = {
1086                 [MOD_AUTHENTICATE]      = mod_authenticate,
1087                 [MOD_AUTHORIZE]         = mod_authorize
1088         },
1089 };