]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add `lookforward_steps` option to `rlm_totp`
authorNick Porter <nick@portercomputing.co.uk>
Thu, 2 May 2024 10:15:48 +0000 (11:15 +0100)
committerNick Porter <nick@portercomputing.co.uk>
Thu, 2 May 2024 10:34:47 +0000 (11:34 +0100)
Helps with clock skew between client and server (and module test where
the OTP is calcuated using an exec call after the packet starts
processing)

raddb/mods-available/totp
src/modules/rlm_totp/rlm_totp.c
src/modules/rlm_totp/totp.c
src/modules/rlm_totp/totp.h
src/tests/modules/totp/module.conf

index 22eabe8dc370e4bf3e8f52ba3df429af022f0228..d1dc0cc20b2a0510b554d122846dfa7d9e8698e4 100644 (file)
@@ -66,6 +66,11 @@ totp {
        #
        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.
        #
index 4325bdbed6790d51182bd22832a22ec795130b0d..8a292c3a3b7f06e78b647e9f4b420b34f42efbc9 100644 (file)
@@ -67,6 +67,7 @@ static const conf_parser_t module_config[] = {
        { 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
 };
 
@@ -101,6 +102,8 @@ static int mod_instantiate(module_inst_ctx_t const *mctx)
        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);
index 91b9cfcd421718387e7464ac16e9bb4b10f0c92e..76f4c30578c6df7937169cc70bed018df6780cf5 100644 (file)
@@ -70,14 +70,15 @@ static void totp_log(char const *fmt, ...)
  */
 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);
 
@@ -99,12 +100,15 @@ int fr_totp_cmp(fr_totp_t const *cfg, request_t *request, time_t now, uint8_t co
 
        /*
         *      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;
@@ -153,6 +157,15 @@ int fr_totp_cmp(fr_totp_t const *cfg, request_t *request, time_t now, uint8_t co
                }
 
                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;
index 33503b4e7be646c04348594a5107cfa0ebd94445..cb7c7d456fd857d1ec64f8175990083ada9e7c95 100644 (file)
@@ -35,6 +35,7 @@ typedef struct {
        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));
index a6f27232cd40bef7f1f4c52d872b40fdef21bb96..91fd59845e448b300ed95acd02160caebdaa33e3 100644 (file)
@@ -1,4 +1,5 @@
 totp {
+       lookforward_steps = 1
 }
 
 exec {