4 * Copyright (c) Andrew G. Morgan <morgan@linux.kernel.org> 1996-8
7 * $Id: pam_delay.c,v 1.5 1997/04/05 06:54:19 morgan Exp $
8 * $FreeBSD: src/contrib/libpam/libpam/pam_delay.c,v 1.1.1.1.6.2 2001/06/11 15:28:12 markm Exp $
10 * $Log: pam_delay.c,v $
14 * This is a simple implementation of a delay on failure mechanism; an
15 * attempt to overcome authentication-time attacks in a simple manner.
19 #include "pam_private.h"
21 /* **********************************************************************
22 * initialize the time as unset, this is set on the return from the
23 * authenticating pair of of the libpam pam_XXX calls.
26 void _pam_reset_timer(pam_handle_t *pamh)
28 D(("setting pamh->fail_delay.set to FALSE"));
29 pamh->fail_delay.set = PAM_FALSE;
32 /* **********************************************************************
33 * this function sets the start time for possible delayed failing.
35 * Eventually, it may set the timer so libpam knows how long the program
36 * has already been executing. Currently, this value is used to seed
37 * a pseudo-random number generator...
40 void _pam_start_timer(pam_handle_t *pamh)
42 pamh->fail_delay.begin = time(NULL);
43 D(("starting timer..."));
46 /* *******************************************************************
47 * Compute a pseudo random time. The value is base*(1 +/- 1/5) where
48 * the distribution is pseudo gausian (the sum of three evenly
49 * distributed random numbers -- central limit theorem and all ;^) The
50 * linear random numbers are based on a formulae given in Knuth's
51 * Seminumerical recipies that was reproduced in `Numerical Recipies
52 * in C'. It is *not* a cryptographically strong generator, but it is
53 * probably "good enough" for our purposes here.
55 * /dev/random might be a better place to look for some numbers...
58 static unsigned int _pam_rand(unsigned int seed)
65 static unsigned int _pam_compute_delay(unsigned int seed, unsigned int base)
71 for (sum=i=0; i<3; ++i) {
72 seed = _pam_rand(seed);
73 sum += (double) ((seed / 10) % 1000000);
75 sum = (sum/3.)/1e6 - .5; /* rescale */
76 ans = (unsigned int) ( base*(1.+sum) );
77 D(("random number: base=%u -> ans=%u\n", base, ans));
82 /* **********************************************************************
83 * the following function sleeps for a random time. The actual time
84 * slept is computed above.. It is based on the requested time but will
85 * differ by up to +/- 25%.
88 void _pam_await_timer(pam_handle_t *pamh, int status)
93 delay = _pam_compute_delay(pamh->fail_delay.begin,
94 pamh->fail_delay.delay);
95 if (pamh->fail_delay.delay_fn_ptr) {
98 void (*fn)(int, unsigned);
101 /* always call the applications delay function, even if
102 the delay is zero - indicate status */
103 hack_fn_u.value = pamh->fail_delay.delay_fn_ptr;
104 hack_fn_u.fn(status, delay);
106 } else if (status != PAM_SUCCESS && pamh->fail_delay.set) {
108 D(("will wait %u usec", delay));
113 tval.tv_sec = delay / 1000000;
114 tval.tv_usec = delay % 1000000;
115 select(0, NULL, NULL, NULL, &tval);
119 _pam_reset_timer(pamh);
123 /* **********************************************************************
124 * this function is known to both the module and the application, it
125 * keeps a running score of the largest-requested delay so far, as
126 * specified by either modules or an application.
129 int pam_fail_delay(pam_handle_t *pamh, unsigned int usec)
133 IF_NO_PAMH("pam_fail_delay", pamh, PAM_SYSTEM_ERR);
135 D(("setting delay to %u",usec));
137 if (pamh->fail_delay.set) {
138 largest = pamh->fail_delay.delay;
140 pamh->fail_delay.set = PAM_TRUE;
144 D(("largest = %u",largest));
146 if (largest < usec) {
147 D(("resetting largest delay"));
148 pamh->fail_delay.delay = usec;