From b527294153be3b79563c82c66102adc0004736c0 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 19:54:12 +0200 Subject: Adding upstream version 2.6.7+dfsg. Signed-off-by: Daniel Baumann --- contrib/slapd-modules/passwd/totp/slapd-totp.c | 1000 ++++++++++++++++++++++++ 1 file changed, 1000 insertions(+) create mode 100644 contrib/slapd-modules/passwd/totp/slapd-totp.c (limited to 'contrib/slapd-modules/passwd/totp/slapd-totp.c') diff --git a/contrib/slapd-modules/passwd/totp/slapd-totp.c b/contrib/slapd-modules/passwd/totp/slapd-totp.c new file mode 100644 index 0000000..08bd4eb --- /dev/null +++ b/contrib/slapd-modules/passwd/totp/slapd-totp.c @@ -0,0 +1,1000 @@ +/* slapd-totp.c - Password module and overlay for TOTP */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * Copyright 2015-2022 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work includes code from the lastbind overlay. + */ + +#include + +#if HAVE_STDINT_H +#include +#endif + +#include +#include +#include "lutil.h" +#include +#include +#include +/* include socket.h to get sys/types.h and/or winsock2.h */ +#include + +#if HAVE_OPENSSL +#include +#include + +#define TOTP_SHA512_DIGEST_LENGTH SHA512_DIGEST_LENGTH +#define TOTP_SHA1 EVP_sha1() +#define TOTP_SHA256 EVP_sha256() +#define TOTP_SHA512 EVP_sha512() +#define TOTP_HMAC_CTX HMAC_CTX * + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static HMAC_CTX *HMAC_CTX_new(void) +{ + HMAC_CTX *ctx = OPENSSL_malloc(sizeof(*ctx)); + if (ctx != NULL) { + HMAC_CTX_init(ctx); + } + return ctx; +} + +static void HMAC_CTX_free(HMAC_CTX *ctx) +{ + if (ctx != NULL) { + HMAC_CTX_cleanup(ctx); + OPENSSL_free(ctx); + } +} +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + +#define HMAC_setup(ctx, key, len, hash) \ + ctx = HMAC_CTX_new(); \ + 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_free(ctx) + +#elif HAVE_GNUTLS +#include + +#define TOTP_SHA512_DIGEST_LENGTH SHA512_DIGEST_SIZE +#define TOTP_SHA1 &nettle_sha1 +#define TOTP_SHA256 &nettle_sha256 +#define TOTP_SHA512 &nettle_sha512 +#define TOTP_HMAC_CTX struct hmac_sha512_ctx + +#define HMAC_setup(ctx, key, len, hash) \ + const struct nettle_hash *h=hash;\ + hmac_set_key(&ctx.outer, &ctx.inner, &ctx.state, h, len, key) +#define HMAC_crunch(ctx, buf, len) hmac_update(&ctx.state, h, len, buf) +#define HMAC_finish(ctx, dig, dlen) \ + hmac_digest(&ctx.outer, &ctx.inner, &ctx.state, h, h->digest_size, dig);\ + dlen = h->digest_size + +#else +# error Unsupported crypto backend. +#endif + +#include "slap.h" +#include "slap-config.h" + +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; + +/* This is the definition used by ISODE, as supplied to us in + * ITS#6238 Followup #9 + */ +static struct schema_info { + char *def; + AttributeDescription **ad; +} totp_OpSchema[] = { + { "( 1.3.6.1.4.1.453.16.2.188 " + "NAME 'authTimestamp' " + "DESC 'last successful authentication using any method/mech' " + "EQUALITY generalizedTimeMatch " + "ORDERING generalizedTimeOrderingMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE dsaOperation )", + &ad_authTimestamp}, + { NULL, NULL } +}; + +/* RFC3548 base32 encoding/decoding */ + +static const char Base32[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; +static const char Pad32 = '='; + +static int +totp_b32_ntop( + unsigned char const *src, + size_t srclength, + char *target, + size_t targsize) +{ + size_t datalength = 0; + unsigned char input0; + unsigned int input1; /* assumed to be at least 32 bits */ + unsigned 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> 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, + unsigned 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 = 0; + + /* 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 */ + + +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) +{ + TOTP_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, + uint64_t tval, + int digits, + myval *out, + const void *mech) +{ + unsigned char digest[TOTP_SHA512_DIGEST_LENGTH]; + myval digval; + myval data; + unsigned char msg[8]; + int i, offset, res, otp; + +#if WORDS_BIGENDIAN + *(uint64_t *)msg = tval; +#else + 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 ); +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, + const struct berval *cred, + const void *mech, + const char **text) +{ + void *ctx, *op_tmp; + Operation *op; + Entry *e; + Attribute *a; + long t, told = 0; + 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 */ + 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) { + told = tt.tt_sec / TIME_STEP; + if (told >= t) + rc = LUTIL_PASSWD_ERR; + } + if (!rc) { /* seems OK, remember old stamp */ + slap_callback *sc; + for (sc = op->o_callback; sc; sc = sc->sc_next) { + if (sc->sc_response == totp_bind_response) { + sc->sc_private = ber_dupbv_x(NULL, &a->a_vals[0], op->o_tmpmemctx); + break; + } + } + } + } /* 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); + + /* compare */ + if (out.mv_len != cred->bv_len) { + rc = LUTIL_PASSWD_ERR; + goto out; + } + + 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, 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, + const struct berval *cred, + const char **text) +{ + return chk_totp(passwd, cred, TOTP_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, TOTP_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, 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, + 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_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, + 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 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 ) +{ + 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; + if (cb->sc_private) + ber_bvfree_x(cb->sc_private, op->o_tmpmemctx); + op->o_tmpfree( cb, op->o_tmpmemctx ); + return 0; +} + +static int +totp_bind_response( Operation *op, SlapReply *rs ) +{ + Modifications *mod = NULL; + BackendInfo *bi = op->o_bd->bd_info; + Entry *e; + int rc; + + /* we're only interested if the bind was successful */ + if ( rs->sr_err != LDAP_SUCCESS ) + return SLAP_CB_CONTINUE; + + rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); + op->o_bd->bd_info = bi; + + if ( rc != LDAP_SUCCESS ) { + return SLAP_CB_CONTINUE; + } + + { + time_t now; + Attribute *a; + Modifications *m; + char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ]; + struct berval timestamp; + + /* get the current time */ + now = op->o_time; + + /* update the authTimestamp in the user's entry with the current time */ + timestamp.bv_val = nowstr; + timestamp.bv_len = sizeof(nowstr); + slap_timestamp( &now, ×tamp ); + + m = ch_calloc( sizeof(Modifications), 1 ); + m->sml_op = LDAP_MOD_REPLACE; + m->sml_flags = 0; + m->sml_type = ad_authTimestamp->ad_cname; + m->sml_desc = ad_authTimestamp; + m->sml_numvals = 1; + m->sml_values = ch_calloc( sizeof(struct berval), 2 ); + m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 ); + + ber_dupbv( &m->sml_values[0], ×tamp ); + ber_dupbv( &m->sml_nvalues[0], ×tamp ); + m->sml_next = mod; + mod = m; + + /* get authTimestamp attribute, if it exists */ + if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL && op->o_callback->sc_private) { + struct berval *bv = op->o_callback->sc_private; + m = ch_calloc( sizeof(Modifications), 1 ); + m->sml_op = LDAP_MOD_DELETE; + m->sml_flags = 0; + m->sml_type = ad_authTimestamp->ad_cname; + m->sml_desc = ad_authTimestamp; + m->sml_numvals = 1; + m->sml_values = ch_calloc( sizeof(struct berval), 2 ); + m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 ); + + ber_dupbv( &m->sml_values[0], bv ); + ber_dupbv( &m->sml_nvalues[0], bv ); + m->sml_next = mod; + mod = m; + } + } + + be_entry_release_r( op, e ); + + /* perform the update */ + if ( mod ) { + Operation op2 = *op; + SlapReply r2 = { REP_RESULT }; + slap_callback cb = { NULL, slap_null_cb, NULL, NULL }; + + /* This is a DSA-specific opattr, it never gets replicated. */ + op2.o_tag = LDAP_REQ_MODIFY; + op2.o_callback = &cb; + op2.orm_modlist = mod; + op2.o_dn = op->o_bd->be_rootdn; + op2.o_ndn = op->o_bd->be_rootndn; + op2.o_dont_replicate = 1; + rc = op->o_bd->be_modify( &op2, &r2 ); + slap_mods_free( mod, 1 ); + if (rc != LDAP_SUCCESS) { + /* slapd has logged this as a success already, but we + * need to fail it because the authTimestamp changed + * out from under us. + */ + rs->sr_err = LDAP_INVALID_CREDENTIALS; + connection2anonymous(op->o_conn); + op2 = *op; + op2.o_callback = NULL; + send_ldap_result(&op2, rs); + op->o_bd->bd_info = bi; + return rs->sr_err; + } + } + + op->o_bd->bd_info = bi; + return SLAP_CB_CONTINUE; +} + +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_response = totp_bind_response; + 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) { + rc = register_at(totp_OpSchema[0].def, totp_OpSchema[0].ad, 0 ); + if (rc) { + snprintf(cr->msg, sizeof(cr->msg), "unable to find or register authTimestamp attribute: %s (%d)", + text, rc); + Debug(LDAP_DEBUG_ANY, "totp: %s.\n", cr->msg ); + } + ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE; + } + } + 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) + 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; + + return overlay_register(&totp); +} + +int init_module(int argc, char *argv[]) { + return totp_initialize(); +} -- cgit v1.2.3