ITS#9055 Introduce a combined password scheme

This commit is contained in:
Greg Veldman 2019-10-03 08:41:31 +01:00 committed by Ondřej Kuzník
parent 711a96064e
commit 3be82f40d5
3 changed files with 239 additions and 9 deletions

View File

@ -9,6 +9,17 @@ userPassword: {TOTP1}GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
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|<some_other_passwd>
where <some_other_passwd> 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 @@ cannot use that overlay on the same database as this one.
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

View File

@ -94,11 +94,16 @@ static void HMAC_CTX_free(HMAC_CTX *ctx)
#include "slap.h"
#include "config.h"
static LUTIL_PASSWD_CHK_FUNC chk_totp1, chk_totp256, chk_totp512;
static LUTIL_PASSWD_HASH_FUNC hash_totp1, hash_totp256, hash_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,
hash_totp1andpw, hash_totp256andpw, hash_totp512andpw;
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 +417,8 @@ static int totp_bind_response( Operation *op, SlapReply *rs );
#define TIME_STEP 30
#define DIGITS 6
#define DELIM '|' /* a single character */
#define TOTP_AND_PW_HASH_SCHEME "{SSHA}"
static int chk_totp(
const struct berval *passwd,
@ -423,7 +430,7 @@ static int chk_totp(
Operation *op;
Entry *e;
Attribute *a;
long t = time(0L) / TIME_STEP;
long t, told = 0;
int rc;
myval out, key;
char outbuf[32];
@ -439,13 +446,14 @@ static int chk_totp(
if (rc != LDAP_SUCCESS) return LUTIL_PASSWD_ERR;
/* Make sure previous login is older than current time */
t = op->o_time / TIME_STEP;
a = attr_find(e->e_attrs, ad_authTimestamp);
if (a) {
struct lutil_tm tm;
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;
}
@ -507,6 +515,59 @@ out:
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, rc_pass, rc_otp;
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], DIGITS, 0, &cred_otp);
/* The password part of the entered credential */
if (!ber_str2bv(cred->bv_val, cred->bv_len - DIGITS, 0, &cred_pass)) {
/* Cleanup */
memset(passwd_otp.bv_val, 0, passwd_otp.bv_len);
ber_memfree(passwd_otp.bv_val);
return rc;
}
rc_otp = chk_totp(&passwd_otp, &cred_otp, mech, text);
rc_pass = lutil_passwd(&passwd_pass, &cred_pass, NULL, text);
if (rc_otp == LUTIL_PASSWD_OK && rc_pass == LUTIL_PASSWD_OK)
rc = LUTIL_PASSWD_OK;
/* Cleanup and return */
memset(passwd_otp.bv_val, 0, passwd_otp.bv_len);
ber_memfree(passwd_otp.bv_val);
return rc;
}
static int chk_totp1(
const struct berval *scheme,
const struct berval *passwd,
@ -534,6 +595,33 @@ static int chk_totp512(
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,
@ -554,6 +642,74 @@ static int passwd_string32(
return LUTIL_PASSWD_OK;
}
static int hash_totp_and_pw(
const struct berval *scheme,
const struct berval *passwd,
struct berval *hash,
const char **text)
{
struct berval otp, pass, hash_otp, hash_pass;
ber_len_t len;
char *s;
int rc = LUTIL_PASSWD_ERR;
/* The OTP seed part */
s = strchr(passwd->bv_val, DELIM);
if (s) {
len = s - passwd->bv_val;
} else {
return rc;
}
if (!ber_str2bv(passwd->bv_val, len, 0, &otp))
return rc;
/* The static password part */
s++;
ber_str2bv(s, 0, 0, &pass);
/* Hash the OTP seed */
rc = passwd_string32(scheme, &otp, &hash_otp);
/* If successful, hash the static password, else cleanup and return */
if (rc == LUTIL_PASSWD_OK) {
rc = lutil_passwd_hash(&pass, TOTP_AND_PW_HASH_SCHEME,
&hash_pass, text);
} else {
return LUTIL_PASSWD_ERR;
}
/* If successful, allocate memory to combine them, else cleanup
* and return */
if (rc == LUTIL_PASSWD_OK) {
/* Add 1 character to bv_len to hold DELIM */
hash->bv_len = hash_pass.bv_len + hash_otp.bv_len + 1;
hash->bv_val = ber_memalloc(hash->bv_len + 1);
if (!hash->bv_val)
rc = LUTIL_PASSWD_ERR;
} else {
memset(hash_otp.bv_val, 0, hash_otp.bv_len);
ber_memfree(hash_otp.bv_val);
return LUTIL_PASSWD_ERR;
}
/* If successful, combine the two hashes with the delimiter */
if (rc == LUTIL_PASSWD_OK) {
AC_MEMCPY(hash->bv_val, hash_otp.bv_val, hash_otp.bv_len);
hash->bv_val[hash_otp.bv_len] = DELIM;
AC_MEMCPY(hash->bv_val + hash_otp.bv_len + 1,
hash_pass.bv_val, hash_pass.bv_len);
hash->bv_val[hash->bv_len] = '\0';
}
/* Cleanup and return */
memset(hash_otp.bv_val, 0, hash_otp.bv_len);
memset(hash_pass.bv_val, 0, hash_pass.bv_len);
ber_memfree(hash_otp.bv_val);
ber_memfree(hash_pass.bv_val);
return rc;
}
static int hash_totp1(
const struct berval *scheme,
const struct berval *passwd,
@ -599,6 +755,51 @@ static int hash_totp512(
return passwd_string32(scheme, passwd, hash);
}
static int hash_totp1andpw(
const struct berval *scheme,
const struct berval *passwd,
struct berval *hash,
const char **text)
{
#if 0
if (passwd->bv_len != SHA_DIGEST_LENGTH) {
*text = "invalid key length";
return LUTIL_PASSWD_ERR;
}
#endif
return hash_totp_and_pw(scheme, passwd, hash, text);
}
static int hash_totp256andpw(
const struct berval *scheme,
const struct berval *passwd,
struct berval *hash,
const char **text)
{
#if 0
if (passwd->bv_len != SHA256_DIGEST_LENGTH) {
*text = "invalid key length";
return LUTIL_PASSWD_ERR;
}
#endif
return hash_totp_and_pw(scheme, passwd, hash, text);
}
static int hash_totp512andpw(
const struct berval *scheme,
const struct berval *passwd,
struct berval *hash,
const char **text)
{
#if 0
if (passwd->bv_len != SHA512_DIGEST_LENGTH) {
*text = "invalid key length";
return LUTIL_PASSWD_ERR;
}
#endif
return hash_totp_and_pw(scheme, passwd, hash, text);
}
static int totp_op_cleanup(
Operation *op,
SlapReply *rs )
@ -782,6 +983,12 @@ totp_initialize(void)
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, hash_totp1andpw);
if (!rc)
rc = lutil_passwd_add((struct berval *) &scheme_totp256andpw, chk_totp256andpw, hash_totp256andpw);
if (!rc)
rc = lutil_passwd_add((struct berval *) &scheme_totp512andpw, chk_totp512andpw, hash_totp512andpw);
if (rc)
return rc;

View File

@ -27,6 +27,12 @@ to the user's key and delivered to the user through SMS or some other channel.
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 @@ value should correspond to that used by the the prover (authenticator).
.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
@ -68,6 +75,11 @@ is needed to properly implement TOTP, provisions need to be made to propagate
the authTimestamp attribute to other servers that are providing authentication
services.
The hash functions for the {TOTP1ANDPW}, {TOTP256ANDPW}, and {TOTP512ANDPW}
schemes expect the secret to be entered in the form:
<OTP seed><DELIM><static password>, where DELIM is currently defined
as the pipe character (|).
.SH BUGS
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
@ -85,9 +97,6 @@ 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.
@ -96,3 +105,5 @@ be changed at the same time. This is likely to be undesirable behavior.
.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.