This prevents the reuse of TOTP tokens by recording the last token
interval that was used. This will be replicated as normal. However,
this patch does not increase the number of writes to the database
in the standard authentication case. This is because it also
eliminates an unnecessary write during authentication. Hence, this
patch should be write-load neutral with the existing code.

Further performance enhancement is desired, but is outside the
scope of this patch.

https://fedorahosted.org/freeipa/ticket/4410
From 1e49cab9ad09e2f4dcdef4cb35ba872d021eafa9 Mon Sep 17 00:00:00 2001
From: Nathaniel McCallum <npmccal...@redhat.com>
Date: Fri, 11 Jul 2014 12:29:58 -0400
Subject: [PATCH] Add TOTP watermark support

This prevents the reuse of TOTP tokens by recording the last token
interval that was used. This will be replicated as normal. However,
this patch does not increase the number of writes to the database
in the standard authentication case. This is because it also
eliminates an unnecessary write during authentication. Hence, this
patch should be write-load neutral with the existing code.

Further performance enhancement is desired, but is outside the
scope of this patch.

https://fedorahosted.org/freeipa/ticket/4410
---
 daemons/ipa-slapi-plugins/libotp/libotp.c | 169 ++++++++++++++++--------------
 install/share/70ipaotp.ldif               |   3 +-
 2 files changed, 93 insertions(+), 79 deletions(-)

