summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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')