summaryrefslogtreecommitdiffstats
path: root/libpam/pam_delay.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpam/pam_delay.c')
-rw-r--r--libpam/pam_delay.c160
1 files changed, 160 insertions, 0 deletions
diff --git a/libpam/pam_delay.c b/libpam/pam_delay.c
new file mode 100644
index 0000000..549da89
--- /dev/null
+++ b/libpam/pam_delay.c
@@ -0,0 +1,160 @@
+/*
+ * 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;
+}