summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel McCallum <npmccallum@redhat.com>2014-07-11 12:29:58 -0400
committerMartin Kosek <mkosek@redhat.com>2014-07-25 10:41:17 +0200
commitd3638438fce1a9d1e07c2be3b8f43befb07a6b40 (patch)
treeaa4b1a85c2cfc85d60a3981a46521230c39c60a2
parent15eb343b9c235a1ca3a6cc48f730590949d439ec (diff)
downloadfreeipa-d3638438fce1a9d1e07c2be3b8f43befb07a6b40.tar.gz
freeipa-d3638438fce1a9d1e07c2be3b8f43befb07a6b40.tar.xz
freeipa-d3638438fce1a9d1e07c2be3b8f43befb07a6b40.zip
Add TOTP watermark supportHEADmaster
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 Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
-rw-r--r--daemons/ipa-slapi-plugins/libotp/libotp.c159
-rw-r--r--install/share/70ipaotp.ldif3
2 files changed, 88 insertions, 74 deletions
diff --git a/daemons/ipa-slapi-plugins/libotp/libotp.c b/daemons/ipa-slapi-plugins/libotp/libotp.c
index eeead43e3..41f9e7b48 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,106 +124,123 @@ 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)
+static bool writeattr(const struct otptoken *token, const char *attr,
+ int value)
{
- uint32_t tmp;
+ Slapi_Value *svals[] = { NULL, NULL };
+ Slapi_PBlock *pb = NULL;
+ Slapi_Mods *mods = NULL;
+ bool success = false;
+ int ret;
- 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:
+ /* Create the value. */
+ svals[0] = slapi_value_new();
+ if (slapi_value_set_int(svals[0], value) != 0) {
+ slapi_value_free(&svals[0]);
return false;
}
- if (!hotp(&token->token, step, &tmp))
- return false;
+ /* Create the mods. */
+ mods = slapi_mods_new();
+ slapi_mods_add_mod_values(mods, LDAP_MOD_REPLACE, attr, svals);
- if (first != tmp)
- return false;
+ /* Perform the modification. */
+ pb = slapi_pblock_new();
+ slapi_modify_internal_set_pb(pb, slapi_sdn_get_dn(token->sdn),
+ slapi_mods_get_ldapmods_byref(mods),
+ NULL, NULL, token->plugin_id, 0);
+ if (slapi_modify_internal_pb(pb) != 0)
+ goto error;
+ if (slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret) != 0)
+ goto error;
+ if (ret != LDAP_SUCCESS)
+ goto error;
- if (second == NULL)
- return true;
+ success = true;
- if (!hotp(&token->token, step + 1, &tmp))
- return false;
+error:
+ slapi_pblock_destroy(pb);
+ slapi_mods_free(&mods);
+ return success;
- return *second == tmp;
}
-static bool writeback(struct otptoken *token, ssize_t step, bool sync)
+/**
+ * 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)
{
- Slapi_Value *svals[] = { NULL, NULL };
- Slapi_PBlock *pb = NULL;
- Slapi_Mods *mods = NULL;
- bool success = false;
const char *attr;
- int value;
- int ret;
+ uint32_t tmp;
+ /* Calculate the absolute step. */
switch (token->type) {
case OTPTOKEN_TOTP:
- if (!sync)
- return true;
- attr = T("clockOffset");
- value = token->totp.offset + step * token->totp.step;
+ 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:
- /* Having support for LDAP_MOD_INCREMENT could be helpful here. */
- if (step < 0)
- return false; /* NEVER go backwards! */
+ if (step < 0) /* NEVER go backwards! */
+ return false;
attr = H("counter");
- value = token->hotp.counter + step;
+ step = 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;
+ /* Validate the first code. */
+ if (!hotp(&token->token, step++, &tmp))
+ return false;
- /* Create the mods. */
- mods = slapi_mods_new();
- slapi_mods_add_mod_values(mods, LDAP_MOD_REPLACE, attr, svals);
+ if (first != tmp)
+ return false;
- /* Perform the modification. */
- pb = slapi_pblock_new();
- slapi_modify_internal_set_pb(pb, slapi_sdn_get_dn(token->sdn),
- slapi_mods_get_ldapmods_byref(mods),
- NULL, NULL, token->plugin_id, 0);
- if (slapi_modify_internal_pb(pb) != 0)
- goto error;
- if (slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret) != 0)
- goto error;
- if (ret != LDAP_SUCCESS)
- goto error;
+ /* 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.offset = value;
+ token->totp.watermark = step;
break;
case OTPTOKEN_HOTP:
- token->hotp.counter = value;
+ token->hotp.counter = step;
break;
default:
break;
}
- success = true;
-
-error:
- slapi_pblock_destroy(pb);
- slapi_mods_free(&mods);
- return success;
+ 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 0b9815704..bc9555668 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')