(root)/
Linux-PAM-1.5.3/
libpam/
pam_delay.c
       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  }