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: pam_delay.c 274 2005-07-13 09:52:25Z vorlon $
*
*/
/*
* 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;
}
|