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)