4 * Copyright (c) Andrew G. Morgan <morgan@linux.kernel.org> 1996-9
10 * Revision 1.1 2000/06/20 22:11:14 agmorgan
13 * Revision 1.2 1999/07/04 23:23:42 morgan
14 * add appdata_ptr to app callback function
16 * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
17 * Linux PAM sources pre-0.66
22 * This is a simple implementation of a delay on failure mechanism; an
23 * attempt to overcome authentication-time attacks in a simple manner.
27 #include "pam_private.h"
29 /* **********************************************************************
30 * initialize the time as unset, this is set on the return from the
31 * authenticating pair of of the libpam pam_XXX calls.
34 void _pam_reset_timer(pam_handle_t *pamh)
36 D(("setting pamh->fail_delay.set to FALSE"));
37 pamh->fail_delay.set = PAM_FALSE;
40 /* **********************************************************************
41 * this function sets the start time for possible delayed failing.
43 * Eventually, it may set the timer so libpam knows how long the program
44 * has already been executing. Currently, this value is used to seed
45 * a pseudo-random number generator...
48 void _pam_start_timer(pam_handle_t *pamh)
50 pamh->fail_delay.begin = time(NULL);
51 D(("starting timer..."));
54 /* *******************************************************************
55 * Compute a pseudo random time. The value is base*(1 +/- 1/5) where
56 * the distribution is pseudo gausian (the sum of three evenly
57 * distributed random numbers -- central limit theorem and all ;^) The
58 * linear random numbers are based on a formulae given in Knuth's
59 * Seminumerical recipies that was reproduced in `Numerical Recipies
60 * in C'. It is *not* a cryptographically strong generator, but it is
61 * probably "good enough" for our purposes here.
63 * /dev/random might be a better place to look for some numbers...
66 static unsigned int _pam_rand(unsigned int seed)
73 static unsigned int _pam_compute_delay(unsigned int seed, unsigned int base)
79 for (sum=i=0; i<3; ++i) {
80 seed = _pam_rand(seed);
81 sum += (double) ((seed / 10) % 1000000);
83 sum = (sum/3.)/1e6 - .5; /* rescale */
84 ans = (unsigned int) ( base*(1.+sum) );
85 D(("random number: base=%u -> ans=%u\n", base, ans));
90 /* **********************************************************************
91 * the following function sleeps for a random time. The actual time
92 * slept is computed above.. It is based on the requested time but will
93 * differ by up to +/- 25%.
96 void _pam_await_timer(pam_handle_t *pamh, int status)
101 delay = _pam_compute_delay(pamh->fail_delay.begin,
102 pamh->fail_delay.delay);
103 if (pamh->fail_delay.delay_fn_ptr) {
106 void (*fn)(int, unsigned, void *);
110 if (pamh->pam_conversation) {
111 appdata_ptr = pamh->pam_conversation->appdata_ptr;
116 /* always call the applications delay function, even if
117 the delay is zero - indicate status */
118 hack_fn_u.value = pamh->fail_delay.delay_fn_ptr;
119 hack_fn_u.fn(status, delay, appdata_ptr);
121 } else if (status != PAM_SUCCESS && pamh->fail_delay.set) {
123 D(("will wait %u usec", delay));
128 tval.tv_sec = delay / 1000000;
129 tval.tv_usec = delay % 1000000;
130 select(0, NULL, NULL, NULL, &tval);
134 _pam_reset_timer(pamh);
138 /* **********************************************************************
139 * this function is known to both the module and the application, it
140 * keeps a running score of the largest-requested delay so far, as
141 * specified by either modules or an application.
144 int pam_fail_delay(pam_handle_t *pamh, unsigned int usec)
148 IF_NO_PAMH("pam_fail_delay", pamh, PAM_SYSTEM_ERR);
150 D(("setting delay to %u",usec));
152 if (pamh->fail_delay.set) {
153 largest = pamh->fail_delay.delay;
155 pamh->fail_delay.set = PAM_TRUE;
159 D(("largest = %u",largest));
161 if (largest < usec) {
162 D(("resetting largest delay"));
163 pamh->fail_delay.delay = usec;