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 "pam_private.h"
17 #include <unistd.h>
18 #include <time.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 gaussian (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 recipes that was reproduced in `Numerical Recipes
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 * By default, the following function sleeps for a random time. The
83 * actual time slept is computed above. It is based on the requested
84 * time but will differ by up to +/- 50%. If the PAM_FAIL_DELAY item is
85 * set by the client, this function will call the function referenced by
86 * that item, overriding the default behavior.
87 */
88
89 void _pam_await_timer(pam_handle_t *pamh, int status)
90 {
91 unsigned int delay;
92 D(("waiting?..."));
93
94 delay = _pam_compute_delay(pamh->fail_delay.begin,
95 pamh->fail_delay.delay);
96 if (pamh->fail_delay.delay_fn_ptr) {
97 union {
98 const void *value;
99 void (*fn)(int, unsigned, void *);
100 } hack_fn_u;
101 void *appdata_ptr;
102
103 if (pamh->pam_conversation) {
104 appdata_ptr = pamh->pam_conversation->appdata_ptr;
105 } else {
106 appdata_ptr = NULL;
107 }
108
109 /* always call the applications delay function, even if
110 the delay is zero - indicate status */
111 hack_fn_u.value = pamh->fail_delay.delay_fn_ptr;
112 hack_fn_u.fn(status, delay, appdata_ptr);
113
114 } else if (status != PAM_SUCCESS && pamh->fail_delay.set) {
115
116 D(("will wait %u usec", delay));
117
118 if (delay > 0) {
119 struct timeval tval;
120
121 tval.tv_sec = delay / 1000000;
122 tval.tv_usec = delay % 1000000;
123 select(0, NULL, NULL, NULL, &tval);
124 }
125 }
126
127 _pam_reset_timer(pamh);
128 D(("waiting done"));
129 }
130
131 /* **********************************************************************
132 * this function is known to both the module and the application, it
133 * keeps a running score of the largest-requested delay so far, as
134 * specified by either modules or an application.
135 */
136
137 int pam_fail_delay(pam_handle_t *pamh, unsigned int usec)
138 {
139 unsigned int largest;
140
141 IF_NO_PAMH("pam_fail_delay", pamh, PAM_SYSTEM_ERR);
142
143 D(("setting delay to %u",usec));
144
145 if (pamh->fail_delay.set) {
146 largest = pamh->fail_delay.delay;
147 } else {
148 pamh->fail_delay.set = PAM_TRUE;
149 largest = 0;
150 }
151
152 D(("largest = %u",largest));
153
154 if (largest < usec) {
155 D(("resetting largest delay"));
156 pamh->fail_delay.delay = usec;
157 }
158
159 return PAM_SUCCESS;
160 }