]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#9055 Introduce a combined password scheme
authorGreg Veldman <gv@members.scinet.supercomputing.org>
Thu, 3 Oct 2019 07:41:31 +0000 (08:41 +0100)
committerOndřej Kuzník <ondra@mistotebe.net>
Thu, 3 Oct 2019 07:41:31 +0000 (08:41 +0100)
contrib/slapd-modules/passwd/totp/README
contrib/slapd-modules/passwd/totp/slapd-totp.c
contrib/slapd-modules/passwd/totp/slapo-totp.5

index 9aa985bdb352d720eac1b444fafacfc273b907b3..dcb6ed8a15a73dafbda7de748f0ec1d0d47b4d5f 100644 (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
index 57407e4ac8cf7fbded584a9c0e92eebe211de608..bd423f941bc558107759cf433798ee2849309a7b 100644 (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;
 
index daf064efd4324e4c8095d212f44d12003fb48dea..6a6eff1fcd7b14f26f8bbad80002b6576e5a3fd6 100644 (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.