#
lookback_steps = 1
+ #
+ # lookforward_steps:: How many steps forward in time we look for a matching OTP.
+ #
+ lookforward_steps = 0
+
#
# lookback_interval:: Time delta between steps.
#
{ FR_CONF_OFFSET("otp_length", rlm_totp_t, totp.otp_length), .dflt = "6" },
{ FR_CONF_OFFSET("lookback_steps", rlm_totp_t, totp.lookback_steps), .dflt = "1" },
{ FR_CONF_OFFSET("lookback_interval", rlm_totp_t, totp.lookback_interval), .dflt = "30" },
+ { FR_CONF_OFFSET("lookforward_steps", rlm_totp_t, totp.lookforward_steps), .dflt = "0" },
CONF_PARSER_TERMINATOR
};
FR_INTEGER_BOUND_CHECK("lookback_steps", inst->totp.lookback_steps, >=, 1);
FR_INTEGER_BOUND_CHECK("lookback_steps", inst->totp.lookback_steps, <=, 10);
+ FR_INTEGER_BOUND_CHECK("lookforward_steps", inst->totp.lookforward_steps, <=, 10);
+
FR_INTEGER_BOUND_CHECK("lookback_interval", inst->totp.lookback_interval, <=, inst->totp.time_step);
FR_INTEGER_BOUND_CHECK("otp_length", inst->totp.otp_length, >=, 6);
*/
int fr_totp_cmp(fr_totp_t const *cfg, request_t *request, time_t now, uint8_t const *key, size_t keylen, char const *totp)
{
- time_t then;
- unsigned int i;
- uint8_t offset;
- uint32_t challenge;
- uint64_t padded;
- uint8_t data[8];
- uint8_t digest[SHA1_DIGEST_LENGTH];
- char buffer[9];
+ time_t diff, then;
+ uint32_t steps;
+ unsigned int i;
+ uint8_t offset;
+ uint32_t challenge;
+ uint64_t padded;
+ uint8_t data[8];
+ uint8_t digest[SHA1_DIGEST_LENGTH];
+ char buffer[9];
fr_assert(cfg->otp_length == 6 || cfg->otp_length == 8);
/*
* First try to authenticate against the current OTP, then step
- * back in increments of `lookback_interval`, up to `lookback_steps` times,
+ * back and forwards in increments of `lookback_interval`, up to `lookback_steps` times,
* to authenticate properly in cases of long transit delay, as
* described in RFC 6238, section 5.2.
*/
-
- for (i = 0, then = now; i <= cfg->lookback_steps; i++, then -= cfg->lookback_interval) {
+ steps = cfg->lookback_steps > cfg->lookforward_steps ? cfg->lookback_steps : cfg->lookforward_steps;
+ for (i = 0, diff = 0; i <= steps; i++, diff += cfg->lookback_interval) {
+ if (i > cfg->lookback_steps) goto forwards;
+ then = now - diff;
+ repeat:
padded = ((uint64_t) then) / cfg->time_step;
data[0] = padded >> 56;
data[1] = padded >> 48;
}
if (fr_digest_cmp((uint8_t const *) buffer, (uint8_t const *) totp, cfg->otp_length) == 0) return 0;
+
+ /*
+ * We've tested backwards, now do the equivalent time slot forwards
+ */
+ if ((then < now) && (i <= cfg->lookforward_steps)) {
+ forwards:
+ then = now + offset;
+ goto repeat;
+ }
}
return -1;
uint32_t otp_length; //!< forced to 6 or 8
uint32_t lookback_steps; //!< number of steps to look back
uint32_t lookback_interval; //!< interval in seconds between steps
+ uint32_t lookforward_steps; //!< number of steps to look forwards
} fr_totp_t;
int fr_totp_cmp(fr_totp_t const *cfg, request_t *request, time_t now, uint8_t const *key, size_t keylen, char const *totp) CC_HINT(nonnull(1,4,6));
totp {
+ lookforward_steps = 1
}
exec {