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