summaryrefslogtreecommitdiff
path: root/libpam/pam_delay.c
blob: a9cfa8028f4c11a89804d3c82808000cbe8f794f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
 * pam_delay.c
 *
 * Copyright (c) Andrew G. Morgan <morgan@kernel.org> 1996-9
 * All rights reserved.
 *
 * $Id$
 *
 */

/*
 * This is a simple implementation of a delay on failure mechanism; an
 * attempt to overcome authentication-time attacks in a simple manner.
 */

#include "pam_private.h"
#include <unistd.h>
#include <time.h>

/* **********************************************************************
 * initialize the time as unset, this is set on the return from the
 * authenticating pair of of the libpam pam_XXX calls.
 */

void _pam_reset_timer(pam_handle_t *pamh)
{
     D(("setting pamh->fail_delay.set to FALSE"));
     pamh->fail_delay.set = PAM_FALSE;
}

/* **********************************************************************
 * this function sets the start time for possible delayed failing.
 *
 * Eventually, it may set the timer so libpam knows how long the program
 * has already been executing. Currently, this value is used to seed
 * a pseudo-random number generator...
 */

void _pam_start_timer(pam_handle_t *pamh)
{
     pamh->fail_delay.begin = time(NULL);
     D(("starting timer..."));
}

/* *******************************************************************
 * Compute a pseudo random time. The value is base*(1 +/- 1/5) where
 * the distribution is pseudo gausian (the sum of three evenly
 * distributed random numbers -- central limit theorem and all ;^) The
 * linear random numbers are based on a formulae given in Knuth's
 * Seminumerical recipies that was reproduced in `Numerical Recipies
 * in C'. It is *not* a cryptographically strong generator, but it is
 * probably "good enough" for our purposes here.
 *
 * /dev/random might be a better place to look for some numbers...
 */

static unsigned int _pam_rand(unsigned int seed)
{
#define N1 1664525
#define N2 1013904223
     return N1*seed + N2;
}

static unsigned int _pam_compute_delay(unsigned int seed, unsigned int base)
{
     int i;
     double sum;
     unsigned int ans;

     for (sum=i=0; i<3; ++i) {
	  seed = _pam_rand(seed);
	  sum += (double) ((seed / 10) % 1000000);
     }
     sum = (sum/3.)/1e6 - .5;                      /* rescale */
     ans = (unsigned int) ( base*(1.+sum) );
     D(("random number: base=%u -> ans=%u\n", base, ans));

     return ans;
}

/* **********************************************************************
 * the following function sleeps for a random time. The actual time
 * slept is computed above.. It is based on the requested time but will
 * differ by up to +/- 25%.
 */

void _pam_await_timer(pam_handle_t *pamh, int status)
{
    unsigned int delay;
    D(("waiting?..."));

    delay = _pam_compute_delay(pamh->fail_delay.begin,
			       pamh->fail_delay.delay);
    if (pamh->fail_delay.delay_fn_ptr) {
	union {
	    const void *value;
	    void (*fn)(int, unsigned, void *);
	} hack_fn_u;
	void *appdata_ptr;

	if (pamh->pam_conversation) {
	    appdata_ptr = pamh->pam_conversation->appdata_ptr;
	} else {
	    appdata_ptr = NULL;
	}

	/* always call the applications delay function, even if
	   the delay is zero - indicate status */
	hack_fn_u.value = pamh->fail_delay.delay_fn_ptr;
	hack_fn_u.fn(status, delay, appdata_ptr);

    } else if (status != PAM_SUCCESS && pamh->fail_delay.set) {

	D(("will wait %u usec", delay));

	if (delay > 0) {
	    struct timeval tval;

	    tval.tv_sec  = delay / 1000000;
	    tval.tv_usec = delay % 1000000;
	    select(0, NULL, NULL, NULL, &tval);
	}
    }

    _pam_reset_timer(pamh);
    D(("waiting done"));
}

/* **********************************************************************
 * this function is known to both the module and the application, it
 * keeps a running score of the largest-requested delay so far, as
 * specified by either modules or an application.
 */

int pam_fail_delay(pam_handle_t *pamh, unsigned int usec)
{
     unsigned int largest;

     IF_NO_PAMH("pam_fail_delay", pamh, PAM_SYSTEM_ERR);

     D(("setting delay to %u",usec));

     if (pamh->fail_delay.set) {
          largest = pamh->fail_delay.delay;
     } else {
	  pamh->fail_delay.set = PAM_TRUE;
          largest = 0;
     }

     D(("largest = %u",largest));

     if (largest < usec) {
          D(("resetting largest delay"));
	  pamh->fail_delay.delay = usec;
     }

     return PAM_SUCCESS;
}