diff --git a/daemons/ipa-slapi-plugins/libotp/libotp.c b/daemons/ipa-slapi-plugins/libotp/libotp.c
index eeead43e35c9cbde473c54efcf6482d38e6329b3..41f9e7b4809fbca82452d260b9aa7d1d3059fd2e 100644
--- a/daemons/ipa-slapi-plugins/libotp/libotp.c
+++ b/daemons/ipa-slapi-plugins/libotp/libotp.c
@@ -66,6 +66,7 @@ struct otptoken {
     enum otptoken_type type;
     union {
         struct {
+            uint64_t watermark;
             unsigned int step;
             int offset;
         } totp;
@@ -123,69 +124,21 @@ static const struct berval *entry_attr_get_berval(const Slapi_Entry* e,
     return slapi_value_get_berval(v);
 }
 
-static bool validate(struct otptoken *token, time_t now, ssize_t step,
-                     uint32_t first, const uint32_t *second)
-{
-    uint32_t tmp;
-
-    switch (token->type) {
-    case OTPTOKEN_TOTP:
-        step = (now + token->totp.offset) / token->totp.step + step;
-        break;
-    case OTPTOKEN_HOTP:
-        step = token->hotp.counter + step;
-        break;
-    default:
-        return false;
-    }
-
-    if (!hotp(&token->token, step, &tmp))
-        return false;
-
-    if (first != tmp)
-        return false;
-
-    if (second == NULL)
-        return true;
-
-    if (!hotp(&token->token, step + 1, &tmp))
-        return false;
-
-    return *second == tmp;
-}
-
-static bool writeback(struct otptoken *token, ssize_t step, bool sync)
+static bool writeattr(const struct otptoken *token, const char *attr,
+                      int value)
 {
     Slapi_Value *svals[] = { NULL, NULL };
     Slapi_PBlock *pb = NULL;
     Slapi_Mods *mods = NULL;
     bool success = false;
-    const char *attr;
-    int value;
     int ret;
 
-    switch (token->type) {
-    case OTPTOKEN_TOTP:
-        if (!sync)
-            return true;
-        attr = T("clockOffset");
-        value = token->totp.offset + step * token->totp.step;
-        break;
-    case OTPTOKEN_HOTP:
-        /* Having support for LDAP_MOD_INCREMENT could be helpful here. */
-        if (step < 0)
-            return false; /* NEVER go backwards! */
-        attr = H("counter");
-        value = token->hotp.counter + step;
-        break;
-    default:
-        return false;
-    }
-
     /* Create the value. */
     svals[0] = slapi_value_new();
-    if (slapi_value_set_int(svals[0], value) != 0)
-        goto error;
+    if (slapi_value_set_int(svals[0], value) != 0) {
+        slapi_value_free(&svals[0]);
+        return false;
+    }
 
     /* Create the mods. */
     mods = slapi_mods_new();
@@ -203,26 +156,91 @@ static bool writeback(struct otptoken *token, ssize_t step, bool sync)
     if (ret != LDAP_SUCCESS)
         goto error;
 
-    /* Save our modifications to the object. */
-    switch (token->type) {
-    case OTPTOKEN_TOTP:
-        token->totp.offset = value;
-        break;
-    case OTPTOKEN_HOTP:
-        token->hotp.counter = value;
-        break;
-    default:
-        break;
-    }
-
     success = true;
 
 error:
     slapi_pblock_destroy(pb);
     slapi_mods_free(&mods);
     return success;
+
 }
 
+/**
+ * Validate a token.
+ *
+ * If the second token code is specified, perform synchronization.
+ */
+static bool validate(struct otptoken *token, time_t now, ssize_t step,
+                     uint32_t first, const uint32_t *second)
+{
+    const char *attr;
+    uint32_t tmp;
+
+    /* Calculate the absolute step. */
+    switch (token->type) {
+    case OTPTOKEN_TOTP:
+        attr = T("watermark");
+        step = (now + token->totp.offset) / token->totp.step + step;
+        if (token->totp.watermark > 0 && step < token->totp.watermark)
+            return false;
+        break;
+    case OTPTOKEN_HOTP:
+        if (step < 0) /* NEVER go backwards! */
+            return false;
+        attr = H("counter");
+        step = token->hotp.counter + step;
+        break;
+    default:
+        return false;
+    }
+
+    /* Validate the first code. */
+    if (!hotp(&token->token, step++, &tmp))
+        return false;
+
+    if (first != tmp)
+        return false;
+
+    /* Validate the second code if specified. */
+    if (second != NULL) {
+        if (!hotp(&token->token, step++, &tmp))
+            return false;
+
+        if (*second != tmp)
+            return false;
+
+        /* Perform optional synchronization steps. */
+        switch (token->type) {
+        case OTPTOKEN_TOTP:
+            tmp = (step - now / token->totp.step) * token->totp.step;
+            if (!writeattr(token, T("clockOffset"), tmp))
+                return false;
+            break;
+        default:
+            break;
+        }
+    }
+
+    /* Write the step value. */
+    if (!writeattr(token, attr, step))
+        return false;
+
+    /* Save our modifications to the object. */
+    switch (token->type) {
+    case OTPTOKEN_TOTP:
+        token->totp.watermark = step;
+        break;
+    case OTPTOKEN_HOTP:
+        token->hotp.counter = step;
+        break;
+    default:
+        break;
+    }
+
+    return true;
+}
+
+
 static void otptoken_free(struct otptoken *token)
 {
     if (token == NULL)
@@ -303,6 +321,9 @@ static struct otptoken *otptoken_new(Slapi_ComponentId *id, Slapi_Entry *entry)
         /* Get offset. */
         token->totp.offset = slapi_entry_attr_get_int(entry, T("clockOffset"));
 
+        /* Get watermark. */
+        token->totp.watermark = slapi_entry_attr_get_int(entry, T("watermark"));
+
         /* Get step. */
         token->totp.step = slapi_entry_attr_get_uint(entry, T("timeStep"));
         if (token->totp.step == 0)
@@ -464,15 +485,11 @@ static bool otptoken_validate(struct otptoken *token, size_t steps,
     for (int i = 0; i <= steps; i++) {
         /* Validate the positive step. */
         if (validate(token, now, i, code, NULL))
-            return writeback(token, i + 1, false);
-
-        /* Counter-based tokens must NEVER validate old steps! */
-        if (i == 0 || token->type == OTPTOKEN_HOTP)
-            continue;
+            return true;
 
         /* Validate the negative step. */
         if (validate(token, now, 0 - i, code, NULL))
-            return writeback(token, 0 - i + 1, false);
+            return true;
     }
 
     return false;
@@ -538,15 +555,11 @@ static bool otptoken_sync(struct otptoken * const *tokens, size_t steps,
         for (int j = 0; tokens[j] != NULL; j++) {
             /* Validate the positive step. */
             if (validate(tokens[j], now, i, first_code, &second_code))
-                return writeback(tokens[j], i + 2, true);
-
-            /* Counter-based tokens must NEVER validate old steps! */
-            if (i == 0 || tokens[j]->type == OTPTOKEN_HOTP)
-                continue;
+                return true;
 
             /* Validate the negative step. */
             if (validate(tokens[j], now, 0 - i, first_code, &second_code))
-                return writeback(tokens[j], 0 - i + 2, true);
+                return true;
         }
     }
 
diff --git a/install/share/70ipaotp.ldif b/install/share/70ipaotp.ldif
index 0b9815704cccabfa515c3a744de9ffa330f65500..bc95556682ef65ba375aa2f3cab6f53621641b3f 100644
--- a/install/share/70ipaotp.ldif
+++ b/install/share/70ipaotp.ldif
@@ -23,8 +23,9 @@ attributeTypes: (2.16.840.1.113730.3.8.16.1.18 NAME 'ipatokenRadiusTimeout' DESC
 attributeTypes: (2.16.840.1.113730.3.8.16.1.19 NAME 'ipatokenRadiusRetries' DESC 'Number of allowed Retries' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP')
 attributeTypes: (2.16.840.1.113730.3.8.16.1.20 NAME 'ipatokenUserMapAttribute' DESC 'Attribute to map from the user entry for RADIUS server authentication' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA OTP')
 attributeTypes: (2.16.840.1.113730.3.8.16.1.21 NAME 'ipatokenHOTPcounter' DESC 'HOTP counter' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP')
+attributeTypes: (2.16.840.1.113730.3.8.16.1.22 NAME 'ipatokenTOTPwatermark' DESC 'TOTP watermark' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP')
 objectClasses:  (2.16.840.1.113730.3.8.16.2.1  NAME 'ipaToken' SUP top ABSTRACT DESC 'Abstract token class for tokens' MUST (ipatokenUniqueID) MAY (description $ managedBy $ ipatokenOwner $ ipatokenDisabled $ ipatokenNotBefore $ ipatokenNotAfter $ ipatokenVendor $ ipatokenModel $ ipatokenSerial) X-ORIGIN 'IPA OTP')
-objectClasses:  (2.16.840.1.113730.3.8.16.2.2  NAME 'ipatokenTOTP' SUP ipaToken STRUCTURAL DESC 'TOTP Token Type' MUST (ipatokenOTPkey $ ipatokenOTPalgorithm $ ipatokenOTPdigits $ ipatokenTOTPclockOffset $ ipatokenTOTPtimeStep) X-ORIGIN 'IPA OTP')
+objectClasses:  (2.16.840.1.113730.3.8.16.2.2  NAME 'ipatokenTOTP' SUP ipaToken STRUCTURAL DESC 'TOTP Token Type' MUST (ipatokenOTPkey $ ipatokenOTPalgorithm $ ipatokenOTPdigits $ ipatokenTOTPclockOffset $ ipatokenTOTPtimeStep) MAY (ipatokenTOTPwatermark) X-ORIGIN 'IPA OTP')
 objectClasses:  (2.16.840.1.113730.3.8.16.2.3  NAME 'ipatokenRadiusProxyUser' SUP top AUXILIARY DESC 'Radius Proxy User' MAY (ipatokenRadiusConfigLink $ ipatokenRadiusUserName) X-ORIGIN 'IPA OTP')
 objectClasses:  (2.16.840.1.113730.3.8.16.2.4  NAME 'ipatokenRadiusConfiguration' SUP top STRUCTURAL DESC 'Proxy Radius Configuration' MUST (cn $ ipatokenRadiusServer $ ipatokenRadiusSecret) MAY (description $ ipatokenRadiusTimeout $ ipatokenRadiusRetries $ ipatokenUserMapAttribute) X-ORIGIN 'IPA OTP')
 objectClasses:  (2.16.840.1.113730.3.8.16.2.5  NAME 'ipatokenHOTP' SUP ipaToken STRUCTURAL DESC 'HOTP Token Type' MUST (ipatokenOTPkey $ ipatokenOTPalgorithm $ ipatokenOTPdigits $ ipatokenHOTPcounter) X-ORIGIN 'IPA OTP')
-- 
2.0.0

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to