]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/passwd/totp/slapd-totp.c
Merge remote-tracking branch 'origin/mdb.RE/0.9'
[openldap] / contrib / slapd-modules / passwd / totp / slapd-totp.c
1 /* slapd-totp.c - Password module and overlay for TOTP */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2015 The OpenLDAP Foundation.
6  * Portions Copyright 2015 by Howard Chu, Symas Corp.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
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>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work includes code from the lastbind overlay.
19  */
20
21 #include <portable.h>
22
23 #if HAVE_STDINT_H
24 #include <stdint.h>
25 #endif
26
27 #include <lber.h>
28 #include <lber_pvt.h>
29 #include "lutil.h"
30 #include <ac/stdlib.h>
31 #include <ac/ctype.h>
32 #include <ac/string.h>
33 /* include socket.h to get sys/types.h and/or winsock2.h */
34 #include <ac/socket.h>
35
36 #if HAVE_OPENSSL
37 #include <openssl/sha.h>
38 #include <openssl/hmac.h>
39
40 #define TOTP_SHA512_DIGEST_LENGTH       SHA512_DIGEST_LENGTH
41 #define TOTP_SHA1       EVP_sha1()
42 #define TOTP_SHA256     EVP_sha256()
43 #define TOTP_SHA512     EVP_sha512()
44 #define TOTP_HMAC_CTX   HMAC_CTX
45
46 #define HMAC_setup(ctx, key, len, hash) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, key, len, hash, 0)
47 #define HMAC_crunch(ctx, buf, len)      HMAC_Update(&ctx, buf, len)
48 #define HMAC_finish(ctx, dig, dlen)     HMAC_Final(&ctx, dig, &dlen); HMAC_CTX_cleanup(&ctx)
49
50 #elif HAVE_GNUTLS
51 #include <nettle/hmac.h>
52
53 #define TOTP_SHA512_DIGEST_LENGTH       SHA512_DIGEST_SIZE
54 #define TOTP_SHA1       &nettle_sha1
55 #define TOTP_SHA256     &nettle_sha256
56 #define TOTP_SHA512     &nettle_sha512
57 #define TOTP_HMAC_CTX   struct hmac_sha512_ctx
58
59 #define HMAC_setup(ctx, key, len, hash) \
60         const struct nettle_hash *h=hash;\
61         hmac_set_key(&ctx.outer, &ctx.inner, &ctx.state, h, len, key)
62 #define HMAC_crunch(ctx, buf, len)      hmac_update(&ctx.state, h, len, buf)
63 #define HMAC_finish(ctx, dig, dlen) \
64         hmac_digest(&ctx.outer, &ctx.inner, &ctx.state, h, h->digest_size, dig);\
65         dlen = h->digest_size
66
67 #else
68 # error Unsupported crypto backend.
69 #endif
70
71 #include "slap.h"
72 #include "config.h"
73
74 static LUTIL_PASSWD_CHK_FUNC chk_totp1, chk_totp256, chk_totp512;
75 static LUTIL_PASSWD_HASH_FUNC hash_totp1, hash_totp256, hash_totp512;
76 static const struct berval scheme_totp1 = BER_BVC("{TOTP1}");
77 static const struct berval scheme_totp256 = BER_BVC("{TOTP256}");
78 static const struct berval scheme_totp512 = BER_BVC("{TOTP512}");
79
80 static AttributeDescription *ad_authTimestamp;
81
82 /* This is the definition used by ISODE, as supplied to us in
83  * ITS#6238 Followup #9
84  */
85 static struct schema_info {
86         char *def;
87         AttributeDescription **ad;
88 } totp_OpSchema[] = {
89         {       "( 1.3.6.1.4.1.453.16.2.188 "
90                 "NAME 'authTimestamp' "
91                 "DESC 'last successful authentication using any method/mech' "
92                 "EQUALITY generalizedTimeMatch "
93                 "ORDERING generalizedTimeOrderingMatch "
94                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
95                 "SINGLE-VALUE NO-USER-MODIFICATION USAGE dsaOperation )",
96                 &ad_authTimestamp},
97         { NULL, NULL }
98 };
99
100 /* RFC3548 base32 encoding/decoding */
101
102 static const char Base32[] =
103         "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
104 static const char Pad32 = '=';
105
106 static int
107 totp_b32_ntop(
108         u_char const *src,
109         size_t srclength,
110         char *target,
111         size_t targsize)
112 {
113         size_t datalength = 0;
114         u_char input0;
115         u_int input1;   /* assumed to be at least 32 bits */
116         u_char output[8];
117         int i;
118
119         while (4 < srclength) {
120                 if (datalength + 8 > targsize)
121                         return (-1);
122                 input0 = *src++;
123                 input1 = *src++;
124                 input1 <<= 8;
125                 input1 |= *src++;
126                 input1 <<= 8;
127                 input1 |= *src++;
128                 input1 <<= 8;
129                 input1 |= *src++;
130                 srclength -= 5;
131
132                 for (i=7; i>1; i--) {
133                         output[i] = input1 & 0x1f;
134                         input1 >>= 5;
135                 }
136                 output[0] = input0 >> 3;
137                 output[1] = (input0 & 0x07) << 2 | input1;
138
139                 for (i=0; i<8; i++)
140                         target[datalength++] = Base32[output[i]];
141         }
142     
143         /* Now we worry about padding. */
144         if (0 != srclength) {
145                 static const int outlen[] = { 2,4,5,7 };
146                 int n;
147                 if (datalength + 8 > targsize)
148                         return (-1);
149
150                 /* Get what's left. */
151                 input1 = *src++;
152                 for (i = 1; i < srclength; i++) {
153                         input1 <<= 8;
154                         input1 |= *src++;
155                 }
156                 input1 <<= 8 * (4-srclength);
157                 n = outlen[srclength-1];
158                 for (i=0; i<n; i++) {
159                         target[datalength++] = Base32[(input1 & 0xf8000000) >> 27];
160                         input1 <<= 5;
161                 }
162                 for (; i<8; i++)
163                         target[datalength++] = Pad32;
164         }
165         if (datalength >= targsize)
166                 return (-1);
167         target[datalength] = '\0';      /* Returned value doesn't count \0. */
168         return (datalength);
169 }
170
171 /* converts characters, eight at a time, starting at src
172    from base - 32 numbers into five 8 bit bytes in the target area.
173    it returns the number of data bytes stored at the target, or -1 on error.
174  */
175
176 static int
177 totp_b32_pton(
178         char const *src,
179         u_char *target, 
180         size_t targsize)
181 {
182         int tarindex, state, ch;
183         char *pos;
184
185         state = 0;
186         tarindex = 0;
187
188         while ((ch = *src++) != '\0') {
189                 if (ch == Pad32)
190                         break;
191
192                 pos = strchr(Base32, ch);
193                 if (pos == 0)           /* A non-base32 character. */
194                         return (-1);
195
196                 switch (state) {
197                 case 0:
198                         if (target) {
199                                 if ((size_t)tarindex >= targsize)
200                                         return (-1);
201                                 target[tarindex] = (pos - Base32) << 3;
202                         }
203                         state = 1;
204                         break;
205                 case 1:
206                         if (target) {
207                                 if ((size_t)tarindex + 1 >= targsize)
208                                         return (-1);
209                                 target[tarindex]   |=  (pos - Base32) >> 2;
210                                 target[tarindex+1]  = ((pos - Base32) & 0x3)
211                                                         << 6 ;
212                         }
213                         tarindex++;
214                         state = 2;
215                         break;
216                 case 2:
217                         if (target) {
218                                 target[tarindex]   |=  (pos - Base32) << 1;
219                         }
220                         state = 3;
221                         break;
222                 case 3:
223                         if (target) {
224                                 if ((size_t)tarindex + 1 >= targsize)
225                                         return (-1);
226                                 target[tarindex] |= (pos - Base32) >> 4;
227                                 target[tarindex+1]  = ((pos - Base32) & 0xf)
228                                                         << 4 ;
229                         }
230                         tarindex++;
231                         state = 4;
232                         break;
233                 case 4:
234                         if (target) {
235                                 if ((size_t)tarindex + 1 >= targsize)
236                                         return (-1);
237                                 target[tarindex] |= (pos - Base32) >> 1;
238                                 target[tarindex+1]  = ((pos - Base32) & 0x1)
239                                                         << 7 ;
240                         }
241                         tarindex++;
242                         state = 5;
243                         break;
244                 case 5:
245                         if (target) {
246                                 target[tarindex]   |=  (pos - Base32) << 2;
247                         }
248                         state = 6;
249                         break;
250                 case 6:
251                         if (target) {
252                                 if ((size_t)tarindex + 1 >= targsize)
253                                         return (-1);
254                                 target[tarindex] |= (pos - Base32) >> 3;
255                                 target[tarindex+1]  = ((pos - Base32) & 0x7)
256                                                         << 5 ;
257                         }
258                         tarindex++;
259                         state = 7;
260                         break;
261                 case 7:
262                         if (target) {
263                                 target[tarindex]   |=  (pos - Base32);
264                         }
265                         state = 0;
266                         tarindex++;
267                         break;
268
269                 default:
270                         abort();
271                 }
272         }
273
274         /*
275          * We are done decoding Base-32 chars.  Let's see if we ended
276          * on a byte boundary, and/or with erroneous trailing characters.
277          */
278
279         if (ch == Pad32) {              /* We got a pad char. */
280                 int i = 0;
281
282                 /* count pad chars */
283                 for (; ch; ch = *src++) {
284                         if (ch != Pad32)
285                                 return (-1);
286                         i++;
287                 }
288                 /* there are only 4 valid ending states with a
289                  * pad character, make sure the number of pads is valid.
290                  */
291                 switch(state) {
292                 case 2: if (i != 6) return -1;
293                         break;
294                 case 4: if (i != 4) return -1;
295                         break;
296                 case 5: if (i != 3) return -1;
297                         break;
298                 case 7: if (i != 1) return -1;
299                         break;
300                 default:
301                         return -1;
302                 }
303                 /*
304                  * Now make sure that the "extra" bits that slopped past
305                  * the last full byte were zeros.  If we don't check them,
306                  * they become a subliminal channel.
307                  */
308                 if (target && target[tarindex] != 0)
309                         return (-1);
310         } else {
311                 /*
312                  * We ended by seeing the end of the string.  Make sure we
313                  * have no partial bytes lying around.
314                  */
315                 if (state != 0)
316                         return (-1);
317         }
318
319         return (tarindex);
320 }
321
322 /* RFC6238 TOTP */
323
324
325 typedef struct myval {
326         ber_len_t mv_len;
327         void *mv_val;
328 } myval;
329
330 static void do_hmac(
331         const void *hash,
332         myval *key,
333         myval *data,
334         myval *out)
335 {
336         TOTP_HMAC_CTX ctx;
337         unsigned int digestLen;
338
339         HMAC_setup(ctx, key->mv_val, key->mv_len, hash);
340         HMAC_crunch(ctx, data->mv_val, data->mv_len);
341         HMAC_finish(ctx, out->mv_val, digestLen);
342         out->mv_len = digestLen;
343 }
344
345 static const int DIGITS_POWER[] = {
346         1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
347
348 static void generate(
349         myval *key,
350         uint64_t tval,
351         int digits,
352         myval *out,
353         const void *mech)
354 {
355         unsigned char digest[TOTP_SHA512_DIGEST_LENGTH];
356         myval digval;
357         myval data;
358         unsigned char msg[8];
359         int i, offset, res, otp;
360
361 #if WORDS_BIGENDIAN
362         *(uint64_t *)msg = tval;
363 #else
364         for (i=7; i>=0; i--) {
365                 msg[i] = tval & 0xff;
366                 tval >>= 8;
367         }
368 #endif
369
370         data.mv_val = msg;
371         data.mv_len = sizeof(msg);
372
373         digval.mv_val = digest;
374         digval.mv_len = sizeof(digest);
375         do_hmac(mech, key, &data, &digval);
376
377         offset = digest[digval.mv_len-1] & 0xf;
378         res = ((digest[offset] & 0x7f) << 24) |
379                         ((digest[offset+1] & 0xff) << 16) |
380                         ((digest[offset+2] & 0xff) << 8) |
381                         (digest[offset+3] & 0xff);
382
383         otp = res % DIGITS_POWER[digits];
384         out->mv_len = snprintf(out->mv_val, out->mv_len, "%0*d", digits, otp);
385 }
386
387 static int totp_op_cleanup( Operation *op, SlapReply *rs );
388 static int totp_bind_response( Operation *op, SlapReply *rs );
389
390 #define TIME_STEP       30
391 #define DIGITS  6
392
393 static int chk_totp(
394         const struct berval *passwd,
395         const struct berval *cred,
396         const void *mech,
397         const char **text)
398 {
399         void *ctx, *op_tmp;
400         Operation *op;
401         Entry *e;
402         Attribute *a;
403         long t = time(0L) / TIME_STEP;
404         int rc;
405         myval out, key;
406         char outbuf[32];
407
408         /* Find our thread context, find our Operation */
409         ctx = ldap_pvt_thread_pool_context();
410         if (ldap_pvt_thread_pool_getkey(ctx, totp_op_cleanup, &op_tmp, NULL) ||
411                 !op_tmp)
412                 return LUTIL_PASSWD_ERR;
413         op = op_tmp;
414
415         rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
416         if (rc != LDAP_SUCCESS) return LUTIL_PASSWD_ERR;
417
418         /* Make sure previous login is older than current time */
419         a = attr_find(e->e_attrs, ad_authTimestamp);
420         if (a) {
421                 struct lutil_tm tm;
422                 struct lutil_timet tt;
423                 if (lutil_parsetime(a->a_vals[0].bv_val, &tm) == 0 &&
424                         lutil_tm2time(&tm, &tt) == 0) {
425                         long told = tt.tt_sec / TIME_STEP;
426                         if (told >= t)
427                                 rc = LUTIL_PASSWD_ERR;
428                 }
429                 if (!rc) {      /* seems OK, remember old stamp */
430                         slap_callback *sc;
431                         for (sc = op->o_callback; sc; sc = sc->sc_next) {
432                                 if (sc->sc_response == totp_bind_response) {
433                                         sc->sc_private = ber_dupbv_x(NULL, &a->a_vals[0], op->o_tmpmemctx);
434                                         break;
435                                 }
436                         }
437                 }
438         }       /* else no previous login, 1st use is OK */
439
440         be_entry_release_r(op, e);
441         if (rc) return rc;
442
443         /* Key is stored in base32 */
444         key.mv_len = passwd->bv_len * 5 / 8;
445         key.mv_val = ber_memalloc(key.mv_len+1);
446
447         if (!key.mv_val)
448                 return LUTIL_PASSWD_ERR;
449
450         rc = totp_b32_pton(passwd->bv_val, key.mv_val, key.mv_len);
451         if (rc < 1) {
452                 rc = LUTIL_PASSWD_ERR;
453                 goto out;
454         }
455
456         out.mv_val = outbuf;
457         out.mv_len = sizeof(outbuf);
458         generate(&key, t, DIGITS, &out, mech);
459         memset(key.mv_val, 0, key.mv_len);
460
461         /* compare */
462         if (out.mv_len != cred->bv_len) {
463                 rc = LUTIL_PASSWD_ERR;
464                 goto out;
465         }
466
467         rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
468
469 out:
470         ber_memfree(key.mv_val);
471         return rc;
472 }
473
474 static int chk_totp1(
475         const struct berval *scheme,
476         const struct berval *passwd,
477         const struct berval *cred,
478         const char **text)
479 {
480         return chk_totp(passwd, cred, TOTP_SHA1, text);
481 }
482
483 static int chk_totp256(
484         const struct berval *scheme,
485         const struct berval *passwd,
486         const struct berval *cred,
487         const char **text)
488 {
489         return chk_totp(passwd, cred, TOTP_SHA256, text);
490 }
491
492 static int chk_totp512(
493         const struct berval *scheme,
494         const struct berval *passwd,
495         const struct berval *cred,
496         const char **text)
497 {
498         return chk_totp(passwd, cred, TOTP_SHA512, text);
499 }
500
501 static int passwd_string32(
502         const struct berval *scheme,
503         const struct berval *passwd,
504         struct berval *hash)
505 {
506         int b32len = (passwd->bv_len + 4)/5 * 8;
507         int rc;
508         hash->bv_len = scheme->bv_len + b32len;
509         hash->bv_val = ber_memalloc(hash->bv_len + 1);
510         AC_MEMCPY(hash->bv_val, scheme->bv_val, scheme->bv_len);
511         rc = totp_b32_ntop((unsigned char *)passwd->bv_val, passwd->bv_len,
512                 hash->bv_val + scheme->bv_len, b32len+1);
513         if (rc < 0) {
514                 ber_memfree(hash->bv_val);
515                 hash->bv_val = NULL;
516                 return LUTIL_PASSWD_ERR;
517         }
518         return LUTIL_PASSWD_OK;
519 }
520
521 static int hash_totp1(
522         const struct berval *scheme,
523         const struct berval *passwd,
524         struct berval *hash,
525         const char **text)
526 {
527 #if 0
528         if (passwd->bv_len != SHA_DIGEST_LENGTH) {
529                 *text = "invalid key length";
530                 return LUTIL_PASSWD_ERR;
531         }
532 #endif
533         return passwd_string32(scheme, passwd, hash);
534 }
535
536 static int hash_totp256(
537         const struct berval *scheme,
538         const struct berval *passwd,
539         struct berval *hash,
540         const char **text)
541 {
542 #if 0
543         if (passwd->bv_len != SHA256_DIGEST_LENGTH) {
544                 *text = "invalid key length";
545                 return LUTIL_PASSWD_ERR;
546         }
547 #endif
548         return passwd_string32(scheme, passwd, hash);
549 }
550
551 static int hash_totp512(
552         const struct berval *scheme,
553         const struct berval *passwd,
554         struct berval *hash,
555         const char **text)
556 {
557 #if 0
558         if (passwd->bv_len != SHA512_DIGEST_LENGTH) {
559                 *text = "invalid key length";
560                 return LUTIL_PASSWD_ERR;
561         }
562 #endif
563         return passwd_string32(scheme, passwd, hash);
564 }
565
566 static int totp_op_cleanup(
567         Operation *op,
568         SlapReply *rs )
569 {
570         slap_callback *cb;
571
572         /* clear out the current key */
573         ldap_pvt_thread_pool_setkey( op->o_threadctx, totp_op_cleanup,
574                 NULL, 0, NULL, NULL );
575
576         /* free the callback */
577         cb = op->o_callback;
578         op->o_callback = cb->sc_next;
579         if (cb->sc_private)
580                 ber_bvfree_x(cb->sc_private, op->o_tmpmemctx);
581         op->o_tmpfree( cb, op->o_tmpmemctx );
582         return 0;
583 }
584
585 static int
586 totp_bind_response( Operation *op, SlapReply *rs )
587 {
588         Modifications *mod = NULL;
589         BackendInfo *bi = op->o_bd->bd_info;
590         Entry *e;
591         int rc;
592
593         /* we're only interested if the bind was successful */
594         if ( rs->sr_err != LDAP_SUCCESS )
595                 return SLAP_CB_CONTINUE;
596
597         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
598         op->o_bd->bd_info = bi;
599
600         if ( rc != LDAP_SUCCESS ) {
601                 return SLAP_CB_CONTINUE;
602         }
603
604         {
605                 time_t now;
606                 Attribute *a;
607                 Modifications *m;
608                 char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
609                 struct berval timestamp;
610
611                 /* get the current time */
612                 now = op->o_time;
613
614                 /* update the authTimestamp in the user's entry with the current time */
615                 timestamp.bv_val = nowstr;
616                 timestamp.bv_len = sizeof(nowstr);
617                 slap_timestamp( &now, &timestamp );
618
619                 m = ch_calloc( sizeof(Modifications), 1 );
620                 m->sml_op = LDAP_MOD_REPLACE;
621                 m->sml_flags = 0;
622                 m->sml_type = ad_authTimestamp->ad_cname;
623                 m->sml_desc = ad_authTimestamp;
624                 m->sml_numvals = 1;
625                 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
626                 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
627
628                 ber_dupbv( &m->sml_values[0], &timestamp );
629                 ber_dupbv( &m->sml_nvalues[0], &timestamp );
630                 m->sml_next = mod;
631                 mod = m;
632
633                 /* get authTimestamp attribute, if it exists */
634                 if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL && op->o_callback->sc_private) {
635                         struct berval *bv = op->o_callback->sc_private;
636                         m = ch_calloc( sizeof(Modifications), 1 );
637                         m->sml_op = LDAP_MOD_DELETE;
638                         m->sml_flags = 0;
639                         m->sml_type = ad_authTimestamp->ad_cname;
640                         m->sml_desc = ad_authTimestamp;
641                         m->sml_numvals = 1;
642                         m->sml_values = ch_calloc( sizeof(struct berval), 2 );
643                         m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
644
645                         ber_dupbv( &m->sml_values[0], bv );
646                         ber_dupbv( &m->sml_nvalues[0], bv );
647                         m->sml_next = mod;
648                         mod = m;
649                 }
650         }
651
652         be_entry_release_r( op, e );
653
654         /* perform the update */
655         if ( mod ) {
656                 Operation op2 = *op;
657                 SlapReply r2 = { REP_RESULT };
658                 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
659
660                 /* This is a DSA-specific opattr, it never gets replicated. */
661                 op2.o_tag = LDAP_REQ_MODIFY;
662                 op2.o_callback = &cb;
663                 op2.orm_modlist = mod;
664                 op2.o_dn = op->o_bd->be_rootdn;
665                 op2.o_ndn = op->o_bd->be_rootndn;
666                 op2.o_dont_replicate = 1;
667                 rc = op->o_bd->be_modify( &op2, &r2 );
668                 slap_mods_free( mod, 1 );
669                 if (rc != LDAP_SUCCESS) {
670                         /* slapd has logged this as a success already, but we
671                          * need to fail it because the authTimestamp changed
672                          * out from under us.
673                          */
674                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
675                         connection2anonymous(op->o_conn);
676                         op2 = *op;
677                         op2.o_callback = NULL;
678                         send_ldap_result(&op2, rs);
679                         op->o_bd->bd_info = bi;
680                         return rs->sr_err;
681                 }
682         }
683
684         op->o_bd->bd_info = bi;
685         return SLAP_CB_CONTINUE;
686 }
687
688 static int totp_op_bind(
689         Operation *op,
690         SlapReply *rs )
691 {
692         /* If this is a simple Bind, stash the Op pointer so our chk
693          * function can find it. Set a cleanup callback to clear it
694          * out when the Bind completes.
695          */
696         if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) {
697                 slap_callback *cb;
698                 ldap_pvt_thread_pool_setkey( op->o_threadctx,
699                         totp_op_cleanup, op, 0, NULL, NULL );
700                 cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
701                 cb->sc_response = totp_bind_response;
702                 cb->sc_cleanup = totp_op_cleanup;
703                 cb->sc_next = op->o_callback;
704                 op->o_callback = cb;
705         }
706         return SLAP_CB_CONTINUE;
707 }
708
709 static int totp_db_open(
710         BackendDB *be,
711         ConfigReply *cr
712 )
713 {
714         int rc = 0;
715
716         if (!ad_authTimestamp) {
717                 const char *text = NULL;
718                 rc = slap_str2ad("authTimestamp", &ad_authTimestamp, &text);
719                 if (rc) {
720                         rc = register_at(totp_OpSchema[0].def, totp_OpSchema[0].ad, 0 );
721                         if (rc) {
722                                 snprintf(cr->msg, sizeof(cr->msg), "unable to find or register authTimestamp attribute: %s (%d)",
723                                         text, rc);
724                                 Debug(LDAP_DEBUG_ANY, "totp: %s.\n", cr->msg, 0, 0);
725                         }
726                         ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE;
727                 }
728         }
729         return rc;
730 }
731
732 static slap_overinst totp;
733
734 int
735 totp_initialize(void)
736 {
737         int rc;
738
739         totp.on_bi.bi_type = "totp";
740
741         totp.on_bi.bi_db_open = totp_db_open;
742         totp.on_bi.bi_op_bind = totp_op_bind;
743
744         rc = lutil_passwd_add((struct berval *) &scheme_totp1, chk_totp1, hash_totp1);
745         if (!rc)
746                 rc = lutil_passwd_add((struct berval *) &scheme_totp256, chk_totp256, hash_totp256);
747         if (!rc)
748                 rc = lutil_passwd_add((struct berval *) &scheme_totp512, chk_totp512, hash_totp512);
749         if (rc)
750                 return rc;
751
752         return overlay_register(&totp);
753 }
754
755 int init_module(int argc, char *argv[]) {
756         return totp_initialize();
757 }