pam_delay.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /*
  2. * pam_delay.c
  3. *
  4. * Copyright (c) Andrew G. Morgan <morgan@kernel.org> 1996-9
  5. * All rights reserved.
  6. *
  7. * $Id$
  8. *
  9. */
  10. /*
  11. * This is a simple implementation of a delay on failure mechanism; an
  12. * attempt to overcome authentication-time attacks in a simple manner.
  13. */
  14. #include "pam_private.h"
  15. #include <unistd.h>
  16. #include <time.h>
  17. /* **********************************************************************
  18. * initialize the time as unset, this is set on the return from the
  19. * authenticating pair of of the libpam pam_XXX calls.
  20. */
  21. void _pam_reset_timer(pam_handle_t *pamh)
  22. {
  23. D(("setting pamh->fail_delay.set to FALSE"));
  24. pamh->fail_delay.set = PAM_FALSE;
  25. }
  26. /* **********************************************************************
  27. * this function sets the start time for possible delayed failing.
  28. *
  29. * Eventually, it may set the timer so libpam knows how long the program
  30. * has already been executing. Currently, this value is used to seed
  31. * a pseudo-random number generator...
  32. */
  33. void _pam_start_timer(pam_handle_t *pamh)
  34. {
  35. pamh->fail_delay.begin = time(NULL);
  36. D(("starting timer..."));
  37. }
  38. /* *******************************************************************
  39. * Compute a pseudo random time. The value is base*(1 +/- 1/5) where
  40. * the distribution is pseudo gaussian (the sum of three evenly
  41. * distributed random numbers -- central limit theorem and all ;^) The
  42. * linear random numbers are based on a formulae given in Knuth's
  43. * Seminumerical recipes that was reproduced in `Numerical Recipes
  44. * in C'. It is *not* a cryptographically strong generator, but it is
  45. * probably "good enough" for our purposes here.
  46. *
  47. * /dev/random might be a better place to look for some numbers...
  48. */
  49. static unsigned int _pam_rand(unsigned int seed)
  50. {
  51. #define N1 1664525
  52. #define N2 1013904223
  53. return N1*seed + N2;
  54. }
  55. static unsigned int _pam_compute_delay(unsigned int seed, unsigned int base)
  56. {
  57. int i;
  58. double sum;
  59. unsigned int ans;
  60. for (sum=i=0; i<3; ++i) {
  61. seed = _pam_rand(seed);
  62. sum += (double) ((seed / 10) % 1000000);
  63. }
  64. sum = (sum/3.)/1e6 - .5; /* rescale */
  65. ans = (unsigned int) ( base*(1.+sum) );
  66. D(("random number: base=%u -> ans=%u\n", base, ans));
  67. return ans;
  68. }
  69. /* **********************************************************************
  70. * By default, the following function sleeps for a random time. The
  71. * actual time slept is computed above. It is based on the requested
  72. * time but will differ by up to +/- 50%. If the PAM_FAIL_DELAY item is
  73. * set by the client, this function will call the function referenced by
  74. * that item, overriding the default behavior.
  75. */
  76. void _pam_await_timer(pam_handle_t *pamh, int status)
  77. {
  78. unsigned int delay;
  79. D(("waiting?..."));
  80. delay = _pam_compute_delay(pamh->fail_delay.begin,
  81. pamh->fail_delay.delay);
  82. if (pamh->fail_delay.delay_fn_ptr) {
  83. union {
  84. const void *value;
  85. void (*fn)(int, unsigned, void *);
  86. } hack_fn_u;
  87. void *appdata_ptr;
  88. if (pamh->pam_conversation) {
  89. appdata_ptr = pamh->pam_conversation->appdata_ptr;
  90. } else {
  91. appdata_ptr = NULL;
  92. }
  93. /* always call the applications delay function, even if
  94. the delay is zero - indicate status */
  95. hack_fn_u.value = pamh->fail_delay.delay_fn_ptr;
  96. hack_fn_u.fn(status, delay, appdata_ptr);
  97. } else if (status != PAM_SUCCESS && pamh->fail_delay.set) {
  98. D(("will wait %u usec", delay));
  99. if (delay > 0) {
  100. struct timeval tval;
  101. tval.tv_sec = delay / 1000000;
  102. tval.tv_usec = delay % 1000000;
  103. select(0, NULL, NULL, NULL, &tval);
  104. }
  105. }
  106. _pam_reset_timer(pamh);
  107. D(("waiting done"));
  108. }
  109. /* **********************************************************************
  110. * this function is known to both the module and the application, it
  111. * keeps a running score of the largest-requested delay so far, as
  112. * specified by either modules or an application.
  113. */
  114. int pam_fail_delay(pam_handle_t *pamh, unsigned int usec)
  115. {
  116. unsigned int largest;
  117. IF_NO_PAMH("pam_fail_delay", pamh, PAM_SYSTEM_ERR);
  118. D(("setting delay to %u",usec));
  119. if (pamh->fail_delay.set) {
  120. largest = pamh->fail_delay.delay;
  121. } else {
  122. pamh->fail_delay.set = PAM_TRUE;
  123. largest = 0;
  124. }
  125. D(("largest = %u",largest));
  126. if (largest < usec) {
  127. D(("resetting largest delay"));
  128. pamh->fail_delay.delay = usec;
  129. }
  130. return PAM_SUCCESS;
  131. }