]> granicus.if.org Git - linux-pam/blob - libpam/pam_delay.c
Relevant BUGIDs: 109219
[linux-pam] / libpam / pam_delay.c
1 /*
2  * pam_delay.c
3  *
4  * Copyright (c) Andrew G. Morgan <morgan@linux.kernel.org> 1996-9
5  * All rights reserved.
6  *
7  * $Id$
8  *
9  * $Log$
10  * Revision 1.1  2000/06/20 22:11:14  agmorgan
11  * Initial revision
12  *
13  * Revision 1.2  1999/07/04 23:23:42  morgan
14  * add appdata_ptr to app callback function
15  *
16  * Revision 1.1.1.1  1998/07/12 05:17:15  morgan
17  * Linux PAM sources pre-0.66
18  *
19  */
20
21 /*
22  * This is a simple implementation of a delay on failure mechanism; an
23  * attempt to overcome authentication-time attacks in a simple manner.
24  */
25
26 #include <unistd.h>
27 #include "pam_private.h"
28
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.
32  */
33
34 void _pam_reset_timer(pam_handle_t *pamh)
35 {
36      D(("setting pamh->fail_delay.set to FALSE"));
37      pamh->fail_delay.set = PAM_FALSE;
38 }
39
40 /* **********************************************************************
41  * this function sets the start time for possible delayed failing.
42  *
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...
46  */
47
48 void _pam_start_timer(pam_handle_t *pamh)
49 {
50      pamh->fail_delay.begin = time(NULL);
51      D(("starting timer..."));
52 }
53
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.
62  *
63  * /dev/random might be a better place to look for some numbers...
64  */
65
66 static unsigned int _pam_rand(unsigned int seed)
67 {
68 #define N1 1664525
69 #define N2 1013904223
70      return N1*seed + N2;
71 }
72
73 static unsigned int _pam_compute_delay(unsigned int seed, unsigned int base)
74 {
75      int i;
76      double sum;
77      unsigned int ans;
78
79      for (sum=i=0; i<3; ++i) {
80           seed = _pam_rand(seed);
81           sum += (double) ((seed / 10) % 1000000);
82      }
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));
86
87      return ans;
88 }
89
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%.
94  */
95
96 void _pam_await_timer(pam_handle_t *pamh, int status)
97 {
98     unsigned int delay;
99     D(("waiting?..."));
100
101     delay = _pam_compute_delay(pamh->fail_delay.begin,
102                                pamh->fail_delay.delay);
103     if (pamh->fail_delay.delay_fn_ptr) {
104         union {
105             const void *value;
106             void (*fn)(int, unsigned, void *);
107         } hack_fn_u;
108         void *appdata_ptr;
109
110         if (pamh->pam_conversation) {
111             appdata_ptr = pamh->pam_conversation->appdata_ptr;
112         } else {
113             appdata_ptr = NULL;
114         }
115
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);
120
121     } else if (status != PAM_SUCCESS && pamh->fail_delay.set) {
122
123         D(("will wait %u usec", delay));
124
125         if (delay > 0) {
126             struct timeval tval;
127
128             tval.tv_sec  = delay / 1000000;
129             tval.tv_usec = delay % 1000000;
130             select(0, NULL, NULL, NULL, &tval);
131         }
132     }
133
134     _pam_reset_timer(pamh);
135     D(("waiting done"));
136 }
137
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.
142  */
143
144 int pam_fail_delay(pam_handle_t *pamh, unsigned int usec)
145 {
146      int largest;
147
148      IF_NO_PAMH("pam_fail_delay", pamh, PAM_SYSTEM_ERR);
149
150      D(("setting delay to %u",usec));
151
152      if (pamh->fail_delay.set) {
153           largest = pamh->fail_delay.delay;
154      } else {
155           pamh->fail_delay.set = PAM_TRUE;
156           largest = 0;
157      }
158
159      D(("largest = %u",largest));
160
161      if (largest < usec) {
162           D(("resetting largest delay"));
163           pamh->fail_delay.delay = usec;
164      }
165
166      return PAM_SUCCESS;
167 }
168