]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add lookback capability to address transit delay
authorMargaret Cullen <margaret@painless-security.com>
Fri, 11 Aug 2023 22:47:58 +0000 (18:47 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Wed, 16 Aug 2023 12:45:53 +0000 (08:45 -0400)
Signed-off-by: Margaret Cullen <margaret@painless-security.com>
src/modules/rlm_totp/rlm_totp.c

index c9c4709c90df4b0cececd67e658dffa6d4fb8b8b..32328b0fcc825e371b5e8a78aa6fb8a6a3a585f3 100644 (file)
@@ -29,6 +29,8 @@ RCSID("$Id$")
 #include <freeradius-devel/rad_assert.h>
 
 #define TIME_STEP (30)
+#define BACK_STEPS (1)
+#define BACK_STEP_SECS (30)
 
 /*
  *     RFC 4648 base32 decoding.
@@ -174,45 +176,57 @@ static int totp_cmp(TESTING_UNUSED REQUEST *request, time_t now, uint8_t const *
        char buffer[9];
        uint8_t data[8];
        uint8_t digest[SHA1_DIGEST_LENGTH];
-
-       padded = ((uint64_t) now) / TIME_STEP;
-       data[0] = padded >> 56;
-       data[1] = padded >> 48;
-       data[2] = padded >> 40;
-       data[3] = padded >> 32;
-       data[4] = padded >> 24;
-       data[5] = padded >> 16;
-       data[6] = padded >> 8;
-       data[7] = padded & 0xff;
-
-       /*
-        *      Encrypt the network order time with the key.
-        */
-       fr_hmac_sha1(digest, data, 8, key, keylen);
-
-       /*
-        *      Take the least significant 4 bits.
-        */
-       offset = digest[SHA1_DIGEST_LENGTH - 1] & 0x0f;
-
-       /*
-        *      Grab the 32bits at "offset", and drop the high bit.
-        */
-       challenge = (digest[offset] & 0x7f) << 24;
-       challenge |= digest[offset + 1] << 16;
-       challenge |= digest[offset + 2] << 8;
-       challenge |= digest[offset + 3];
+       time_t then;
+       int i;
 
        /*
-        *      The token is the last 6 digits in the number.
+        *      First try to authenticate against the current OTP, then step
+        *      back in increments of BACK_STEP_SECS, up to BACK_STEPS times,
+        *      to authenticate properly in cases of long transit delay, as
+        *      described in RFC 6238, secion 5.2.
         */
-       snprintf(buffer, sizeof(buffer), PRINT, challenge % DIV);
-
-       RDEBUG3("Time %zu", (size_t) now);
-       RDEBUG3("Expected %s", buffer);
-       RDEBUG3("Received %s", totp);
-
-       return rad_digest_cmp((uint8_t const *) buffer, (uint8_t const *) totp, LEN);
+       for (i = 0, then = now; i <= BACK_STEPS; i++, then -= BACK_STEP_SECS) {
+              padded = (uint64_t) then / TIME_STEP;
+              data[0] = padded >> 56;
+              data[1] = padded >> 48;
+              data[2] = padded >> 40;
+              data[3] = padded >> 32;
+              data[4] = padded >> 24;
+              data[5] = padded >> 16;
+              data[6] = padded >> 8;
+              data[7] = padded & 0xff;
+
+              /*
+               *       Encrypt the network order time with the key.
+               */
+              fr_hmac_sha1(digest, data, 8, key, keylen);
+
+              /*
+               *       Take the least significant 4 bits.
+               */
+              offset = digest[SHA1_DIGEST_LENGTH - 1] & 0x0f;
+
+              /*
+               *       Grab the 32bits at "offset", and drop the high bit.
+               */
+              challenge = (digest[offset] & 0x7f) << 24;
+              challenge |= digest[offset + 1] << 16;
+              challenge |= digest[offset + 2] << 8;
+              challenge |= digest[offset + 3];
+
+              /*
+               *       The token is the last 6 digits in the number (or 8 for testing)..
+               */
+              snprintf(buffer, sizeof(buffer), PRINT, challenge % DIV);
+
+              RDEBUG3("Now: %zu, Then: %zu", (size_t) now, (size_t) then);
+              RDEBUG3("Expected %s", buffer);
+              RDEBUG3("Received %s", totp);
+
+              if (rad_digest_cmp((uint8_t const *) buffer, (uint8_t const *) totp, LEN) == 0)
+                     return 0;
+       }
+       return 1;
 }
 
 #ifndef TESTING
@@ -226,7 +240,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
        uint8_t const *key;
        size_t keylen;
        uint8_t buffer[80];     /* multiple of 5*8 characters */
-
+       uint64_t now = time(NULL);
 
        password = fr_pair_find_by_num(request->packet->vps, PW_TOTP_PASSWORD, 0, TAG_ANY);
        if (!password) return RLM_MODULE_NOOP;
@@ -260,9 +274,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
                keylen = len;
        }
 
-       if (totp_cmp(request, time(NULL), key, keylen, password->vp_strvalue) != 0) return RLM_MODULE_FAIL;
-
-       return RLM_MODULE_OK;
+       if (totp_cmp(request, now, key, keylen, password->vp_strvalue) == 0)
+                    return RLM_MODULE_OK;
+       }
+       return RLM_MODULE_FAIL;
 }
 
 
@@ -327,8 +342,12 @@ int main(int argc, char **argv)
                        (void) sscanf(argv[2], "%llu", &now);
                }
 
-               if (totp_cmp(NULL, (time_t) now, (uint8_t const *) argv[3], strlen(argv[3]), argv[4]) == 0) {
-                       return 0;
+               printf ("=== Time = %llu, TIME_STEP = %d, BACK_STEPS = %d, BACK_STEP_SECS = %d ===\n",
+                        now, TIME_STEP, BACK_STEPS, BACK_STEP_SECS);
+
+               if (totp_cmp(NULL, (time_t) now, (uint8_t const *) argv[3],
+                            strlen(argv[3]), argv[4]) == 0) {
+                      return 0;
                }
                printf("Fail\n");
                return 1;