diff -Naur totp.orig/README totp/README --- totp.orig/README 2019-07-09 11:47:36.683458904 -0400 +++ totp/README 2019-07-09 12:05:33.916446855 -0400 @@ -9,6 +9,17 @@ which encodes the key '12345678901234567890'. +It can also encode credentials consisting of a TOTP and a static +password. The format for this is: + +userPassword: {TOTP1ANDPW}GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ| + +where can be any scheme currently understood +by OpenLDAP. For example, using '{SHA}5en6G6MezRroT3XKqkdPOmY/BfQ=' +would encode the above TOTP with a static password of 'secret'. To +authenticate using this scheme, enter the static password immediately +followed by the TOTP, for example 'secret123456'. + Building -------- @@ -33,7 +44,8 @@ Configuring ----------- -The {TOTP1}, {TOTP256}, and {TOTP512} password schemes should now be recognised. +The {TOTP1}, {TOTP256}, {TOTP512}, {TOTP1ANDPW}, {TOTP256ANDPW}, +and {TOTP512ANDPW} password schemes should now be recognised. You can also tell OpenLDAP to use one of these new schemes when processing LDAP Password Modify Extended Operations, thanks to the password-hash option in diff -Naur totp.orig/slapd-totp.c totp/slapd-totp.c --- totp.orig/slapd-totp.c 2019-07-09 11:47:41.943561759 -0400 +++ totp/slapd-totp.c 2019-07-12 12:11:23.853996668 -0400 @@ -94,11 +94,15 @@ #include "slap.h" #include "config.h" -static LUTIL_PASSWD_CHK_FUNC chk_totp1, chk_totp256, chk_totp512; +static LUTIL_PASSWD_CHK_FUNC chk_totp1, chk_totp256, chk_totp512, + chk_totp1andpw, chk_totp256andpw, chk_totp512andpw; static LUTIL_PASSWD_HASH_FUNC hash_totp1, hash_totp256, hash_totp512; static const struct berval scheme_totp1 = BER_BVC("{TOTP1}"); static const struct berval scheme_totp256 = BER_BVC("{TOTP256}"); static const struct berval scheme_totp512 = BER_BVC("{TOTP512}"); +static const struct berval scheme_totp1andpw = BER_BVC("{TOTP1ANDPW}"); +static const struct berval scheme_totp256andpw = BER_BVC("{TOTP256ANDPW}"); +static const struct berval scheme_totp512andpw = BER_BVC("{TOTP512ANDPW}"); static AttributeDescription *ad_authTimestamp; @@ -412,6 +416,7 @@ #define TIME_STEP 30 #define DIGITS 6 +#define DELIM '|' static int chk_totp( const struct berval *passwd, @@ -423,7 +428,7 @@ Operation *op; Entry *e; Attribute *a; - long t = time(0L) / TIME_STEP; + long t = time(0L) / TIME_STEP, told = 0; int rc; myval out, key; char outbuf[32]; @@ -445,7 +450,7 @@ struct lutil_timet tt; if (lutil_parsetime(a->a_vals[0].bv_val, &tm) == 0 && lutil_tm2time(&tm, &tt) == 0) { - long told = tt.tt_sec / TIME_STEP; + told = tt.tt_sec / TIME_STEP; if (told >= t) rc = LUTIL_PASSWD_ERR; } @@ -479,7 +484,6 @@ out.mv_val = outbuf; out.mv_len = sizeof(outbuf); generate(&key, t, DIGITS, &out, mech); - memset(key.mv_val, 0, key.mv_len); /* compare */ if (out.mv_len != cred->bv_len) { @@ -489,11 +493,79 @@ rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; + /* If current value doesn't match, try again with previous value + * but only if the most recent login is older than the previous + * time step but still set */ + if (rc == LUTIL_PASSWD_ERR && told < t - 1 && told > 0) { + out.mv_val = outbuf; + out.mv_len = sizeof(outbuf); + generate(&key, t - 1, DIGITS, &out, mech); + /* compare */ + if (out.mv_len != cred->bv_len) + goto out; + rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; + } + out: + memset(key.mv_val, 0, key.mv_len); ber_memfree(key.mv_val); return rc; } +static int chk_totp_and_pw( + const struct berval *scheme, + const struct berval *passwd, + const struct berval *cred, + const char **text, + const void *mech) +{ + char *s; + int rc = LUTIL_PASSWD_ERR; + ber_len_t len; + struct berval cred_pass, cred_otp, passwd_pass, passwd_otp; + + /* Check credential length, no point to continue if too short */ + if (cred->bv_len <= DIGITS) + return rc; + + /* The OTP seed of the stored password */ + s = strchr(passwd->bv_val, DELIM); + if (s) + len = s - passwd->bv_val; + else + return rc; + if (!ber_str2bv(passwd->bv_val, len, 1, &passwd_otp)) + return rc; + + /* The password part of the stored password */ + s++; + ber_str2bv(s, 0, 0, &passwd_pass); + + /* The OTP part of the entered credential */ + ber_str2bv(&cred->bv_val[cred->bv_len - DIGITS], 0, 0, &cred_otp); + + /* The password part of the entered credential */ + if (!ber_str2bv(cred->bv_val, cred->bv_len - DIGITS, 1, &cred_pass)) { + /* Cleanup */ + memset(passwd_otp.bv_val, 0, passwd_otp.bv_len); + ber_memfree(passwd_otp.bv_val); + return rc; + } + + if (chk_totp(&passwd_otp, &cred_otp, mech, text) == LUTIL_PASSWD_OK + && lutil_passwd(&passwd_pass, &cred_pass, NULL, text) + == LUTIL_PASSWD_OK) + rc = LUTIL_PASSWD_OK; + + /* Cleanup */ + memset(passwd_otp.bv_val, 0, passwd_otp.bv_len); + memset(cred_pass.bv_val, 0, cred_pass.bv_len); + ber_memfree(passwd_otp.bv_val); + ber_memfree(cred_pass.bv_val); + + return rc; +} + static int chk_totp1( const struct berval *scheme, const struct berval *passwd, @@ -521,6 +593,33 @@ return chk_totp(passwd, cred, TOTP_SHA512, text); } +static int chk_totp1andpw( + const struct berval *scheme, + const struct berval *passwd, + const struct berval *cred, + const char **text) +{ + return chk_totp_and_pw(scheme, passwd, cred, text, TOTP_SHA1); +} + +static int chk_totp256andpw( + const struct berval *scheme, + const struct berval *passwd, + const struct berval *cred, + const char **text) +{ + return chk_totp_and_pw(scheme, passwd, cred, text, TOTP_SHA256); +} + +static int chk_totp512andpw( + const struct berval *scheme, + const struct berval *passwd, + const struct berval *cred, + const char **text) +{ + return chk_totp_and_pw(scheme, passwd, cred, text, TOTP_SHA512); +} + static int passwd_string32( const struct berval *scheme, const struct berval *passwd, @@ -769,6 +868,12 @@ rc = lutil_passwd_add((struct berval *) &scheme_totp256, chk_totp256, hash_totp256); if (!rc) rc = lutil_passwd_add((struct berval *) &scheme_totp512, chk_totp512, hash_totp512); + if (!rc) + rc = lutil_passwd_add((struct berval *) &scheme_totp1andpw, chk_totp1andpw, NULL); + if (!rc) + rc = lutil_passwd_add((struct berval *) &scheme_totp256andpw, chk_totp256andpw, NULL); + if (!rc) + rc = lutil_passwd_add((struct berval *) &scheme_totp512andpw, chk_totp512andpw, NULL); if (rc) return rc; diff -Naur totp.orig/slapo-totp.5 totp/slapo-totp.5 --- totp.orig/slapo-totp.5 2019-07-09 11:47:51.455747744 -0400 +++ totp/slapo-totp.5 2019-07-09 13:23:55.540426232 -0400 @@ -27,6 +27,12 @@ When prompted to authenticate, the user merely enters the six-digit code provided by the prover. +Additionally, the overlay can also authenticate TOTP passwords +combined with a static password. To do this, utilize one of the +{TOTP1ANDPW}, {TOTP256ANDPW}, or {TOTP512ANDPW} password schemes +and append the static password scheme value to the end of the +userPassword attribute, separated by a pipe (|) character. + This implementation complies with .B RFC 6238 TOTP Time-based One Time Passwords and includes support for the SHA-1, SHA-256, and SHA-512 HMAC @@ -39,7 +45,8 @@ .SH CONFIGURATION Once the module is loaded with the moduleload command from the synopsis, -the {TOTP1}, {TOTP256}, and {TOTP512} +the {TOTP1}, {TOTP256}, {TOTP512} +{TOTP1ANDPW}, {TOTP256ANDPW}, and {TOTP512ANDPW} password schemes will be recognized. On the databases where your users reside you must configure the @@ -72,15 +79,17 @@ The time step is hard-coded to thirty seconds. This should be OK for many use cases, but it would be nice if the value could be changed with a configuration keyword or in an attribute value. +However, the TOTP value of the previous time window may also be used +to successfully authenticate, provided no successful bind has been +performed already in the current or previous time window. This +eliminates false negatives caused by user or network delays +entering or transmitting the TOTP value. The authenticator code that is generated is hard-coded to a length of six digits. While in most cases this is probably better than the alternative length of four digits, there may be cases where a four-digit value is preferred. -There is currently no way to require a separate PIN code with the authenticator -code. - In cases where password-hash lists multiple mechanisms, the TOTP key will also be changed at the same time. This is likely to be undesirable behavior. @@ -89,3 +98,5 @@ .SH ACKNOWLEDGEMENT This work was developed by Howard Chu of Symas Corporation for inclusion in OpenLDAP Software. + +Password + TOTP support added by Greg Veldman on behalf of SCinet.