--- /dev/null
+/* slapd-totp.c - Password module and overlay for TOTP */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2015 The OpenLDAP Foundation.
+ * Portions Copyright 2015 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include <portable.h>
+
+#include <lber.h>
+#include <lber_pvt.h>
+#include "lutil.h"
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+/* include socket.h to get sys/types.h and/or winsock2.h */
+#include <ac/socket.h>
+
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+
+#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 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 AttributeDescription *ad_authTimestamp;
+
+/* RFC3548 base32 encoding/decoding */
+
+static const char Base32[] =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+static const char Pad32 = '=';
+
+static int
+totp_b32_ntop(
+       u_char const *src,
+       size_t srclength,
+       char *target,
+       size_t targsize)
+{
+       size_t datalength = 0;
+       u_char input0;
+       u_int input1;   /* assumed to be at least 32 bits */
+       u_char output[8];
+       int i;
+
+       while (4 < srclength) {
+               if (datalength + 8 > targsize)
+                       return (-1);
+               input0 = *src++;
+               input1 = *src++;
+               input1 <<= 8;
+               input1 |= *src++;
+               input1 <<= 8;
+               input1 |= *src++;
+               input1 <<= 8;
+               input1 |= *src++;
+               srclength -= 5;
+
+               for (i=7; i>1; i--) {
+                       output[i] = input1 & 0x1f;
+                       input1 >>= 5;
+               }
+               output[0] = input0 >> 3;
+               output[1] = (input0 & 0x07) << 2 | input1;
+
+               for (i=0; i<8; i++)
+                       target[datalength++] = Base32[output[i]];
+       }
+    
+       /* Now we worry about padding. */
+       if (0 != srclength) {
+               static const int outlen[] = { 2,4,5,7 };
+               int n;
+               if (datalength + 8 > targsize)
+                       return (-1);
+
+               /* Get what's left. */
+               input1 = *src++;
+               for (i = 1; i < srclength; i++) {
+                       input1 <<= 8;
+                       input1 |= *src++;
+               }
+               input1 <<= 8 * (4-srclength);
+               n = outlen[srclength-1];
+               for (i=0; i<n; i++) {
+                       target[datalength++] = Base32[(input1 & 0xf8000000) >> 27];
+                       input1 <<= 5;
+               }
+               for (; i<8; i++)
+                       target[datalength++] = Pad32;
+       }
+       if (datalength >= targsize)
+               return (-1);
+       target[datalength] = '\0';      /* Returned value doesn't count \0. */
+       return (datalength);
+}
+
+/* converts characters, eight at a time, starting at src
+   from base - 32 numbers into five 8 bit bytes in the target area.
+   it returns the number of data bytes stored at the target, or -1 on error.
+ */
+
+static int
+totp_b32_pton(
+       char const *src,
+       u_char *target, 
+       size_t targsize)
+{
+       int tarindex, state, ch;
+       char *pos;
+
+       state = 0;
+       tarindex = 0;
+
+       while ((ch = *src++) != '\0') {
+               if (ch == Pad32)
+                       break;
+
+               pos = strchr(Base32, ch);
+               if (pos == 0)           /* A non-base32 character. */
+                       return (-1);
+
+               switch (state) {
+               case 0:
+                       if (target) {
+                               if ((size_t)tarindex >= targsize)
+                                       return (-1);
+                               target[tarindex] = (pos - Base32) << 3;
+                       }
+                       state = 1;
+                       break;
+               case 1:
+                       if (target) {
+                               if ((size_t)tarindex + 1 >= targsize)
+                                       return (-1);
+                               target[tarindex]   |=  (pos - Base32) >> 2;
+                               target[tarindex+1]  = ((pos - Base32) & 0x3)
+                                                       << 6 ;
+                       }
+                       tarindex++;
+                       state = 2;
+                       break;
+               case 2:
+                       if (target) {
+                               target[tarindex]   |=  (pos - Base32) << 1;
+                       }
+                       state = 3;
+                       break;
+               case 3:
+                       if (target) {
+                               if ((size_t)tarindex + 1 >= targsize)
+                                       return (-1);
+                               target[tarindex] |= (pos - Base32) >> 4;
+                               target[tarindex+1]  = ((pos - Base32) & 0xf)
+                                                       << 4 ;
+                       }
+                       tarindex++;
+                       state = 4;
+                       break;
+               case 4:
+                       if (target) {
+                               if ((size_t)tarindex + 1 >= targsize)
+                                       return (-1);
+                               target[tarindex] |= (pos - Base32) >> 1;
+                               target[tarindex+1]  = ((pos - Base32) & 0x1)
+                                                       << 7 ;
+                       }
+                       tarindex++;
+                       state = 5;
+                       break;
+               case 5:
+                       if (target) {
+                               target[tarindex]   |=  (pos - Base32) << 2;
+                       }
+                       state = 6;
+                       break;
+               case 6:
+                       if (target) {
+                               if ((size_t)tarindex + 1 >= targsize)
+                                       return (-1);
+                               target[tarindex] |= (pos - Base32) >> 3;
+                               target[tarindex+1]  = ((pos - Base32) & 0x7)
+                                                       << 5 ;
+                       }
+                       tarindex++;
+                       state = 7;
+                       break;
+               case 7:
+                       if (target) {
+                               target[tarindex]   |=  (pos - Base32);
+                       }
+                       state = 0;
+                       tarindex++;
+                       break;
+
+               default:
+                       abort();
+               }
+       }
+
+       /*
+        * We are done decoding Base-32 chars.  Let's see if we ended
+        * on a byte boundary, and/or with erroneous trailing characters.
+        */
+
+       if (ch == Pad32) {              /* We got a pad char. */
+               int i = 1;
+
+               /* count pad chars */
+               for (; ch; ch = *src++) {
+                       if (ch != Pad32)
+                               return (-1);
+                       i++;
+               }
+               /* there are only 4 valid ending states with a
+                * pad character, make sure the number of pads is valid.
+                */
+               switch(state) {
+               case 2: if (i != 6) return -1;
+                       break;
+               case 4: if (i != 4) return -1;
+                       break;
+               case 5: if (i != 3) return -1;
+                       break;
+               case 7: if (i != 1) return -1;
+                       break;
+               default:
+                       return -1;
+               }
+               /*
+                * Now make sure that the "extra" bits that slopped past
+                * the last full byte were zeros.  If we don't check them,
+                * they become a subliminal channel.
+                */
+               if (target && target[tarindex] != 0)
+                       return (-1);
+       } else {
+               /*
+                * We ended by seeing the end of the string.  Make sure we
+                * have no partial bytes lying around.
+                */
+               if (state != 0)
+                       return (-1);
+       }
+
+       return (tarindex);
+}
+
+/* RFC6238 TOTP */
+
+#define HMAC_setup(ctx, key, len, hash)        HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, key, len, hash, 0)
+#define HMAC_crunch(ctx, buf, len)     HMAC_Update(&ctx, buf, len)
+#define HMAC_finish(ctx, dig, dlen)    HMAC_Final(&ctx, dig, &dlen); HMAC_CTX_cleanup(&ctx)
+
+typedef struct myval {
+       ber_len_t mv_len;
+       void *mv_val;
+} myval;
+
+static void do_hmac(
+       const void *hash,
+       myval *key,
+       myval *data,
+       myval *out)
+{
+       HMAC_CTX ctx;
+       unsigned int digestLen;
+
+       HMAC_setup(ctx, key->mv_val, key->mv_len, hash);
+       HMAC_crunch(ctx, data->mv_val, data->mv_len);
+       HMAC_finish(ctx, out->mv_val, digestLen);
+       out->mv_len = digestLen;
+}
+
+static const int DIGITS_POWER[] = {
+       1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
+
+static void generate(
+       myval *key,
+       unsigned long tval,
+       int digits,
+       myval *out,
+       const void *mech)
+{
+       unsigned char digest[SHA512_DIGEST_LENGTH];
+       myval digval;
+       myval data;
+       unsigned char msg[8];
+       int i, offset, res, otp;
+
+#if !WORDS_BIGENDIAN
+       /* only needed on little-endian, can just use tval directly on big-endian */
+       for (i=7; i>=0; i--) {
+               msg[i] = tval & 0xff;
+               tval >>= 8;
+       }
+#endif
+
+       data.mv_val = msg;
+       data.mv_len = sizeof(msg);
+
+       digval.mv_val = digest;
+       digval.mv_len = sizeof(digest);
+       do_hmac(mech, key, &data, &digval);
+
+       offset = digest[digval.mv_len-1] & 0xf;
+       res = ((digest[offset] & 0x7f) << 24) |
+                       ((digest[offset+1] & 0xff) << 16) |
+                       ((digest[offset+2] & 0xff) << 8) |
+                       (digest[offset+3] & 0xff);
+
+       otp = res % DIGITS_POWER[digits];
+       out->mv_len = snprintf(out->mv_val, out->mv_len, "%0*d", digits, otp);
+}
+
+static int totp_op_cleanup( Operation *op, SlapReply *rs );
+
+#define TIME_STEP      30
+#define DIGITS 6
+
+static int chk_totp(
+       const struct berval *passwd,
+       const struct berval *cred,
+       const void *mech,
+       const char **text)
+{
+       void *ctx, *op_tmp;
+       Operation *op;
+       Entry *e;
+       Attribute *a;
+       long t = time(0L) / TIME_STEP;
+       int rc;
+       myval out, key;
+       char outbuf[32];
+
+       /* Find our thread context, find our Operation */
+       ctx = ldap_pvt_thread_pool_context();
+       if (ldap_pvt_thread_pool_getkey(ctx, totp_op_cleanup, &op_tmp, NULL) ||
+               !op_tmp)
+               return LUTIL_PASSWD_ERR;
+       op = op_tmp;
+
+       rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
+       if (rc != LDAP_SUCCESS) return LUTIL_PASSWD_ERR;
+
+       /* Make sure previous login is older than current time */
+       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;
+                       if (told >= t)
+                               rc = LUTIL_PASSWD_ERR;
+               }
+       }       /* else no previous login, 1st use is OK */
+
+       be_entry_release_r(op, e);
+       if (rc) return rc;
+
+       /* Key is stored in base32 */
+       key.mv_len = passwd->bv_len * 5 / 8;
+       key.mv_val = ber_memalloc(key.mv_len+1);
+
+       if (!key.mv_val)
+               return LUTIL_PASSWD_ERR;
+
+       rc = totp_b32_pton(passwd->bv_val, key.mv_val, key.mv_len);
+       if (rc < 1) {
+               rc = LUTIL_PASSWD_ERR;
+               goto out;
+       }
+
+       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)
+               return LUTIL_PASSWD_ERR;
+
+       rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+
+out:
+       ber_memfree(key.mv_val);
+       return rc;
+}
+
+static int chk_totp1(
+       const struct berval *scheme,
+       const struct berval *passwd,
+       const struct berval *cred,
+       const char **text)
+{
+       return chk_totp(passwd, cred, EVP_sha1(), text);
+}
+
+static int chk_totp256(
+       const struct berval *scheme,
+       const struct berval *passwd,
+       const struct berval *cred,
+       const char **text)
+{
+       return chk_totp(passwd, cred, EVP_sha256(), text);
+}
+
+static int chk_totp512(
+       const struct berval *scheme,
+       const struct berval *passwd,
+       const struct berval *cred,
+       const char **text)
+{
+       return chk_totp(passwd, cred, EVP_sha512(), text);
+}
+
+static int passwd_string32(
+       const struct berval *scheme,
+       const struct berval *passwd,
+       struct berval *hash)
+{
+       int b32len = (passwd->bv_len + 4)/5 * 8;
+       int rc;
+       hash->bv_len = scheme->bv_len + b32len;
+       hash->bv_val = ber_memalloc(hash->bv_len + 1);
+       AC_MEMCPY(hash->bv_val, scheme->bv_val, scheme->bv_len);
+       rc = totp_b32_ntop((unsigned char *)passwd->bv_val, passwd->bv_len,
+               hash->bv_val + scheme->bv_len, b32len+1);
+       if (rc < 0) {
+               ber_memfree(hash->bv_val);
+               hash->bv_val = NULL;
+               return LUTIL_PASSWD_ERR;
+       }
+       return LUTIL_PASSWD_OK;
+}
+
+static int hash_totp1(
+       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 passwd_string32(scheme, passwd, hash);
+}
+
+static int hash_totp256(
+       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 passwd_string32(scheme, passwd, hash);
+}
+
+static int hash_totp512(
+       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 passwd_string32(scheme, passwd, hash);
+}
+
+static int totp_op_cleanup(
+       Operation *op,
+       SlapReply *rs )
+{
+       slap_callback *cb;
+
+       /* clear out the current key */
+       ldap_pvt_thread_pool_setkey( op->o_threadctx, totp_op_cleanup,
+               NULL, 0, NULL, NULL );
+
+       /* free the callback */
+       cb = op->o_callback;
+       op->o_callback = cb->sc_next;
+       op->o_tmpfree( cb, op->o_tmpmemctx );
+       return 0;
+}
+
+static int totp_op_bind(
+       Operation *op,
+       SlapReply *rs )
+{
+       /* If this is a simple Bind, stash the Op pointer so our chk
+        * function can find it. Set a cleanup callback to clear it
+        * out when the Bind completes.
+        */
+       if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) {
+               slap_callback *cb;
+               ldap_pvt_thread_pool_setkey( op->o_threadctx,
+                       totp_op_cleanup, op, 0, NULL, NULL );
+               cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+               cb->sc_cleanup = totp_op_cleanup;
+               cb->sc_next = op->o_callback;
+               op->o_callback = cb;
+       }
+       return SLAP_CB_CONTINUE;
+}
+
+static int totp_db_open(
+       BackendDB *be,
+       ConfigReply *cr
+)
+{
+       int rc = 0;
+
+       if (!ad_authTimestamp) {
+               const char *text = NULL;
+               rc = slap_str2ad("authTimestamp", &ad_authTimestamp, &text);
+               if (rc) {
+                       snprintf(cr->msg, sizeof(cr->msg), "unable to find authTimestamp attribute: %s (%d)",
+                               text, rc);
+                       Debug(LDAP_DEBUG_ANY, "totp: %s.\n", cr->msg, 0, 0);
+               }
+       }
+       return rc;
+}
+
+static slap_overinst totp;
+
+int
+totp_initialize(void)
+{
+       int rc;
+
+       totp.on_bi.bi_type = "totp";
+
+       totp.on_bi.bi_db_open = totp_db_open;
+       totp.on_bi.bi_op_bind = totp_op_bind;
+
+       rc = lutil_passwd_add((struct berval *) &scheme_totp1, chk_totp1, hash_totp1);
+       if (!rc)
+               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)
+               return rc;
+
+       return overlay_register(&totp);
+}
+
+int init_module(int argc, char *argv[]) {
+       return totp_initialize();
+}