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>.
18 * This work includes code from the lastbind overlay.
26 #include <ac/stdlib.h>
28 #include <ac/string.h>
29 /* include socket.h to get sys/types.h and/or winsock2.h */
30 #include <ac/socket.h>
32 #include <openssl/sha.h>
33 #include <openssl/hmac.h>
38 static LUTIL_PASSWD_CHK_FUNC chk_totp1, chk_totp256, chk_totp512;
39 static LUTIL_PASSWD_HASH_FUNC hash_totp1, hash_totp256, hash_totp512;
40 static const struct berval scheme_totp1 = BER_BVC("{TOTP1}");
41 static const struct berval scheme_totp256 = BER_BVC("{TOTP256}");
42 static const struct berval scheme_totp512 = BER_BVC("{TOTP512}");
44 static AttributeDescription *ad_authTimestamp;
46 /* This is the definition used by ISODE, as supplied to us in
47 * ITS#6238 Followup #9
49 static struct schema_info {
51 AttributeDescription **ad;
53 { "( 1.3.6.1.4.1.453.16.2.188 "
54 "NAME 'authTimestamp' "
55 "DESC 'last successful authentication using any method/mech' "
56 "EQUALITY generalizedTimeMatch "
57 "ORDERING generalizedTimeOrderingMatch "
58 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
59 "SINGLE-VALUE NO-USER-MODIFICATION USAGE dsaOperation )",
64 /* RFC3548 base32 encoding/decoding */
66 static const char Base32[] =
67 "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
68 static const char Pad32 = '=';
77 size_t datalength = 0;
79 u_int input1; /* assumed to be at least 32 bits */
83 while (4 < srclength) {
84 if (datalength + 8 > targsize)
97 output[i] = input1 & 0x1f;
100 output[0] = input0 >> 3;
101 output[1] = (input0 & 0x07) << 2 | input1;
104 target[datalength++] = Base32[output[i]];
107 /* Now we worry about padding. */
108 if (0 != srclength) {
109 static const int outlen[] = { 2,4,5,7 };
111 if (datalength + 8 > targsize)
114 /* Get what's left. */
116 for (i = 1; i < srclength; i++) {
120 input1 <<= 8 * (4-srclength);
121 n = outlen[srclength-1];
122 for (i=0; i<n; i++) {
123 target[datalength++] = Base32[(input1 & 0xf8000000) >> 27];
127 target[datalength++] = Pad32;
129 if (datalength >= targsize)
131 target[datalength] = '\0'; /* Returned value doesn't count \0. */
135 /* converts characters, eight at a time, starting at src
136 from base - 32 numbers into five 8 bit bytes in the target area.
137 it returns the number of data bytes stored at the target, or -1 on error.
146 int tarindex, state, ch;
152 while ((ch = *src++) != '\0') {
156 pos = strchr(Base32, ch);
157 if (pos == 0) /* A non-base32 character. */
163 if ((size_t)tarindex >= targsize)
165 target[tarindex] = (pos - Base32) << 3;
171 if ((size_t)tarindex + 1 >= targsize)
173 target[tarindex] |= (pos - Base32) >> 2;
174 target[tarindex+1] = ((pos - Base32) & 0x3)
182 target[tarindex] |= (pos - Base32) << 1;
188 if ((size_t)tarindex + 1 >= targsize)
190 target[tarindex] |= (pos - Base32) >> 4;
191 target[tarindex+1] = ((pos - Base32) & 0xf)
199 if ((size_t)tarindex + 1 >= targsize)
201 target[tarindex] |= (pos - Base32) >> 1;
202 target[tarindex+1] = ((pos - Base32) & 0x1)
210 target[tarindex] |= (pos - Base32) << 2;
216 if ((size_t)tarindex + 1 >= targsize)
218 target[tarindex] |= (pos - Base32) >> 3;
219 target[tarindex+1] = ((pos - Base32) & 0x7)
227 target[tarindex] |= (pos - Base32);
239 * We are done decoding Base-32 chars. Let's see if we ended
240 * on a byte boundary, and/or with erroneous trailing characters.
243 if (ch == Pad32) { /* We got a pad char. */
246 /* count pad chars */
247 for (; ch; ch = *src++) {
252 /* there are only 4 valid ending states with a
253 * pad character, make sure the number of pads is valid.
256 case 2: if (i != 6) return -1;
258 case 4: if (i != 4) return -1;
260 case 5: if (i != 3) return -1;
262 case 7: if (i != 1) return -1;
268 * Now make sure that the "extra" bits that slopped past
269 * the last full byte were zeros. If we don't check them,
270 * they become a subliminal channel.
272 if (target && target[tarindex] != 0)
276 * We ended by seeing the end of the string. Make sure we
277 * have no partial bytes lying around.
288 #define HMAC_setup(ctx, key, len, hash) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, key, len, hash, 0)
289 #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, buf, len)
290 #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, dig, &dlen); HMAC_CTX_cleanup(&ctx)
292 typedef struct myval {
304 unsigned int digestLen;
306 HMAC_setup(ctx, key->mv_val, key->mv_len, hash);
307 HMAC_crunch(ctx, data->mv_val, data->mv_len);
308 HMAC_finish(ctx, out->mv_val, digestLen);
309 out->mv_len = digestLen;
312 static const int DIGITS_POWER[] = {
313 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
315 static void generate(
322 unsigned char digest[SHA512_DIGEST_LENGTH];
325 unsigned char msg[8];
326 int i, offset, res, otp;
329 /* only needed on little-endian, can just use tval directly on big-endian */
330 for (i=7; i>=0; i--) {
331 msg[i] = tval & 0xff;
337 data.mv_len = sizeof(msg);
339 digval.mv_val = digest;
340 digval.mv_len = sizeof(digest);
341 do_hmac(mech, key, &data, &digval);
343 offset = digest[digval.mv_len-1] & 0xf;
344 res = ((digest[offset] & 0x7f) << 24) |
345 ((digest[offset+1] & 0xff) << 16) |
346 ((digest[offset+2] & 0xff) << 8) |
347 (digest[offset+3] & 0xff);
349 otp = res % DIGITS_POWER[digits];
350 out->mv_len = snprintf(out->mv_val, out->mv_len, "%0*d", digits, otp);
353 static int totp_op_cleanup( Operation *op, SlapReply *rs );
354 static int totp_bind_response( Operation *op, SlapReply *rs );
360 const struct berval *passwd,
361 const struct berval *cred,
369 long t = time(0L) / TIME_STEP;
374 /* Find our thread context, find our Operation */
375 ctx = ldap_pvt_thread_pool_context();
376 if (ldap_pvt_thread_pool_getkey(ctx, totp_op_cleanup, &op_tmp, NULL) ||
378 return LUTIL_PASSWD_ERR;
381 rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
382 if (rc != LDAP_SUCCESS) return LUTIL_PASSWD_ERR;
384 /* Make sure previous login is older than current time */
385 a = attr_find(e->e_attrs, ad_authTimestamp);
388 struct lutil_timet tt;
389 if (lutil_parsetime(a->a_vals[0].bv_val, &tm) == 0 &&
390 lutil_tm2time(&tm, &tt) == 0) {
391 long told = tt.tt_sec / TIME_STEP;
393 rc = LUTIL_PASSWD_ERR;
395 if (!rc) { /* seems OK, remember old stamp */
397 for (sc = op->o_callback; sc; sc = sc->sc_next) {
398 if (sc->sc_response == totp_bind_response) {
399 sc->sc_private = ber_dupbv_x(NULL, &a->a_vals[0], op->o_tmpmemctx);
404 } /* else no previous login, 1st use is OK */
406 be_entry_release_r(op, e);
409 /* Key is stored in base32 */
410 key.mv_len = passwd->bv_len * 5 / 8;
411 key.mv_val = ber_memalloc(key.mv_len+1);
414 return LUTIL_PASSWD_ERR;
416 rc = totp_b32_pton(passwd->bv_val, key.mv_val, key.mv_len);
418 rc = LUTIL_PASSWD_ERR;
423 out.mv_len = sizeof(outbuf);
424 generate(&key, t, DIGITS, &out, mech);
425 memset(key.mv_val, 0, key.mv_len);
428 if (out.mv_len != cred->bv_len) {
429 rc = LUTIL_PASSWD_ERR;
433 rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
436 ber_memfree(key.mv_val);
440 static int chk_totp1(
441 const struct berval *scheme,
442 const struct berval *passwd,
443 const struct berval *cred,
446 return chk_totp(passwd, cred, EVP_sha1(), text);
449 static int chk_totp256(
450 const struct berval *scheme,
451 const struct berval *passwd,
452 const struct berval *cred,
455 return chk_totp(passwd, cred, EVP_sha256(), text);
458 static int chk_totp512(
459 const struct berval *scheme,
460 const struct berval *passwd,
461 const struct berval *cred,
464 return chk_totp(passwd, cred, EVP_sha512(), text);
467 static int passwd_string32(
468 const struct berval *scheme,
469 const struct berval *passwd,
472 int b32len = (passwd->bv_len + 4)/5 * 8;
474 hash->bv_len = scheme->bv_len + b32len;
475 hash->bv_val = ber_memalloc(hash->bv_len + 1);
476 AC_MEMCPY(hash->bv_val, scheme->bv_val, scheme->bv_len);
477 rc = totp_b32_ntop((unsigned char *)passwd->bv_val, passwd->bv_len,
478 hash->bv_val + scheme->bv_len, b32len+1);
480 ber_memfree(hash->bv_val);
482 return LUTIL_PASSWD_ERR;
484 return LUTIL_PASSWD_OK;
487 static int hash_totp1(
488 const struct berval *scheme,
489 const struct berval *passwd,
494 if (passwd->bv_len != SHA_DIGEST_LENGTH) {
495 *text = "invalid key length";
496 return LUTIL_PASSWD_ERR;
499 return passwd_string32(scheme, passwd, hash);
502 static int hash_totp256(
503 const struct berval *scheme,
504 const struct berval *passwd,
509 if (passwd->bv_len != SHA256_DIGEST_LENGTH) {
510 *text = "invalid key length";
511 return LUTIL_PASSWD_ERR;
514 return passwd_string32(scheme, passwd, hash);
517 static int hash_totp512(
518 const struct berval *scheme,
519 const struct berval *passwd,
524 if (passwd->bv_len != SHA512_DIGEST_LENGTH) {
525 *text = "invalid key length";
526 return LUTIL_PASSWD_ERR;
529 return passwd_string32(scheme, passwd, hash);
532 static int totp_op_cleanup(
538 /* clear out the current key */
539 ldap_pvt_thread_pool_setkey( op->o_threadctx, totp_op_cleanup,
540 NULL, 0, NULL, NULL );
542 /* free the callback */
544 op->o_callback = cb->sc_next;
546 ber_bvfree_x(cb->sc_private, op->o_tmpmemctx);
547 op->o_tmpfree( cb, op->o_tmpmemctx );
552 totp_bind_response( Operation *op, SlapReply *rs )
554 Modifications *mod = NULL;
555 BackendInfo *bi = op->o_bd->bd_info;
559 /* we're only interested if the bind was successful */
560 if ( rs->sr_err != LDAP_SUCCESS )
561 return SLAP_CB_CONTINUE;
563 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
564 op->o_bd->bd_info = bi;
566 if ( rc != LDAP_SUCCESS ) {
567 return SLAP_CB_CONTINUE;
574 char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
575 struct berval timestamp;
577 /* get the current time */
580 /* update the authTimestamp in the user's entry with the current time */
581 timestamp.bv_val = nowstr;
582 timestamp.bv_len = sizeof(nowstr);
583 slap_timestamp( &now, ×tamp );
585 m = ch_calloc( sizeof(Modifications), 1 );
586 m->sml_op = LDAP_MOD_REPLACE;
588 m->sml_type = ad_authTimestamp->ad_cname;
589 m->sml_desc = ad_authTimestamp;
591 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
592 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
594 ber_dupbv( &m->sml_values[0], ×tamp );
595 ber_dupbv( &m->sml_nvalues[0], ×tamp );
599 /* get authTimestamp attribute, if it exists */
600 if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL && op->o_callback->sc_private) {
601 struct berval *bv = op->o_callback->sc_private;
602 m = ch_calloc( sizeof(Modifications), 1 );
603 m->sml_op = LDAP_MOD_DELETE;
605 m->sml_type = ad_authTimestamp->ad_cname;
606 m->sml_desc = ad_authTimestamp;
608 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
609 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
611 ber_dupbv( &m->sml_values[0], bv );
612 ber_dupbv( &m->sml_nvalues[0], bv );
619 be_entry_release_r( op, e );
621 /* perform the update */
624 SlapReply r2 = { REP_RESULT };
625 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
627 /* This is a DSA-specific opattr, it never gets replicated. */
628 op2.o_tag = LDAP_REQ_MODIFY;
629 op2.o_callback = &cb;
630 op2.orm_modlist = mod;
631 op2.o_dn = op->o_bd->be_rootdn;
632 op2.o_ndn = op->o_bd->be_rootndn;
633 op2.o_dont_replicate = 1;
634 rc = op->o_bd->be_modify( &op2, &r2 );
635 slap_mods_free( mod, 1 );
636 if (rc != LDAP_SUCCESS) {
637 /* slapd has logged this as a success already, but we
638 * need to fail it because the authTimestamp changed
641 rs->sr_err = LDAP_INVALID_CREDENTIALS;
642 connection2anonymous(op->o_conn);
644 op2.o_callback = NULL;
645 send_ldap_result(&op2, rs);
646 op->o_bd->bd_info = bi;
651 op->o_bd->bd_info = bi;
652 return SLAP_CB_CONTINUE;
655 static int totp_op_bind(
659 /* If this is a simple Bind, stash the Op pointer so our chk
660 * function can find it. Set a cleanup callback to clear it
661 * out when the Bind completes.
663 if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) {
665 ldap_pvt_thread_pool_setkey( op->o_threadctx,
666 totp_op_cleanup, op, 0, NULL, NULL );
667 cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
668 cb->sc_response = totp_bind_response;
669 cb->sc_cleanup = totp_op_cleanup;
670 cb->sc_next = op->o_callback;
673 return SLAP_CB_CONTINUE;
676 static int totp_db_open(
683 if (!ad_authTimestamp) {
684 const char *text = NULL;
685 rc = slap_str2ad("authTimestamp", &ad_authTimestamp, &text);
687 rc = register_at(totp_OpSchema[0].def, totp_OpSchema[0].ad, 0 );
689 snprintf(cr->msg, sizeof(cr->msg), "unable to find or register authTimestamp attribute: %s (%d)",
691 Debug(LDAP_DEBUG_ANY, "totp: %s.\n", cr->msg, 0, 0);
693 ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE;
699 static slap_overinst totp;
702 totp_initialize(void)
706 totp.on_bi.bi_type = "totp";
708 totp.on_bi.bi_db_open = totp_db_open;
709 totp.on_bi.bi_op_bind = totp_op_bind;
711 rc = lutil_passwd_add((struct berval *) &scheme_totp1, chk_totp1, hash_totp1);
713 rc = lutil_passwd_add((struct berval *) &scheme_totp256, chk_totp256, hash_totp256);
715 rc = lutil_passwd_add((struct berval *) &scheme_totp512, chk_totp512, hash_totp512);
719 return overlay_register(&totp);
722 int init_module(int argc, char *argv[]) {
723 return totp_initialize();