1 /* slapd-totp.c - Password module and overlay for TOTP */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2015 The OpenLDAP Foundation.
6 * Portions Copyright 2015 by Howard Chu, Symas Corp.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
23 #include <ac/stdlib.h>
25 #include <ac/string.h>
26 /* include socket.h to get sys/types.h and/or winsock2.h */
27 #include <ac/socket.h>
29 #include <openssl/sha.h>
30 #include <openssl/hmac.h>
35 static LUTIL_PASSWD_CHK_FUNC chk_totp1, chk_totp256, chk_totp512;
36 static LUTIL_PASSWD_HASH_FUNC hash_totp1, hash_totp256, hash_totp512;
37 static const struct berval scheme_totp1 = BER_BVC("{TOTP1}");
38 static const struct berval scheme_totp256 = BER_BVC("{TOTP256}");
39 static const struct berval scheme_totp512 = BER_BVC("{TOTP512}");
41 static AttributeDescription *ad_authTimestamp;
43 /* RFC3548 base32 encoding/decoding */
45 static const char Base32[] =
46 "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
47 static const char Pad32 = '=';
56 size_t datalength = 0;
58 u_int input1; /* assumed to be at least 32 bits */
62 while (4 < srclength) {
63 if (datalength + 8 > targsize)
76 output[i] = input1 & 0x1f;
79 output[0] = input0 >> 3;
80 output[1] = (input0 & 0x07) << 2 | input1;
83 target[datalength++] = Base32[output[i]];
86 /* Now we worry about padding. */
88 static const int outlen[] = { 2,4,5,7 };
90 if (datalength + 8 > targsize)
93 /* Get what's left. */
95 for (i = 1; i < srclength; i++) {
99 input1 <<= 8 * (4-srclength);
100 n = outlen[srclength-1];
101 for (i=0; i<n; i++) {
102 target[datalength++] = Base32[(input1 & 0xf8000000) >> 27];
106 target[datalength++] = Pad32;
108 if (datalength >= targsize)
110 target[datalength] = '\0'; /* Returned value doesn't count \0. */
114 /* converts characters, eight at a time, starting at src
115 from base - 32 numbers into five 8 bit bytes in the target area.
116 it returns the number of data bytes stored at the target, or -1 on error.
125 int tarindex, state, ch;
131 while ((ch = *src++) != '\0') {
135 pos = strchr(Base32, ch);
136 if (pos == 0) /* A non-base32 character. */
142 if ((size_t)tarindex >= targsize)
144 target[tarindex] = (pos - Base32) << 3;
150 if ((size_t)tarindex + 1 >= targsize)
152 target[tarindex] |= (pos - Base32) >> 2;
153 target[tarindex+1] = ((pos - Base32) & 0x3)
161 target[tarindex] |= (pos - Base32) << 1;
167 if ((size_t)tarindex + 1 >= targsize)
169 target[tarindex] |= (pos - Base32) >> 4;
170 target[tarindex+1] = ((pos - Base32) & 0xf)
178 if ((size_t)tarindex + 1 >= targsize)
180 target[tarindex] |= (pos - Base32) >> 1;
181 target[tarindex+1] = ((pos - Base32) & 0x1)
189 target[tarindex] |= (pos - Base32) << 2;
195 if ((size_t)tarindex + 1 >= targsize)
197 target[tarindex] |= (pos - Base32) >> 3;
198 target[tarindex+1] = ((pos - Base32) & 0x7)
206 target[tarindex] |= (pos - Base32);
218 * We are done decoding Base-32 chars. Let's see if we ended
219 * on a byte boundary, and/or with erroneous trailing characters.
222 if (ch == Pad32) { /* We got a pad char. */
225 /* count pad chars */
226 for (; ch; ch = *src++) {
231 /* there are only 4 valid ending states with a
232 * pad character, make sure the number of pads is valid.
235 case 2: if (i != 6) return -1;
237 case 4: if (i != 4) return -1;
239 case 5: if (i != 3) return -1;
241 case 7: if (i != 1) return -1;
247 * Now make sure that the "extra" bits that slopped past
248 * the last full byte were zeros. If we don't check them,
249 * they become a subliminal channel.
251 if (target && target[tarindex] != 0)
255 * We ended by seeing the end of the string. Make sure we
256 * have no partial bytes lying around.
267 #define HMAC_setup(ctx, key, len, hash) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, key, len, hash, 0)
268 #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, buf, len)
269 #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, dig, &dlen); HMAC_CTX_cleanup(&ctx)
271 typedef struct myval {
283 unsigned int digestLen;
285 HMAC_setup(ctx, key->mv_val, key->mv_len, hash);
286 HMAC_crunch(ctx, data->mv_val, data->mv_len);
287 HMAC_finish(ctx, out->mv_val, digestLen);
288 out->mv_len = digestLen;
291 static const int DIGITS_POWER[] = {
292 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
294 static void generate(
301 unsigned char digest[SHA512_DIGEST_LENGTH];
304 unsigned char msg[8];
305 int i, offset, res, otp;
308 /* only needed on little-endian, can just use tval directly on big-endian */
309 for (i=7; i>=0; i--) {
310 msg[i] = tval & 0xff;
316 data.mv_len = sizeof(msg);
318 digval.mv_val = digest;
319 digval.mv_len = sizeof(digest);
320 do_hmac(mech, key, &data, &digval);
322 offset = digest[digval.mv_len-1] & 0xf;
323 res = ((digest[offset] & 0x7f) << 24) |
324 ((digest[offset+1] & 0xff) << 16) |
325 ((digest[offset+2] & 0xff) << 8) |
326 (digest[offset+3] & 0xff);
328 otp = res % DIGITS_POWER[digits];
329 out->mv_len = snprintf(out->mv_val, out->mv_len, "%0*d", digits, otp);
332 static int totp_op_cleanup( Operation *op, SlapReply *rs );
338 const struct berval *passwd,
339 const struct berval *cred,
347 long t = time(0L) / TIME_STEP;
352 /* Find our thread context, find our Operation */
353 ctx = ldap_pvt_thread_pool_context();
354 if (ldap_pvt_thread_pool_getkey(ctx, totp_op_cleanup, &op_tmp, NULL) ||
356 return LUTIL_PASSWD_ERR;
359 rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
360 if (rc != LDAP_SUCCESS) return LUTIL_PASSWD_ERR;
362 /* Make sure previous login is older than current time */
363 a = attr_find(e->e_attrs, ad_authTimestamp);
366 struct lutil_timet tt;
367 if (lutil_parsetime(a->a_vals[0].bv_val, &tm) == 0 &&
368 lutil_tm2time(&tm, &tt) == 0) {
369 long told = tt.tt_sec / TIME_STEP;
371 rc = LUTIL_PASSWD_ERR;
373 } /* else no previous login, 1st use is OK */
375 be_entry_release_r(op, e);
378 /* Key is stored in base32 */
379 key.mv_len = passwd->bv_len * 5 / 8;
380 key.mv_val = ber_memalloc(key.mv_len+1);
383 return LUTIL_PASSWD_ERR;
385 rc = totp_b32_pton(passwd->bv_val, key.mv_val, key.mv_len);
387 rc = LUTIL_PASSWD_ERR;
392 out.mv_len = sizeof(outbuf);
393 generate(&key, t, DIGITS, &out, mech);
394 memset(key.mv_val, 0, key.mv_len);
397 if (out.mv_len != cred->bv_len)
398 return LUTIL_PASSWD_ERR;
400 rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
403 ber_memfree(key.mv_val);
407 static int chk_totp1(
408 const struct berval *scheme,
409 const struct berval *passwd,
410 const struct berval *cred,
413 return chk_totp(passwd, cred, EVP_sha1(), text);
416 static int chk_totp256(
417 const struct berval *scheme,
418 const struct berval *passwd,
419 const struct berval *cred,
422 return chk_totp(passwd, cred, EVP_sha256(), text);
425 static int chk_totp512(
426 const struct berval *scheme,
427 const struct berval *passwd,
428 const struct berval *cred,
431 return chk_totp(passwd, cred, EVP_sha512(), text);
434 static int passwd_string32(
435 const struct berval *scheme,
436 const struct berval *passwd,
439 int b32len = (passwd->bv_len + 4)/5 * 8;
441 hash->bv_len = scheme->bv_len + b32len;
442 hash->bv_val = ber_memalloc(hash->bv_len + 1);
443 AC_MEMCPY(hash->bv_val, scheme->bv_val, scheme->bv_len);
444 rc = totp_b32_ntop((unsigned char *)passwd->bv_val, passwd->bv_len,
445 hash->bv_val + scheme->bv_len, b32len+1);
447 ber_memfree(hash->bv_val);
449 return LUTIL_PASSWD_ERR;
451 return LUTIL_PASSWD_OK;
454 static int hash_totp1(
455 const struct berval *scheme,
456 const struct berval *passwd,
461 if (passwd->bv_len != SHA_DIGEST_LENGTH) {
462 *text = "invalid key length";
463 return LUTIL_PASSWD_ERR;
466 return passwd_string32(scheme, passwd, hash);
469 static int hash_totp256(
470 const struct berval *scheme,
471 const struct berval *passwd,
476 if (passwd->bv_len != SHA256_DIGEST_LENGTH) {
477 *text = "invalid key length";
478 return LUTIL_PASSWD_ERR;
481 return passwd_string32(scheme, passwd, hash);
484 static int hash_totp512(
485 const struct berval *scheme,
486 const struct berval *passwd,
491 if (passwd->bv_len != SHA512_DIGEST_LENGTH) {
492 *text = "invalid key length";
493 return LUTIL_PASSWD_ERR;
496 return passwd_string32(scheme, passwd, hash);
499 static int totp_op_cleanup(
505 /* clear out the current key */
506 ldap_pvt_thread_pool_setkey( op->o_threadctx, totp_op_cleanup,
507 NULL, 0, NULL, NULL );
509 /* free the callback */
511 op->o_callback = cb->sc_next;
512 op->o_tmpfree( cb, op->o_tmpmemctx );
516 static int totp_op_bind(
520 /* If this is a simple Bind, stash the Op pointer so our chk
521 * function can find it. Set a cleanup callback to clear it
522 * out when the Bind completes.
524 if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) {
526 ldap_pvt_thread_pool_setkey( op->o_threadctx,
527 totp_op_cleanup, op, 0, NULL, NULL );
528 cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
529 cb->sc_cleanup = totp_op_cleanup;
530 cb->sc_next = op->o_callback;
533 return SLAP_CB_CONTINUE;
536 static int totp_db_open(
543 if (!ad_authTimestamp) {
544 const char *text = NULL;
545 rc = slap_str2ad("authTimestamp", &ad_authTimestamp, &text);
547 snprintf(cr->msg, sizeof(cr->msg), "unable to find authTimestamp attribute: %s (%d)",
549 Debug(LDAP_DEBUG_ANY, "totp: %s.\n", cr->msg, 0, 0);
555 static slap_overinst totp;
558 totp_initialize(void)
562 totp.on_bi.bi_type = "totp";
564 totp.on_bi.bi_db_open = totp_db_open;
565 totp.on_bi.bi_op_bind = totp_op_bind;
567 rc = lutil_passwd_add((struct berval *) &scheme_totp1, chk_totp1, hash_totp1);
569 rc = lutil_passwd_add((struct berval *) &scheme_totp256, chk_totp256, hash_totp256);
571 rc = lutil_passwd_add((struct berval *) &scheme_totp512, chk_totp512, hash_totp512);
575 return overlay_register(&totp);
578 int init_module(int argc, char *argv[]) {
579 return totp_initialize();