Hello everyone,
I'm using pam_oath for authentication with an apache webserver to sync
some files via WebDAV. Basic authentication over TLS is used for this.
Entering a new OTP on every access of a web-resource (using a TOTP
token) does obviously not work well. So I created a patch that allows a
time (in seconds) to be set where the old OTP is considered valid. This
reduces security a bit, but has the advantage of working really well
with WebDAV or other protocols that authenticate the user over and over
again.
Even for VPN users this may be an option to allow the VPN client to
automatically reconnect within a specified time range without forcing
the user to generate a new OTP value.
If the linger time is within a few minutes the reduction in security
should be acceptable. Even if someone uses a keylogger to get the OTP
value, he has to be quick before the linger time is over and the OTP
isn't accepted anymore.
I've attached the patch (changing pam_oath.c and the README). Maybe
someone finds this feature useful.
Greetings,
Daniel
diff --git a/pam_oath/README b/pam_oath/README
index bef4265..ded0283 100644
--- a/pam_oath/README
+++ b/pam_oath/README
@@ -101,7 +101,7 @@ follows:
---------
# cat /etc/users.oath
-HOTP root - 00 0 328482 2009-12-07T23:23:49L
+HOTP root - 00 0 328482 2009-12-07T23:23:49L
#
---------
@@ -224,6 +224,13 @@ List of all parameters
"window": Specify search depth, an integer typically from 5 to 50
but other values can be useful too.
+ "linger_time": Specify how long the last used OTP value will be
+ accepted. This is especially necessary for
+ services that reauthenticate every use of a
+ ressource (eg. webservers with basic auth).
+ BEWARE: This will reduce the security of the OTP
+ value because it's not really one-time anymore.
+
SSH Configuration
-----------------
diff --git a/pam_oath/pam_oath.c b/pam_oath/pam_oath.c
index 2820318..c304a8e 100644
--- a/pam_oath/pam_oath.c
+++ b/pam_oath/pam_oath.c
@@ -69,6 +69,7 @@ struct cfg
int alwaysok;
int try_first_pass;
int use_first_pass;
+ unsigned linger_time;
char *usersfile;
unsigned digits;
unsigned window;
@@ -86,6 +87,7 @@ parse_cfg (int flags, int argc, const char **argv, struct cfg
*cfg)
cfg->usersfile = NULL;
cfg->digits = -1;
cfg->window = 5;
+ cfg->linger_time = 0;
for (i = 0; i < argc; i++)
{
@@ -103,6 +105,8 @@ parse_cfg (int flags, int argc, const char **argv, struct
cfg *cfg)
cfg->digits = atoi (argv[i] + 7);
if (strncmp (argv[i], "window=", 7) == 0)
cfg->window = atoi (argv[i] + 7);
+ if (strncmp (argv[i], "linger_time=", 12) == 0)
+ cfg->linger_time = atoi (argv[i] + 12);
}
if (cfg->digits != 6 && cfg->digits != 7 && cfg->digits != 8)
@@ -126,6 +130,7 @@ parse_cfg (int flags, int argc, const char **argv, struct
cfg *cfg)
D (("usersfile=%s", cfg->usersfile ? cfg->usersfile : "(null)"));
D (("digits=%d", cfg->digits));
D (("window=%d", cfg->window));
+ D (("linger_time=%d", cfg->linger_time));
}
}
@@ -310,6 +315,22 @@ pam_sm_authenticate (pam_handle_t * pamh,
DBG (("authenticate rc %d (%s: %s) last otp %s", rc,
oath_strerror_name (rc) ? oath_strerror_name (rc) : "UNKNOWN",
oath_strerror (rc), ctime (&last_otp)));
+
+ if ((rc == OATH_REPLAYED_OTP) && (cfg.linger_time != 0))
+ {
+ time_t now;
+ if (time(&now) != (time_t)-1)
+ {
+ int last_otp_timediff = (int)difftime(now, last_otp);
+ if (last_otp_timediff <= cfg.linger_time)
+ {
+ DBG(("One-time password replayed. Time since first use %d of
%d seconds linger time.", last_otp_timediff, cfg.linger_time));
+
+ retval = PAM_SUCCESS;
+ goto done;
+ }
+ }
+ }
}
if (rc != OATH_OK)