Initial import from FreeBSD RELENG_4:
[games.git] / contrib / libpam / libpam / pam_delay.c
1 /*
2  * pam_delay.c
3  *
4  * Copyright (c) Andrew G. Morgan <morgan@linux.kernel.org> 1996-8
5  * All rights reserved.
6  *
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 $
9  *
10  * $Log: pam_delay.c,v $
11  */
12
13 /*
14  * This is a simple implementation of a delay on failure mechanism; an
15  * attempt to overcome authentication-time attacks in a simple manner.
16  */
17
18 #include <unistd.h>
19 #include "pam_private.h"
20
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.
24  */
25
26 void _pam_reset_timer(pam_handle_t *pamh)
27 {
28      D(("setting pamh->fail_delay.set to FALSE"));
29      pamh->fail_delay.set = PAM_FALSE;
30 }
31
32 /* **********************************************************************
33  * this function sets the start time for possible delayed failing.
34  *
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...
38  */
39
40 void _pam_start_timer(pam_handle_t *pamh)
41 {
42      pamh->fail_delay.begin = time(NULL);
43      D(("starting timer..."));
44 }
45
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.
54  *
55  * /dev/random might be a better place to look for some numbers...
56  */
57
58 static unsigned int _pam_rand(unsigned int seed)
59 {
60 #define N1 1664525
61 #define N2 1013904223
62      return N1*seed + N2;
63 }
64
65 static unsigned int _pam_compute_delay(unsigned int seed, unsigned int base)
66 {
67      int i;
68      double sum;
69      unsigned int ans;
70
71      for (sum=i=0; i<3; ++i) {
72           seed = _pam_rand(seed);
73           sum += (double) ((seed / 10) % 1000000);
74      }
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));
78
79      return ans;
80 }
81
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%.
86  */
87
88 void _pam_await_timer(pam_handle_t *pamh, int status)
89 {
90     unsigned int delay;
91     D(("waiting?..."));
92
93     delay = _pam_compute_delay(pamh->fail_delay.begin,
94                                pamh->fail_delay.delay);
95     if (pamh->fail_delay.delay_fn_ptr) {
96         union {
97             const void *value;
98             void (*fn)(int, unsigned);
99         } hack_fn_u;
100
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);
105
106     } else if (status != PAM_SUCCESS && pamh->fail_delay.set) {
107
108         D(("will wait %u usec", delay));
109
110         if (delay > 0) {
111             struct timeval tval;
112
113             tval.tv_sec  = delay / 1000000;
114             tval.tv_usec = delay % 1000000;
115             select(0, NULL, NULL, NULL, &tval);
116         }
117     }
118
119     _pam_reset_timer(pamh);
120     D(("waiting done"));
121 }
122
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.
127  */
128
129 int pam_fail_delay(pam_handle_t *pamh, unsigned int usec)
130 {
131      int largest;
132
133      IF_NO_PAMH("pam_fail_delay", pamh, PAM_SYSTEM_ERR);
134
135      D(("setting delay to %u",usec));
136
137      if (pamh->fail_delay.set) {
138           largest = pamh->fail_delay.delay;
139      } else {
140           pamh->fail_delay.set = PAM_TRUE;
141           largest = 0;
142      }
143
144      D(("largest = %u",largest));
145
146      if (largest < usec) {
147           D(("resetting largest delay"));
148           pamh->fail_delay.delay = usec;
149      }
150
151      return PAM_SUCCESS;
152 }
153