123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- /*
- * 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 gaussian (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 recipes that was reproduced in `Numerical Recipes
- * 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;
- }
- /* **********************************************************************
- * By default, 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 +/- 50%. If the PAM_FAIL_DELAY item is
- * set by the client, this function will call the function referenced by
- * that item, overriding the default behavior.
- */
- 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;
- }
|