All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_pap.c
Go to the documentation of this file.
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: be9cb41684d143ba3162e051b7617272dc1e5aa0 $
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: be9cb41684d143ba3162e051b7617272dc1e5aa0 $")
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  { FR_CONF_OFFSET("normalise", PW_TYPE_BOOLEAN, rlm_pap_t, normify), .dflt = "yes" },
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  fr_dict_enum_t *dval;
115 
116  inst->name = cf_section_name2(conf);
117  if (!inst->name) {
118  inst->name = cf_section_name1(conf);
119  }
120 
121  dval = fr_dict_enum_by_name(NULL, fr_dict_attr_by_num(NULL, 0, PW_AUTH_TYPE), 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
203  * - New #VALUE_PAIR on success.
204  * - NULL on error.
205  */
207 {
208  int attr;
209  char const *p, *q;
210  size_t len;
211 
212  uint8_t digest[256];
213  ssize_t decoded;
214 
215  char buffer[256];
216 
217  VALUE_PAIR *new;
218 
219  VERIFY_VP(vp);
220 
221  /*
222  * Ensure this is only ever called with a
223  * string type attribute.
224  */
225  rad_assert(vp->da->type == PW_TYPE_STRING);
226 
227 redo:
228  p = vp->vp_strvalue;
229  len = vp->vp_length;
230 
231  /*
232  * Has a header {...} prefix
233  */
234  q = strchr(p, '}');
235  if (q) {
236  size_t hlen;
237 
238  hlen = (q + 1) - p;
239  if (hlen >= sizeof(buffer)) {
240  REDEBUG("Password header too long. Got %zu bytes must be less than %zu bytes",
241  hlen, sizeof(buffer));
242  return NULL;
243  }
244 
245  memcpy(buffer, p, hlen);
246  buffer[hlen] = '\0';
247 
248  attr = fr_str2int(header_names, buffer, 0);
249  if (!attr) {
250  if (RDEBUG_ENABLED3) {
251  RDEBUG3("Unknown header {%s} in Password-With-Header = \"%s\", re-writing to "
252  "Cleartext-Password", buffer, vp->vp_strvalue);
253  } else {
254  RDEBUG("Unknown header {%s} in Password-With-Header, re-writing to "
255  "Cleartext-Password", buffer);
256  }
257  goto unknown_header;
258  }
259 
260  /*
261  * The data after the '}' may be binary, so we copy it via
262  * memcpy. BUT it might be a string (or used as one), so
263  * we ensure that there's a trailing zero, too.
264  */
265  new = fr_pair_afrom_num(request, 0, attr);
266  if (new->da->type == PW_TYPE_OCTETS) {
267  fr_pair_value_memcpy(new, (uint8_t const *) q + 1, (len - hlen) + 1);
268  new->vp_length = (len - hlen); /* lie about the length */
269  } else {
270  fr_pair_value_strcpy(new, q + 1);
271  }
272 
273  if (RDEBUG_ENABLED3) {
274  char *old_value, *new_value;
275 
276  old_value = fr_pair_value_asprint(request, vp, '\'');
277  new_value = fr_pair_value_asprint(request, new, '\'');
278  RDEBUG3("Converted: %s = '%s' -> %s = '%s'", vp->da->name, old_value, new->da->name, new_value);
279  talloc_free(old_value);
280  talloc_free(new_value);
281  } else {
282  RDEBUG2("Converted: %s -> %s", vp->da->name, new->da->name);
283  }
284 
285  return new;
286  }
287 
288  /*
289  * Doesn't have a header {...} prefix
290  *
291  * See if it's base64, if it is, decode it and check again!
292  */
293  decoded = fr_base64_decode(digest, sizeof(digest), vp->vp_strvalue, len);
294  if ((decoded > 0) && (digest[0] == '{') && (memchr(digest, '}', decoded) != NULL)) {
295  RDEBUG2("Normalizing %s from base64 encoding, %zu bytes -> %zu bytes",
296  vp->da->name, vp->vp_length, decoded);
297  /*
298  * Password-With-Header is a string attribute.
299  * Even though we're handling binary data, the buffer
300  * must be \0 terminated.
301  */
302  fr_pair_value_bstrncpy(vp, digest, decoded);
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 = fr_pair_afrom_num(request, 0, PW_CLEARTEXT_PASSWORD);
316  fr_pair_value_strcpy(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 (fr_pair_find_by_num(request->config, 0, PW_CLEARTEXT_PASSWORD, 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 (fr_pair_find_by_num(request->config, 0, PW_REALM, TAG_ANY) ||
479  (fr_pair_find_by_num(request->config, 0, PW_PROXY_TO_REALM, TAG_ANY))) {
480  return RLM_MODULE_NOOP;
481  }
482 
483  /*
484  * The TLS types don't need passwords.
485  */
486  vp = fr_pair_find_by_num(request->packet->vps, 0, PW_EAP_TYPE, 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_pair_create(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  (fr_radius_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 (fr_radius_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 (fr_radius_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 (fr_radius_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 (fr_radius_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 (fr_radius_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 (fr_radius_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 (fr_radius_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  (fr_radius_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 (fr_radius_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;
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 = {
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 = {
1088  },
1089 };
void fr_sha1_update(fr_sha1_ctx *context, uint8_t const *data, size_t len)
Definition: sha1.c:106
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:239
The module is OK, continue.
Definition: radiusd.h:91
Metadata exported by the module.
Definition: modules.h:134
VALUE_PAIR * fr_pair_afrom_num(TALLOC_CTX *ctx, unsigned int vendor, unsigned int attr)
Create a new valuepair.
Definition: pair.c:106
static char const * name
char const * name
Definition: rlm_pap.c:51
VALUE_PAIR * radius_pair_create(TALLOC_CTX *ctx, VALUE_PAIR **vps, unsigned int attribute, unsigned int vendor)
Create a VALUE_PAIR and add it to a list of VALUE_PAIR s.
Definition: pair.c:704
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
Handle authorization requests using Couchbase document data.
int auth_type
Definition: rlm_pap.c:52
#define VERIFY_VP(_x)
Definition: pair.h:44
#define UNUSED
Definition: libradius.h:134
#define RLM_MODULE_INIT
Definition: modules.h:86
static rlm_rcode_t CC_HINT(nonnull)
Definition: rlm_pap.c:327
void fr_md5_init(FR_MD5_CTX *ctx)
Initialise a new MD5 context.
Definition: md5.c:84
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
home_pool_t * auth_pool
Definition: realms.h:183
Definition: realms.h:178
void fr_md4_calc(uint8_t out[MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
Calculate the MD4 hash of the contents of a buffer.
Definition: md4.c:24
#define inst
static VALUE_PAIR * normify_with_header(REQUEST *request, VALUE_PAIR *vp)
Convert a Password-With-Header attribute to the correct type.
Definition: rlm_pap.c:206
The module considers the request invalid.
Definition: radiusd.h:93
#define RLM_TYPE_HUP_SAFE
Will be restarted on HUP.
Definition: modules.h:79
static int mod_instantiate(CONF_SECTION *conf, void *instance)
Definition: rlm_pap.c:111
static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request) CC_HINT(nonnull)
ssize_t fr_base64_decode(uint8_t *out, size_t outlen, char const *in, size_t inlen)
Definition: base64.c:251
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
fr_dict_enum_t * fr_dict_enum_by_name(fr_dict_t *dict, fr_dict_attr_t const *da, char const *val)
Definition: dict.c:3703
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
void fr_sha1_init(fr_sha1_ctx *context)
Definition: sha1.c:94
char * fr_pair_value_asprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
Print one attribute value to a string.
Definition: pair.c:2123
void fr_md5_update(FR_MD5_CTX *ctx, uint8_t const *in, size_t inlen) CC_BOUNDED(__string__
#define rad_assert(expr)
Definition: rad_assert.h:38
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
Definition: token.c:451
void fr_pair_value_strcpy(VALUE_PAIR *vp, char const *src)
Copy data into an "string" data type.
Definition: pair.c:2013
USES_APPLE_DEPRECATED_API struct rlm_pap_t rlm_pap_t
void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *vp)
Insert a single VALUE_PAIR at the end of the list.
Definition: cursor.c:321
size_t fr_hex2bin(uint8_t *bin, size_t outlen, char const *hex, size_t inlen)
Convert hex strings to binary data.
Definition: misc.c:220
unsigned int attr
Attribute number.
Definition: dict.h:79
Immediately reject the request.
Definition: radiusd.h:89
static void normify(REQUEST *request, VALUE_PAIR *vp, size_t min_len)
Hex or base64 or bin auto-discovery.
Definition: rlm_pap.c:147
unsigned int vendor
Vendor that defines this attribute.
Definition: dict.h:78
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
VALUE_PAIR * fr_cursor_current(vp_cursor_t *cursor)
Return the VALUE_PAIR the cursor current points to.
Definition: cursor.c:304
void fr_sha1_final(uint8_t digest[20], fr_sha1_ctx *context)
Definition: sha1.c:132
0 methods index for authenticate section.
Definition: modules.h:41
module_t rlm_pap
Definition: rlm_pap.c:1078
A truth value.
Definition: radius.h:56
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, xlat_escape_t escape, void *escape_ctx) CC_HINT(nonnull(1
static rs_t * conf
Definition: radsniff.c:46
#define RWDEBUG2(fmt,...)
Definition: log.h:252
ssize_t fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inlen)
Convert UTF8 string to UCS2 encoding.
Definition: misc.c:580
bool normify
Definition: rlm_pap.c:53
int fr_crypt_check(char const *key, char const *salt)
Definition: crypt.c:55
char const * cf_section_name1(CONF_SECTION const *cs)
Definition: conffile.c:3592
Module succeeded without doing anything.
Definition: radiusd.h:96
static const FR_NAME_NUMBER header_names[]
Definition: rlm_pap.c:76
#define RDEBUG2(fmt,...)
Definition: log.h:244
char name[1]
Attribute name.
Definition: dict.h:89
REALM * realm_find(char const *name)
Definition: realms.c:2235
static const CONF_PARSER module_config[]
Definition: rlm_pap.c:65
uint64_t magic
Used to validate module struct.
Definition: modules.h:135
Module failed, don't reply.
Definition: radiusd.h:90
#define TAG_ANY
Definition: pair.h:191
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
int value
Enum value.
Definition: dict.h:96
#define REDEBUG(fmt,...)
Definition: log.h:254
int fr_radius_digest_cmp(uint8_t const *a, uint8_t const *b, size_t length)
Do a comparison of two authentication digests by comparing the FULL digest.
Definition: radius.c:578
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
VALUE_PAIR * fr_cursor_remove(vp_cursor_t *cursor)
Remove the current pair.
Definition: cursor.c:433
void void fr_md5_final(uint8_t out[MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx) CC_BOUNDED(__minbytes__
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
void fr_pair_value_bstrncpy(VALUE_PAIR *vp, void const *src, size_t len)
Copy data into an "string" data type.
Definition: pair.c:2043
#define MAX_STRING_LEN
Definition: libradius.h:120
fr_dict_attr_t const * fr_dict_attr_by_num(fr_dict_t *dict, unsigned int vendor, unsigned int attr)
Lookup a fr_dict_attr_t by its vendor and attribute numbers.
Definition: dict.c:3519
String of printable characters.
Definition: radius.h:33
#define RWDEBUG(fmt,...)
Definition: log.h:251
PW_TYPE type
Value type.
Definition: dict.h:80
1 methods index for authorize section.
Definition: modules.h:42
#define RCSID(id)
Definition: build.h:135
OK (pairs modified).
Definition: radiusd.h:97
#define RDEBUG(fmt,...)
Definition: log.h:243
Value of an enumerated attribute.
Definition: dict.h:94
Raw octets.
Definition: radius.h:38
void fr_pair_value_memcpy(VALUE_PAIR *vp, uint8_t const *src, size_t len)
Copy data into an "octets" data type.
Definition: pair.c:1905
char const * cf_section_name2(CONF_SECTION const *cs)
Definition: conffile.c:3601
#define USES_APPLE_DEPRECATED_API
Definition: build.h:122
#define RDEBUG3(fmt,...)
Definition: log.h:245