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