pgreco / rpms / ipa

Forked from forks/areguera/rpms/ipa 4 years ago
Clone

Blame SOURCES/0070-Preliminary-refactoring-of-libotp-files.patch

e3ffab
From d3c2efc7362f8eeb1cfb22b5d59bdec22226af65 Mon Sep 17 00:00:00 2001
e3ffab
From: Nathaniel McCallum <npmccallum@redhat.com>
e3ffab
Date: Mon, 10 Nov 2014 20:58:20 -0500
e3ffab
Subject: [PATCH] Preliminary refactoring of libotp files
e3ffab
e3ffab
There are no major changes in this commit other than changing filenames
e3ffab
and symbols to have consistent namespaces. This prepares for larger
e3ffab
changes to come in subsequent commits.
e3ffab
e3ffab
Reviewed-By: Thierry Bordaz <tbordaz@redhat.com>
e3ffab
---
e3ffab
 .../ipa-otp-lasttoken/Makefile.am                  |   1 -
e3ffab
 .../ipa-otp-lasttoken/ipa_otp_lasttoken.c          |  10 +-
e3ffab
 .../ipa-slapi-plugins/ipa-pwd-extop/Makefile.am    |   1 -
e3ffab
 daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h   |   2 +-
e3ffab
 daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c  |  12 +-
e3ffab
 daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c  |  11 +-
e3ffab
 daemons/ipa-slapi-plugins/libotp/Makefile.am       |  10 +-
e3ffab
 daemons/ipa-slapi-plugins/libotp/hotp.c            | 170 ++++++
e3ffab
 daemons/ipa-slapi-plugins/libotp/hotp.h            |  60 +++
e3ffab
 daemons/ipa-slapi-plugins/libotp/libotp.c          | 573 ---------------------
e3ffab
 daemons/ipa-slapi-plugins/libotp/libotp.h          |  93 ----
e3ffab
 daemons/ipa-slapi-plugins/libotp/librfc.c          | 170 ------
e3ffab
 daemons/ipa-slapi-plugins/libotp/librfc.h          |  63 ---
e3ffab
 daemons/ipa-slapi-plugins/libotp/otp_token.c       | 572 ++++++++++++++++++++
e3ffab
 daemons/ipa-slapi-plugins/libotp/otp_token.h       |  87 ++++
e3ffab
 daemons/ipa-slapi-plugins/libotp/t_hotp.c          | 121 +++++
e3ffab
 daemons/ipa-slapi-plugins/libotp/t_librfc.c        | 121 -----
e3ffab
 17 files changed, 1033 insertions(+), 1044 deletions(-)
e3ffab
 create mode 100644 daemons/ipa-slapi-plugins/libotp/hotp.c
e3ffab
 create mode 100644 daemons/ipa-slapi-plugins/libotp/hotp.h
e3ffab
 delete mode 100644 daemons/ipa-slapi-plugins/libotp/libotp.c
e3ffab
 delete mode 100644 daemons/ipa-slapi-plugins/libotp/libotp.h
e3ffab
 delete mode 100644 daemons/ipa-slapi-plugins/libotp/librfc.c
e3ffab
 delete mode 100644 daemons/ipa-slapi-plugins/libotp/librfc.h
e3ffab
 create mode 100644 daemons/ipa-slapi-plugins/libotp/otp_token.c
e3ffab
 create mode 100644 daemons/ipa-slapi-plugins/libotp/otp_token.h
e3ffab
 create mode 100644 daemons/ipa-slapi-plugins/libotp/t_hotp.c
e3ffab
 delete mode 100644 daemons/ipa-slapi-plugins/libotp/t_librfc.c
e3ffab
e3ffab
diff --git a/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/Makefile.am b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/Makefile.am
e3ffab
index d12fbcc891c5f210028b1c02ffbd83908e50acc6..cb6340960818eb4874a70f36603feda93652370f 100644
e3ffab
--- a/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/Makefile.am
e3ffab
+++ b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/Makefile.am
e3ffab
@@ -3,7 +3,6 @@ PLUGIN_COMMON_DIR = ../common
e3ffab
 AM_CPPFLAGS =							\
e3ffab
 	-I.							\
e3ffab
 	-I$(srcdir)						\
e3ffab
-	-I$(srcdir)/../libotp					\
e3ffab
 	-I$(PLUGIN_COMMON_DIR)					\
e3ffab
 	-I/usr/include/dirsrv					\
e3ffab
 	-DPREFIX=\""$(prefix)"\" 				\
e3ffab
diff --git a/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c
e3ffab
index 15b404dcdb6bd88c70ccc7c5fed7a5829483590b..19217ba7fbf562231dd74b25cfd13a0f4d930e7c 100644
e3ffab
--- a/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c
e3ffab
+++ b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c
e3ffab
@@ -41,7 +41,7 @@
e3ffab
 #  include <config.h>
e3ffab
 #endif
e3ffab
 
e3ffab
-#include <libotp.h>
e3ffab
+#include "../libotp/otp_token.h"
e3ffab
 #include <time.h>
e3ffab
 
e3ffab
 #include "util.h"
e3ffab
@@ -61,7 +61,7 @@ target_is_only_enabled_token(Slapi_PBlock *pb)
e3ffab
 {
e3ffab
     Slapi_DN *target_sdn = NULL;
e3ffab
     Slapi_DN *token_sdn = NULL;
e3ffab
-    struct otptoken **tokens;
e3ffab
+    struct otp_token **tokens;
e3ffab
     char *user_dn = NULL;
e3ffab
     bool match;
e3ffab
 
e3ffab
@@ -75,10 +75,10 @@ target_is_only_enabled_token(Slapi_PBlock *pb)
e3ffab
         return false;
e3ffab
 
e3ffab
     /* Get the SDN of the only enabled token. */
e3ffab
-    tokens = otptoken_find(plugin_id, user_dn, NULL, true, NULL);
e3ffab
+    tokens = otp_token_find(plugin_id, user_dn, NULL, true, NULL);
e3ffab
     if (tokens != NULL && tokens[0] != NULL && tokens[1] == NULL)
e3ffab
-        token_sdn = slapi_sdn_dup(otptoken_get_sdn(tokens[0]));
e3ffab
-    otptoken_free_array(tokens);
e3ffab
+        token_sdn = slapi_sdn_dup(otp_token_get_sdn(tokens[0]));
e3ffab
+    otp_token_free_array(tokens);
e3ffab
     if (token_sdn == NULL)
e3ffab
         return false;
e3ffab
 
e3ffab
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am b/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
e3ffab
index 77beca2da0810ed5507d95b21f99d22f63b05fc1..eeb352611e5b67a2f6803b59414fb31c37f39f33 100644
e3ffab
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
e3ffab
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
e3ffab
@@ -11,7 +11,6 @@ ASN1_UTIL_DIR=../../../asn1
e3ffab
 AM_CPPFLAGS =							\
e3ffab
 	-I.							\
e3ffab
 	-I$(srcdir)						\
e3ffab
-	-I$(srcdir)/../libotp					\
e3ffab
 	-I$(PLUGIN_COMMON_DIR)					\
e3ffab
 	-I$(KRB5_UTIL_DIR)					\
e3ffab
 	-I$(ASN1_UTIL_DIR)					\
e3ffab
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
e3ffab
index f8851122b920bb8317d5d6b74915504a98a674cf..2e9d4fe86b34c64731728882db7c81e3871d7d07 100644
e3ffab
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
e3ffab
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
e3ffab
@@ -41,7 +41,7 @@
e3ffab
 #  include <config.h>
e3ffab
 #endif
e3ffab
 
e3ffab
-#include <libotp.h>
e3ffab
+#include "../libotp/otp_token.h"
e3ffab
 
e3ffab
 #include <stdio.h>
e3ffab
 #include <string.h>
e3ffab
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
e3ffab
index 1f595d01d986ca2950672d796d62f5f78b05c212..1dff6db1a8cfcc295ba43d1c29d8887e3cf37fec 100644
e3ffab
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
e3ffab
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
e3ffab
@@ -1140,11 +1140,11 @@ done:
e3ffab
 static bool ipapwd_do_otp_auth(const char *dn, Slapi_Entry *bind_entry,
e3ffab
                                struct berval *creds)
e3ffab
 {
e3ffab
-    struct otptoken **tokens = NULL;
e3ffab
+    struct otp_token **tokens = NULL;
e3ffab
     bool success = false;
e3ffab
 
e3ffab
     /* Find all of the user's active tokens. */
e3ffab
-    tokens = otptoken_find(ipapwd_plugin_id, dn, NULL, true, NULL);
e3ffab
+    tokens = otp_token_find(ipapwd_plugin_id, dn, NULL, true, NULL);
e3ffab
     if (tokens == NULL) {
e3ffab
         slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME,
e3ffab
                         "%s: can't find tokens for '%s'.\n", __func__, dn);
e3ffab
@@ -1157,12 +1157,12 @@ static bool ipapwd_do_otp_auth(const char *dn, Slapi_Entry *bind_entry,
e3ffab
     /* Loop through each token. */
e3ffab
     for (int i = 0; tokens[i] && !success; i++) {
e3ffab
         /* Attempt authentication. */
e3ffab
-        success = otptoken_validate_berval(tokens[i], OTP_VALIDATE_STEPS,
e3ffab
+        success = otp_token_validate_berval(tokens[i], OTP_VALIDATE_STEPS,
e3ffab
                                            creds, true);
e3ffab
 
e3ffab
         /* Truncate the password to remove the OTP code at the end. */
e3ffab
         if (success) {
e3ffab
-            creds->bv_len -= otptoken_get_digits(tokens[i]);
e3ffab
+            creds->bv_len -= otp_token_get_digits(tokens[i]);
e3ffab
             creds->bv_val[creds->bv_len] = '\0';
e3ffab
         }
e3ffab
 
e3ffab
@@ -1170,10 +1170,10 @@ static bool ipapwd_do_otp_auth(const char *dn, Slapi_Entry *bind_entry,
e3ffab
                         "%s: token authentication %s "
e3ffab
                         "(user: '%s', token: '%s\').\n", __func__,
e3ffab
                         success ? "succeeded" : "failed", dn,
e3ffab
-                        slapi_sdn_get_ndn(otptoken_get_sdn(tokens[i])));
e3ffab
+                        slapi_sdn_get_ndn(otp_token_get_sdn(tokens[i])));
e3ffab
     }
e3ffab
 
e3ffab
-    otptoken_free_array(tokens);
e3ffab
+    otp_token_free_array(tokens);
e3ffab
     return success;
e3ffab
 }
e3ffab
 
e3ffab
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c
e3ffab
index cbb4536e7d119f4550e4b523eb02e34d058ae7a1..10c49b724ee276d2b1fb89891a6eb4ee8eaa8fab 100644
e3ffab
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c
e3ffab
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c
e3ffab
@@ -37,8 +37,7 @@
e3ffab
  * All rights reserved.
e3ffab
  * END COPYRIGHT BLOCK **/
e3ffab
 
e3ffab
-
e3ffab
-#include <libotp.h>
e3ffab
+#include "../libotp/otp_token.h"
e3ffab
 #include "syncreq.h"
e3ffab
 
e3ffab
 #define OTP_SYNC_MAX_STEPS 25
e3ffab
@@ -56,7 +55,7 @@ bool sync_request_present(Slapi_PBlock *pb)
e3ffab
 bool sync_request_handle(Slapi_ComponentId *plugin_id, Slapi_PBlock *pb,
e3ffab
                          const char *user_dn)
e3ffab
 {
e3ffab
-    struct otptoken **tokens = NULL;
e3ffab
+    struct otp_token **tokens = NULL;
e3ffab
     LDAPControl **controls = NULL;
e3ffab
     struct berval *second = NULL;
e3ffab
     struct berval *first = NULL;
e3ffab
@@ -91,10 +90,10 @@ bool sync_request_handle(Slapi_ComponentId *plugin_id, Slapi_PBlock *pb,
e3ffab
         /* Process the synchronization. */
e3ffab
         success = false;
e3ffab
         if (ber_scanf(ber, "}") != LBER_ERROR) {
e3ffab
-            tokens = otptoken_find(plugin_id, user_dn, token_dn, true, NULL);
e3ffab
+            tokens = otp_token_find(plugin_id, user_dn, token_dn, true, NULL);
e3ffab
             if (tokens != NULL) {
e3ffab
-                success = otptoken_sync_berval(tokens, OTP_SYNC_MAX_STEPS, first, second);
e3ffab
-                otptoken_free_array(tokens);
e3ffab
+                success = otp_token_sync_berval(tokens, OTP_SYNC_MAX_STEPS, first, second);
e3ffab
+                otp_token_free_array(tokens);
e3ffab
             }
e3ffab
         }
e3ffab
 
e3ffab
diff --git a/daemons/ipa-slapi-plugins/libotp/Makefile.am b/daemons/ipa-slapi-plugins/libotp/Makefile.am
e3ffab
index 6aa60c56a2293916ba7c1d773968055c3f94a4fe..012c8339199af5f63e6434b94109af2d93a38b45 100644
e3ffab
--- a/daemons/ipa-slapi-plugins/libotp/Makefile.am
e3ffab
+++ b/daemons/ipa-slapi-plugins/libotp/Makefile.am
e3ffab
@@ -1,9 +1,11 @@
e3ffab
 MAINTAINERCLEANFILES = *~ Makefile.in
e3ffab
 AM_CPPFLAGS = -I/usr/include/dirsrv
e3ffab
 
e3ffab
-noinst_LTLIBRARIES = librfc.la libotp.la
e3ffab
-libotp_la_LIBADD = librfc.la
e3ffab
+noinst_LTLIBRARIES = libhotp.la libotp.la
e3ffab
+libhotp_la_SOURCES = hotp.c hotp.h
e3ffab
+libotp_la_SOURCES = otp_token.c otp_token.h
e3ffab
+libotp_la_LIBADD = libhotp.la
e3ffab
 
e3ffab
-check_PROGRAMS = t_librfc
e3ffab
+check_PROGRAMS = t_hotp
e3ffab
 TESTS = $(check_PROGRAMS)
e3ffab
-t_librfc_LDADD = $(NSPR_LIBS) $(NSS_LIBS) librfc.la
e3ffab
+t_hotp_LDADD = $(NSPR_LIBS) $(NSS_LIBS) libhotp.la
e3ffab
diff --git a/daemons/ipa-slapi-plugins/libotp/hotp.c b/daemons/ipa-slapi-plugins/libotp/hotp.c
e3ffab
new file mode 100644
e3ffab
index 0000000000000000000000000000000000000000..619bc63ab1bee99d71c2f0fb887809762107c94c
e3ffab
--- /dev/null
e3ffab
+++ b/daemons/ipa-slapi-plugins/libotp/hotp.c
e3ffab
@@ -0,0 +1,170 @@
e3ffab
+/** BEGIN COPYRIGHT BLOCK
e3ffab
+ * This program is free software; you can redistribute it and/or modify
e3ffab
+ * it under the terms of the GNU General Public License as published by
e3ffab
+ * the Free Software Foundation, either version 3 of the License, or
e3ffab
+ * (at your option) any later version.
e3ffab
+ *
e3ffab
+ * This program is distributed in the hope that it will be useful,
e3ffab
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
e3ffab
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
e3ffab
+ * GNU General Public License for more details.
e3ffab
+ *
e3ffab
+ * You should have received a copy of the GNU General Public License
e3ffab
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
e3ffab
+ *
e3ffab
+ * Additional permission under GPLv3 section 7:
e3ffab
+ *
e3ffab
+ * In the following paragraph, "GPL" means the GNU General Public
e3ffab
+ * License, version 3 or any later version, and "Non-GPL Code" means
e3ffab
+ * code that is governed neither by the GPL nor a license
e3ffab
+ * compatible with the GPL.
e3ffab
+ *
e3ffab
+ * You may link the code of this Program with Non-GPL Code and convey
e3ffab
+ * linked combinations including the two, provided that such Non-GPL
e3ffab
+ * Code only links to the code of this Program through those well
e3ffab
+ * defined interfaces identified in the file named EXCEPTION found in
e3ffab
+ * the source code files (the "Approved Interfaces"). The files of
e3ffab
+ * Non-GPL Code may instantiate templates or use macros or inline
e3ffab
+ * functions from the Approved Interfaces without causing the resulting
e3ffab
+ * work to be covered by the GPL. Only the copyright holders of this
e3ffab
+ * Program may make changes or additions to the list of Approved
e3ffab
+ * Interfaces.
e3ffab
+ *
e3ffab
+ * Authors:
e3ffab
+ * Nathaniel McCallum <npmccallum@redhat.com>
e3ffab
+ *
e3ffab
+ * Copyright (C) 2013 Red Hat, Inc.
e3ffab
+ * All rights reserved.
e3ffab
+ * END COPYRIGHT BLOCK **/
e3ffab
+
e3ffab
+/*
e3ffab
+ * This file contains an implementation of HOTP (RFC 4226) and TOTP (RFC 6238).
e3ffab
+ * For details of how these algorithms work, please see the relevant RFCs.
e3ffab
+ */
e3ffab
+
e3ffab
+#include "hotp.h"
e3ffab
+#include <time.h>
e3ffab
+
e3ffab
+#include <nss.h>
e3ffab
+#include <pk11pub.h>
e3ffab
+#include <hasht.h>
e3ffab
+#include <prnetdb.h>
e3ffab
+
e3ffab
+struct digest_buffer {
e3ffab
+    uint8_t buf[SHA512_LENGTH];
e3ffab
+    unsigned int len;
e3ffab
+};
e3ffab
+
e3ffab
+static const struct {
e3ffab
+    const char *algo;
e3ffab
+    CK_MECHANISM_TYPE mech;
e3ffab
+} algo2mech[] = {
e3ffab
+    { "sha1",   CKM_SHA_1_HMAC },
e3ffab
+    { "sha256", CKM_SHA256_HMAC },
e3ffab
+    { "sha384", CKM_SHA384_HMAC },
e3ffab
+    { "sha512", CKM_SHA512_HMAC },
e3ffab
+    { }
e3ffab
+};
e3ffab
+
e3ffab
+/*
e3ffab
+ * This code is mostly cargo-cult taken from here:
e3ffab
+ *   http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn5.html
e3ffab
+ *
e3ffab
+ * It should implement HMAC with the given mechanism (SHA: 1, 256, 384, 512).
e3ffab
+ */
e3ffab
+static bool hmac(SECItem *key, CK_MECHANISM_TYPE mech, const SECItem *in,
e3ffab
+                 struct digest_buffer *out)
e3ffab
+{
e3ffab
+    SECItem param = { siBuffer, NULL, 0 };
e3ffab
+    PK11SlotInfo *slot = NULL;
e3ffab
+    PK11SymKey *symkey = NULL;
e3ffab
+    PK11Context *ctx = NULL;
e3ffab
+    bool ret = false;
e3ffab
+    SECStatus s;
e3ffab
+
e3ffab
+    slot = PK11_GetBestSlot(mech, NULL);
e3ffab
+    if (slot == NULL) {
e3ffab
+        slot = PK11_GetInternalKeySlot();
e3ffab
+        if (slot == NULL) {
e3ffab
+            goto done;
e3ffab
+        }
e3ffab
+    }
e3ffab
+
e3ffab
+    symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap,
e3ffab
+                               CKA_SIGN, key, NULL);
e3ffab
+    if (symkey == NULL)
e3ffab
+        goto done;
e3ffab
+
e3ffab
+    ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN, symkey, ¶m;;
e3ffab
+    if (ctx == NULL)
e3ffab
+        goto done;
e3ffab
+
e3ffab
+    s = PK11_DigestBegin(ctx);
e3ffab
+    if (s != SECSuccess)
e3ffab
+        goto done;
e3ffab
+
e3ffab
+    s = PK11_DigestOp(ctx, in->data, in->len);
e3ffab
+    if (s != SECSuccess)
e3ffab
+        goto done;
e3ffab
+
e3ffab
+    s = PK11_DigestFinal(ctx, out->buf, &out->len, sizeof(out->buf));
e3ffab
+    if (s != SECSuccess)
e3ffab
+        goto done;
e3ffab
+
e3ffab
+    ret = true;
e3ffab
+
e3ffab
+done:
e3ffab
+    if (ctx != NULL)
e3ffab
+        PK11_DestroyContext(ctx, PR_TRUE);
e3ffab
+    if (symkey != NULL)
e3ffab
+        PK11_FreeSymKey(symkey);
e3ffab
+    if (slot != NULL)
e3ffab
+        PK11_FreeSlot(slot);
e3ffab
+    return ret;
e3ffab
+}
e3ffab
+
e3ffab
+/*
e3ffab
+ * An implementation of HOTP (RFC 4226).
e3ffab
+ */
e3ffab
+bool hotp(const struct hotp_token *token, uint64_t counter, uint32_t *out)
e3ffab
+{
e3ffab
+    const SECItem cntr = { siBuffer, (uint8_t *) &counter, sizeof(counter) };
e3ffab
+    SECItem keyitm = { siBuffer, token->key.bytes, token->key.len };
e3ffab
+    CK_MECHANISM_TYPE mech = CKM_SHA_1_HMAC;
e3ffab
+    PRUint64 offset, binary, div;
e3ffab
+    struct digest_buffer digest;
e3ffab
+    int digits = token->digits;
e3ffab
+    int i;
e3ffab
+
e3ffab
+    /* Convert counter to network byte order. */
e3ffab
+    counter = PR_htonll(counter);
e3ffab
+
e3ffab
+    /* Find the mech. */
e3ffab
+    for (i = 0; algo2mech[i].algo; i++) {
e3ffab
+        if (strcasecmp(algo2mech[i].algo, token->algo) == 0) {
e3ffab
+            mech = algo2mech[i].mech;
e3ffab
+            break;
e3ffab
+        }
e3ffab
+    }
e3ffab
+
e3ffab
+    /* Create the digits divisor. */
e3ffab
+    for (div = 1; digits > 0; digits--) {
e3ffab
+        div *= 10;
e3ffab
+    }
e3ffab
+
e3ffab
+    /* Do the digest. */
e3ffab
+    if (!hmac(&keyitm, mech, &cntr, &digest)) {
e3ffab
+        return false;
e3ffab
+    }
e3ffab
+
e3ffab
+    /* Truncate. */
e3ffab
+    offset  = digest.buf[digest.len - 1] & 0xf;
e3ffab
+    binary  = (digest.buf[offset + 0] & 0x7f) << 0x18;
e3ffab
+    binary |= (digest.buf[offset + 1] & 0xff) << 0x10;
e3ffab
+    binary |= (digest.buf[offset + 2] & 0xff) << 0x08;
e3ffab
+    binary |= (digest.buf[offset + 3] & 0xff) << 0x00;
e3ffab
+    binary  = binary % div;
e3ffab
+
e3ffab
+    *out = binary;
e3ffab
+    return true;
e3ffab
+}
e3ffab
diff --git a/daemons/ipa-slapi-plugins/libotp/hotp.h b/daemons/ipa-slapi-plugins/libotp/hotp.h
e3ffab
new file mode 100644
e3ffab
index 0000000000000000000000000000000000000000..06ae1fdae86df4ab124839cd62b61d99d908789a
e3ffab
--- /dev/null
e3ffab
+++ b/daemons/ipa-slapi-plugins/libotp/hotp.h
e3ffab
@@ -0,0 +1,60 @@
e3ffab
+/** BEGIN COPYRIGHT BLOCK
e3ffab
+ * This program is free software; you can redistribute it and/or modify
e3ffab
+ * it under the terms of the GNU General Public License as published by
e3ffab
+ * the Free Software Foundation, either version 3 of the License, or
e3ffab
+ * (at your option) any later version.
e3ffab
+ *
e3ffab
+ * This program is distributed in the hope that it will be useful,
e3ffab
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
e3ffab
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
e3ffab
+ * GNU General Public License for more details.
e3ffab
+ *
e3ffab
+ * You should have received a copy of the GNU General Public License
e3ffab
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
e3ffab
+ *
e3ffab
+ * Additional permission under GPLv3 section 7:
e3ffab
+ *
e3ffab
+ * In the following paragraph, "GPL" means the GNU General Public
e3ffab
+ * License, version 3 or any later version, and "Non-GPL Code" means
e3ffab
+ * code that is governed neither by the GPL nor a license
e3ffab
+ * compatible with the GPL.
e3ffab
+ *
e3ffab
+ * You may link the code of this Program with Non-GPL Code and convey
e3ffab
+ * linked combinations including the two, provided that such Non-GPL
e3ffab
+ * Code only links to the code of this Program through those well
e3ffab
+ * defined interfaces identified in the file named EXCEPTION found in
e3ffab
+ * the source code files (the "Approved Interfaces"). The files of
e3ffab
+ * Non-GPL Code may instantiate templates or use macros or inline
e3ffab
+ * functions from the Approved Interfaces without causing the resulting
e3ffab
+ * work to be covered by the GPL. Only the copyright holders of this
e3ffab
+ * Program may make changes or additions to the list of Approved
e3ffab
+ * Interfaces.
e3ffab
+ *
e3ffab
+ * Authors:
e3ffab
+ * Nathaniel McCallum <npmccallum@redhat.com>
e3ffab
+ *
e3ffab
+ * Copyright (C) 2013 Red Hat, Inc.
e3ffab
+ * All rights reserved.
e3ffab
+ * END COPYRIGHT BLOCK **/
e3ffab
+
e3ffab
+#pragma once
e3ffab
+
e3ffab
+#include <stdbool.h>
e3ffab
+#include <stdint.h>
e3ffab
+#include <stdlib.h>
e3ffab
+
e3ffab
+struct hotp_token_key {
e3ffab
+    uint8_t *bytes;
e3ffab
+    size_t len;
e3ffab
+};
e3ffab
+
e3ffab
+struct hotp_token {
e3ffab
+    struct hotp_token_key key;
e3ffab
+    char *algo;
e3ffab
+    int digits;
e3ffab
+};
e3ffab
+
e3ffab
+/*
e3ffab
+ * An implementation of HOTP (RFC 4226).
e3ffab
+ */
e3ffab
+bool hotp(const struct hotp_token *token, uint64_t counter, uint32_t *out);
e3ffab
diff --git a/daemons/ipa-slapi-plugins/libotp/libotp.c b/daemons/ipa-slapi-plugins/libotp/libotp.c
e3ffab
deleted file mode 100644
e3ffab
index c65aef04348da5abdb1eeb6267e785f0342c7d51..0000000000000000000000000000000000000000
e3ffab
--- a/daemons/ipa-slapi-plugins/libotp/libotp.c
e3ffab
+++ /dev/null
e3ffab
@@ -1,573 +0,0 @@
e3ffab
-/** BEGIN COPYRIGHT BLOCK
e3ffab
- * This program is free software; you can redistribute it and/or modify
e3ffab
- * it under the terms of the GNU General Public License as published by
e3ffab
- * the Free Software Foundation, either version 3 of the License, or
e3ffab
- * (at your option) any later version.
e3ffab
- *
e3ffab
- * This program is distributed in the hope that it will be useful,
e3ffab
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
e3ffab
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
e3ffab
- * GNU General Public License for more details.
e3ffab
- *
e3ffab
- * You should have received a copy of the GNU General Public License
e3ffab
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
e3ffab
- *
e3ffab
- * Additional permission under GPLv3 section 7:
e3ffab
- *
e3ffab
- * In the following paragraph, "GPL" means the GNU General Public
e3ffab
- * License, version 3 or any later version, and "Non-GPL Code" means
e3ffab
- * code that is governed neither by the GPL nor a license
e3ffab
- * compatible with the GPL.
e3ffab
- *
e3ffab
- * You may link the code of this Program with Non-GPL Code and convey
e3ffab
- * linked combinations including the two, provided that such Non-GPL
e3ffab
- * Code only links to the code of this Program through those well
e3ffab
- * defined interfaces identified in the file named EXCEPTION found in
e3ffab
- * the source code files (the "Approved Interfaces"). The files of
e3ffab
- * Non-GPL Code may instantiate templates or use macros or inline
e3ffab
- * functions from the Approved Interfaces without causing the resulting
e3ffab
- * work to be covered by the GPL. Only the copyright holders of this
e3ffab
- * Program may make changes or additions to the list of Approved
e3ffab
- * Interfaces.
e3ffab
- *
e3ffab
- * Authors:
e3ffab
- * Nathaniel McCallum <npmccallum@redhat.com>
e3ffab
- *
e3ffab
- * Copyright (C) 2013 Red Hat, Inc.
e3ffab
- * All rights reserved.
e3ffab
- * END COPYRIGHT BLOCK **/
e3ffab
-
e3ffab
-#include "libotp.h"
e3ffab
-#include "librfc.h"
e3ffab
-
e3ffab
-#include <time.h>
e3ffab
-#include <errno.h>
e3ffab
-
e3ffab
-#define TOKEN(s) "ipaToken" s
e3ffab
-#define O(s) TOKEN("OTP" s)
e3ffab
-#define T(s) TOKEN("TOTP" s)
e3ffab
-#define H(s) TOKEN("HOTP" s)
e3ffab
-
e3ffab
-#define IPA_OTP_DEFAULT_TOKEN_STEP 30
e3ffab
-#define IPA_OTP_OBJCLS_FILTER \
e3ffab
-    "(|(objectClass=ipaTokenTOTP)(objectClass=ipaTokenHOTP))"
e3ffab
-
e3ffab
-
e3ffab
-enum otptoken_type {
e3ffab
-    OTPTOKEN_NONE = 0,
e3ffab
-    OTPTOKEN_TOTP,
e3ffab
-    OTPTOKEN_HOTP,
e3ffab
-};
e3ffab
-
e3ffab
-struct otptoken {
e3ffab
-    Slapi_ComponentId *plugin_id;
e3ffab
-    Slapi_DN *sdn;
e3ffab
-    struct hotp_token token;
e3ffab
-    enum otptoken_type type;
e3ffab
-    union {
e3ffab
-        struct {
e3ffab
-            uint64_t watermark;
e3ffab
-            unsigned int step;
e3ffab
-            int offset;
e3ffab
-        } totp;
e3ffab
-        struct {
e3ffab
-            uint64_t counter;
e3ffab
-        } hotp;
e3ffab
-    };
e3ffab
-};
e3ffab
-
e3ffab
-static const char *get_basedn(Slapi_DN *dn)
e3ffab
-{
e3ffab
-    Slapi_DN *suffix = NULL;
e3ffab
-    void *node = NULL;
e3ffab
-
e3ffab
-    for (suffix = slapi_get_first_suffix(&node, 0);
e3ffab
-         suffix != NULL;
e3ffab
-         suffix = slapi_get_next_suffix(&node, 0)) {
e3ffab
-        if (slapi_sdn_issuffix(dn, suffix))
e3ffab
-            return (char *) slapi_sdn_get_dn(suffix);
e3ffab
-    }
e3ffab
-
e3ffab
-    return NULL;
e3ffab
-}
e3ffab
-
e3ffab
-static inline bool is_algo_valid(const char *algo)
e3ffab
-{
e3ffab
-    static const char *valid_algos[] = { "sha1", "sha256", "sha384",
e3ffab
-                                         "sha512", NULL };
e3ffab
-    int i, ret;
e3ffab
-
e3ffab
-    for (i = 0; valid_algos[i]; i++) {
e3ffab
-        ret = strcasecmp(algo, valid_algos[i]);
e3ffab
-        if (ret == 0)
e3ffab
-            return true;
e3ffab
-    }
e3ffab
-
e3ffab
-    return false;
e3ffab
-}
e3ffab
-
e3ffab
-static const struct berval *entry_attr_get_berval(const Slapi_Entry* e,
e3ffab
-                                                  const char *type)
e3ffab
-{
e3ffab
-    Slapi_Attr* attr = NULL;
e3ffab
-    Slapi_Value *v;
e3ffab
-    int ret;
e3ffab
-
e3ffab
-    ret = slapi_entry_attr_find(e, type, &attr);
e3ffab
-    if (ret != 0 || attr == NULL)
e3ffab
-        return NULL;
e3ffab
-
e3ffab
-    ret = slapi_attr_first_value(attr, &v);
e3ffab
-    if (ret < 0)
e3ffab
-        return NULL;
e3ffab
-
e3ffab
-    return slapi_value_get_berval(v);
e3ffab
-}
e3ffab
-
e3ffab
-static bool writeattr(const struct otptoken *token, const char *attr,
e3ffab
-                      long long val)
e3ffab
-{
e3ffab
-    Slapi_PBlock *pb = NULL;
e3ffab
-    bool success = false;
e3ffab
-    char value[32];
e3ffab
-    int ret;
e3ffab
-
e3ffab
-    LDAPMod *mods[] = {
e3ffab
-        &(LDAPMod) {
e3ffab
-            LDAP_MOD_REPLACE, (char *) attr,
e3ffab
-            .mod_values = (char *[]) { value, NULL }
e3ffab
-        },
e3ffab
-        NULL
e3ffab
-    };
e3ffab
-
e3ffab
-    snprintf(value, sizeof(value), "%lld", val);
e3ffab
-
e3ffab
-    pb = slapi_pblock_new();
e3ffab
-    slapi_modify_internal_set_pb(pb, slapi_sdn_get_dn(token->sdn),
e3ffab
-                                 mods, NULL, NULL, token->plugin_id, 0);
e3ffab
-    if (slapi_modify_internal_pb(pb) != 0)
e3ffab
-        goto error;
e3ffab
-    if (slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret) != 0)
e3ffab
-        goto error;
e3ffab
-    if (ret != LDAP_SUCCESS)
e3ffab
-        goto error;
e3ffab
-
e3ffab
-    success = true;
e3ffab
-
e3ffab
-error:
e3ffab
-    slapi_pblock_destroy(pb);
e3ffab
-    return success;
e3ffab
-}
e3ffab
-
e3ffab
-/**
e3ffab
- * Validate a token.
e3ffab
- *
e3ffab
- * If the second token code is specified, perform synchronization.
e3ffab
- */
e3ffab
-static bool validate(struct otptoken *token, time_t now, ssize_t step,
e3ffab
-                     uint32_t first, const uint32_t *second)
e3ffab
-{
e3ffab
-    const char *attr;
e3ffab
-    uint32_t tmp;
e3ffab
-
e3ffab
-    /* Calculate the absolute step. */
e3ffab
-    switch (token->type) {
e3ffab
-    case OTPTOKEN_TOTP:
e3ffab
-        attr = T("watermark");
e3ffab
-        step = (now + token->totp.offset) / token->totp.step + step;
e3ffab
-        if (token->totp.watermark > 0 && step < token->totp.watermark)
e3ffab
-            return false;
e3ffab
-        break;
e3ffab
-    case OTPTOKEN_HOTP:
e3ffab
-        if (step < 0) /* NEVER go backwards! */
e3ffab
-            return false;
e3ffab
-        attr = H("counter");
e3ffab
-        step = token->hotp.counter + step;
e3ffab
-        break;
e3ffab
-    default:
e3ffab
-        return false;
e3ffab
-    }
e3ffab
-
e3ffab
-    /* Validate the first code. */
e3ffab
-    if (!hotp(&token->token, step++, &tmp))
e3ffab
-        return false;
e3ffab
-
e3ffab
-    if (first != tmp)
e3ffab
-        return false;
e3ffab
-
e3ffab
-    /* Validate the second code if specified. */
e3ffab
-    if (second != NULL) {
e3ffab
-        if (!hotp(&token->token, step++, &tmp))
e3ffab
-            return false;
e3ffab
-
e3ffab
-        if (*second != tmp)
e3ffab
-            return false;
e3ffab
-    }
e3ffab
-
e3ffab
-    /* Write the step value. */
e3ffab
-    if (!writeattr(token, attr, step))
e3ffab
-        return false;
e3ffab
-
e3ffab
-    /* Save our modifications to the object. */
e3ffab
-    switch (token->type) {
e3ffab
-    case OTPTOKEN_TOTP:
e3ffab
-        /* Perform optional synchronization steps. */
e3ffab
-        if (second != NULL) {
e3ffab
-            tmp = (step - now / token->totp.step) * token->totp.step;
e3ffab
-            if (!writeattr(token, T("clockOffset"), tmp))
e3ffab
-                return false;
e3ffab
-            token->totp.offset = tmp;
e3ffab
-        }
e3ffab
-        token->totp.watermark = step;
e3ffab
-        break;
e3ffab
-    case OTPTOKEN_HOTP:
e3ffab
-        token->hotp.counter = step;
e3ffab
-        break;
e3ffab
-    default:
e3ffab
-        break;
e3ffab
-    }
e3ffab
-
e3ffab
-    return true;
e3ffab
-}
e3ffab
-
e3ffab
-
e3ffab
-static void otptoken_free(struct otptoken *token)
e3ffab
-{
e3ffab
-    if (token == NULL)
e3ffab
-        return;
e3ffab
-
e3ffab
-    slapi_sdn_free(&token->sdn);
e3ffab
-    free(token->token.key.bytes);
e3ffab
-    slapi_ch_free_string(&token->token.algo);
e3ffab
-    free(token);
e3ffab
-}
e3ffab
-
e3ffab
-void otptoken_free_array(struct otptoken **tokens)
e3ffab
-{
e3ffab
-    if (tokens == NULL)
e3ffab
-        return;
e3ffab
-
e3ffab
-    for (size_t i = 0; tokens[i] != NULL; i++)
e3ffab
-        otptoken_free(tokens[i]);
e3ffab
-
e3ffab
-    free(tokens);
e3ffab
-}
e3ffab
-
e3ffab
-static struct otptoken *otptoken_new(Slapi_ComponentId *id, Slapi_Entry *entry)
e3ffab
-{
e3ffab
-    const struct berval *tmp;
e3ffab
-    struct otptoken *token;
e3ffab
-    char **vals;
e3ffab
-
e3ffab
-    token = calloc(1, sizeof(struct otptoken));
e3ffab
-    if (token == NULL)
e3ffab
-        return NULL;
e3ffab
-    token->plugin_id = id;
e3ffab
-
e3ffab
-    /* Get the token type. */
e3ffab
-    vals = slapi_entry_attr_get_charray(entry, "objectClass");
e3ffab
-    if (vals == NULL)
e3ffab
-        goto error;
e3ffab
-    token->type = OTPTOKEN_NONE;
e3ffab
-    for (int i = 0; vals[i] != NULL; i++) {
e3ffab
-        if (strcasecmp(vals[i], "ipaTokenTOTP") == 0)
e3ffab
-            token->type = OTPTOKEN_TOTP;
e3ffab
-        else if (strcasecmp(vals[i], "ipaTokenHOTP") == 0)
e3ffab
-            token->type = OTPTOKEN_HOTP;
e3ffab
-    }
e3ffab
-    slapi_ch_array_free(vals);
e3ffab
-    if (token->type == OTPTOKEN_NONE)
e3ffab
-        goto error;
e3ffab
-
e3ffab
-    /* Get SDN. */
e3ffab
-    token->sdn = slapi_sdn_dup(slapi_entry_get_sdn(entry));
e3ffab
-    if (token->sdn == NULL)
e3ffab
-        goto error;
e3ffab
-
e3ffab
-    /* Get key. */
e3ffab
-    tmp = entry_attr_get_berval(entry, O("key"));
e3ffab
-    if (tmp == NULL)
e3ffab
-        goto error;
e3ffab
-    token->token.key.len = tmp->bv_len;
e3ffab
-    token->token.key.bytes = malloc(token->token.key.len);
e3ffab
-    if (token->token.key.bytes == NULL)
e3ffab
-        goto error;
e3ffab
-    memcpy(token->token.key.bytes, tmp->bv_val, token->token.key.len);
e3ffab
-
e3ffab
-    /* Get length. */
e3ffab
-    token->token.digits = slapi_entry_attr_get_int(entry, O("digits"));
e3ffab
-    if (token->token.digits != 6 && token->token.digits != 8)
e3ffab
-        goto error;
e3ffab
-
e3ffab
-    /* Get algorithm. */
e3ffab
-    token->token.algo = slapi_entry_attr_get_charptr(entry, O("algorithm"));
e3ffab
-    if (token->token.algo == NULL)
e3ffab
-        token->token.algo = slapi_ch_strdup("sha1");
e3ffab
-    if (!is_algo_valid(token->token.algo))
e3ffab
-        goto error;
e3ffab
-
e3ffab
-    switch (token->type) {
e3ffab
-    case OTPTOKEN_TOTP:
e3ffab
-        /* Get offset. */
e3ffab
-        token->totp.offset = slapi_entry_attr_get_int(entry, T("clockOffset"));
e3ffab
-
e3ffab
-        /* Get watermark. */
e3ffab
-        token->totp.watermark = slapi_entry_attr_get_int(entry, T("watermark"));
e3ffab
-
e3ffab
-        /* Get step. */
e3ffab
-        token->totp.step = slapi_entry_attr_get_uint(entry, T("timeStep"));
e3ffab
-        if (token->totp.step == 0)
e3ffab
-            token->totp.step = IPA_OTP_DEFAULT_TOKEN_STEP;
e3ffab
-        break;
e3ffab
-    case OTPTOKEN_HOTP:
e3ffab
-        /* Get counter. */
e3ffab
-        token->hotp.counter = slapi_entry_attr_get_int(entry, H("counter"));
e3ffab
-        break;
e3ffab
-    default:
e3ffab
-        break;
e3ffab
-    }
e3ffab
-
e3ffab
-    return token;
e3ffab
-
e3ffab
-error:
e3ffab
-    otptoken_free(token);
e3ffab
-    return NULL;
e3ffab
-}
e3ffab
-
e3ffab
-static struct otptoken **find(Slapi_ComponentId *id, const char *user_dn,
e3ffab
-                              const char *token_dn, const char *intfilter,
e3ffab
-                              const char *extfilter)
e3ffab
-{
e3ffab
-    struct otptoken **tokens = NULL;
e3ffab
-    Slapi_Entry **entries = NULL;
e3ffab
-    Slapi_PBlock *pb = NULL;
e3ffab
-    Slapi_DN *sdn = NULL;
e3ffab
-    char *filter = NULL;
e3ffab
-    const char *basedn = NULL;
e3ffab
-    size_t count = 0;
e3ffab
-    int result = -1;
e3ffab
-
e3ffab
-    if (intfilter == NULL)
e3ffab
-        intfilter = "";
e3ffab
-
e3ffab
-    if (extfilter == NULL)
e3ffab
-        extfilter = "";
e3ffab
-
e3ffab
-    /* Create the filter. */
e3ffab
-    if (user_dn == NULL) {
e3ffab
-        filter = "(&" IPA_OTP_OBJCLS_FILTER "%s%s)";
e3ffab
-        filter = slapi_filter_sprintf(filter, intfilter, extfilter);
e3ffab
-    } else {
e3ffab
-        filter = "(&" IPA_OTP_OBJCLS_FILTER "(ipatokenOwner=%s%s)%s%s)";
e3ffab
-        filter = slapi_filter_sprintf(filter, ESC_AND_NORM_NEXT_VAL,
e3ffab
-                                      user_dn, intfilter, extfilter);
e3ffab
-    }
e3ffab
-
e3ffab
-    /* Create the search. */
e3ffab
-    pb = slapi_pblock_new();
e3ffab
-    if (token_dn != NULL) {
e3ffab
-        /* Find only the token specified. */
e3ffab
-        slapi_search_internal_set_pb(pb, token_dn, LDAP_SCOPE_BASE, filter,
e3ffab
-                                     NULL, 0, NULL, NULL, id, 0);
e3ffab
-    } else {
e3ffab
-        sdn = slapi_sdn_new_dn_byval(user_dn);
e3ffab
-        if (sdn == NULL)
e3ffab
-            goto error;
e3ffab
-
e3ffab
-        basedn = get_basedn(sdn);
e3ffab
-        if (basedn == NULL)
e3ffab
-            goto error;
e3ffab
-
e3ffab
-        /* Find all user tokens. */
e3ffab
-        slapi_search_internal_set_pb(pb, basedn,
e3ffab
-                                     LDAP_SCOPE_SUBTREE, filter, NULL,
e3ffab
-                                     0, NULL, NULL, id, 0);
e3ffab
-    }
e3ffab
-    slapi_search_internal_pb(pb);
e3ffab
-    slapi_ch_free_string(&filter);
e3ffab
-
e3ffab
-    /* Get the results. */
e3ffab
-    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
e3ffab
-    if (result != LDAP_SUCCESS)
e3ffab
-        goto error;
e3ffab
-    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
e3ffab
-    if (entries == NULL)
e3ffab
-        goto error;
e3ffab
-
e3ffab
-    /* TODO: Can I get the count another way? */
e3ffab
-    for (count = 0; entries[count] != NULL; count++)
e3ffab
-        continue;
e3ffab
-
e3ffab
-    /* Create the array. */
e3ffab
-    tokens = calloc(count + 1, sizeof(*tokens));
e3ffab
-    if (tokens == NULL)
e3ffab
-        goto error;
e3ffab
-    for (count = 0; entries[count] != NULL; count++) {
e3ffab
-        tokens[count] = otptoken_new(id, entries[count]);
e3ffab
-        if (tokens[count] == NULL) {
e3ffab
-            otptoken_free_array(tokens);
e3ffab
-            tokens = NULL;
e3ffab
-            goto error;
e3ffab
-        }
e3ffab
-    }
e3ffab
-
e3ffab
-error:
e3ffab
-    if (sdn != NULL)
e3ffab
-        slapi_sdn_free(&sdn;;
e3ffab
-    slapi_pblock_destroy(pb);
e3ffab
-    return tokens;
e3ffab
-}
e3ffab
-
e3ffab
-struct otptoken **otptoken_find(Slapi_ComponentId *id, const char *user_dn,
e3ffab
-                                const char *token_dn, bool active,
e3ffab
-                                const char *filter)
e3ffab
-{
e3ffab
-    static const char template[] =
e3ffab
-    "(|(ipatokenNotBefore<=%04d%02d%02d%02d%02d%02dZ)(!(ipatokenNotBefore=*)))"
e3ffab
-    "(|(ipatokenNotAfter>=%04d%02d%02d%02d%02d%02dZ)(!(ipatokenNotAfter=*)))"
e3ffab
-    "(|(ipatokenDisabled=FALSE)(!(ipatokenDisabled=*)))";
e3ffab
-    char actfilt[sizeof(template)];
e3ffab
-    struct tm tm;
e3ffab
-    time_t now;
e3ffab
-
e3ffab
-    if (!active)
e3ffab
-        return find(id, user_dn, token_dn, NULL, filter);
e3ffab
-
e3ffab
-    /* Get the current time. */
e3ffab
-    if (time(&now) == (time_t) -1)
e3ffab
-        return NULL;
e3ffab
-    if (gmtime_r(&now, &tm) == NULL)
e3ffab
-        return NULL;
e3ffab
-
e3ffab
-    /* Get the current time string. */
e3ffab
-    if (snprintf(actfilt, sizeof(actfilt), template,
e3ffab
-                 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
e3ffab
-                 tm.tm_hour, tm.tm_min, tm.tm_sec,
e3ffab
-                 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
e3ffab
-                 tm.tm_hour, tm.tm_min, tm.tm_sec) < 0)
e3ffab
-        return NULL;
e3ffab
-
e3ffab
-    return find(id, user_dn, token_dn, actfilt, filter);
e3ffab
-}
e3ffab
-
e3ffab
-int otptoken_get_digits(struct otptoken *token)
e3ffab
-{
e3ffab
-    return token == NULL ? 0 : token->token.digits;
e3ffab
-}
e3ffab
-
e3ffab
-const Slapi_DN *otptoken_get_sdn(struct otptoken *token)
e3ffab
-{
e3ffab
-    return token->sdn;
e3ffab
-}
e3ffab
-
e3ffab
-static bool otptoken_validate(struct otptoken *token, size_t steps,
e3ffab
-                              uint32_t code)
e3ffab
-{
e3ffab
-    time_t now = 0;
e3ffab
-
e3ffab
-    if (token == NULL)
e3ffab
-        return false;
e3ffab
-
e3ffab
-    /* We only need the local time for time-based tokens. */
e3ffab
-    if (token->type == OTPTOKEN_TOTP && time(&now) == (time_t) -1)
e3ffab
-        return false;
e3ffab
-
e3ffab
-    for (int i = 0; i <= steps; i++) {
e3ffab
-        /* Validate the positive step. */
e3ffab
-        if (validate(token, now, i, code, NULL))
e3ffab
-            return true;
e3ffab
-
e3ffab
-        /* Validate the negative step. */
e3ffab
-        if (validate(token, now, 0 - i, code, NULL))
e3ffab
-            return true;
e3ffab
-    }
e3ffab
-
e3ffab
-    return false;
e3ffab
-}
e3ffab
-
e3ffab
-
e3ffab
-/*
e3ffab
- *  Convert code berval to decimal.
e3ffab
- *
e3ffab
- *  NOTE: We can't use atol() or strtoul() because:
e3ffab
- *    1. If we have leading zeros, atol() fails.
e3ffab
- *    2. Neither support limiting conversion by length.
e3ffab
- */
e3ffab
-static bool bvtod(const struct berval *code, uint32_t *out)
e3ffab
-{
e3ffab
-    *out = 0;
e3ffab
-
e3ffab
-    for (ber_len_t i = 0; i < code->bv_len; i++) {
e3ffab
-        if (code->bv_val[i] < '0' || code->bv_val[i] > '9')
e3ffab
-            return false;
e3ffab
-        *out *= 10;
e3ffab
-        *out += code->bv_val[i] - '0';
e3ffab
-    }
e3ffab
-
e3ffab
-    return code->bv_len != 0;
e3ffab
-}
e3ffab
-
e3ffab
-bool otptoken_validate_berval(struct otptoken *token, size_t steps,
e3ffab
-                              const struct berval *code, bool tail)
e3ffab
-{
e3ffab
-    struct berval tmp;
e3ffab
-    uint32_t otp;
e3ffab
-
e3ffab
-    if (token == NULL || code == NULL)
e3ffab
-        return false;
e3ffab
-    tmp = *code;
e3ffab
-
e3ffab
-    if (tmp.bv_len < token->token.digits)
e3ffab
-        return false;
e3ffab
-
e3ffab
-    if (tail)
e3ffab
-        tmp.bv_val = &tmp.bv_val[tmp.bv_len - token->token.digits];
e3ffab
-    tmp.bv_len = token->token.digits;
e3ffab
-
e3ffab
-    if (!bvtod(&tmp, &otp))
e3ffab
-        return false;
e3ffab
-
e3ffab
-    return otptoken_validate(token, steps, otp);
e3ffab
-}
e3ffab
-
e3ffab
-static bool otptoken_sync(struct otptoken * const *tokens, size_t steps,
e3ffab
-                          uint32_t first_code, uint32_t second_code)
e3ffab
-{
e3ffab
-    time_t now = 0;
e3ffab
-
e3ffab
-    if (tokens == NULL)
e3ffab
-        return false;
e3ffab
-
e3ffab
-    if (time(&now) == (time_t) -1)
e3ffab
-        return false;
e3ffab
-
e3ffab
-    for (int i = 0; i <= steps; i++) {
e3ffab
-        for (int j = 0; tokens[j] != NULL; j++) {
e3ffab
-            /* Validate the positive step. */
e3ffab
-            if (validate(tokens[j], now, i, first_code, &second_code))
e3ffab
-                return true;
e3ffab
-
e3ffab
-            /* Validate the negative step. */
e3ffab
-            if (validate(tokens[j], now, 0 - i, first_code, &second_code))
e3ffab
-                return true;
e3ffab
-        }
e3ffab
-    }
e3ffab
-
e3ffab
-    return false;
e3ffab
-}
e3ffab
-
e3ffab
-bool otptoken_sync_berval(struct otptoken * const *tokens, size_t steps,
e3ffab
-                          const struct berval *first_code,
e3ffab
-                          const struct berval *second_code)
e3ffab
-{
e3ffab
-    uint32_t second = 0;
e3ffab
-    uint32_t first = 0;
e3ffab
-
e3ffab
-    if (!bvtod(first_code, &first))
e3ffab
-        return false;
e3ffab
-
e3ffab
-    if (!bvtod(second_code, &second))
e3ffab
-        return false;
e3ffab
-
e3ffab
-    return otptoken_sync(tokens, steps, first, second);
e3ffab
-}
e3ffab
diff --git a/daemons/ipa-slapi-plugins/libotp/libotp.h b/daemons/ipa-slapi-plugins/libotp/libotp.h
e3ffab
deleted file mode 100644
e3ffab
index 24915f8667dfa13ffb1ae47d223df95d4622012e..0000000000000000000000000000000000000000
e3ffab
--- a/daemons/ipa-slapi-plugins/libotp/libotp.h
e3ffab
+++ /dev/null
e3ffab
@@ -1,93 +0,0 @@
e3ffab
-/** BEGIN COPYRIGHT BLOCK
e3ffab
- * This program is free software; you can redistribute it and/or modify
e3ffab
- * it under the terms of the GNU General Public License as published by
e3ffab
- * the Free Software Foundation, either version 3 of the License, or
e3ffab
- * (at your option) any later version.
e3ffab
- *
e3ffab
- * This program is distributed in the hope that it will be useful,
e3ffab
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
e3ffab
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
e3ffab
- * GNU General Public License for more details.
e3ffab
- *
e3ffab
- * You should have received a copy of the GNU General Public License
e3ffab
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
e3ffab
- *
e3ffab
- * Additional permission under GPLv3 section 7:
e3ffab
- *
e3ffab
- * In the following paragraph, "GPL" means the GNU General Public
e3ffab
- * License, version 3 or any later version, and "Non-GPL Code" means
e3ffab
- * code that is governed neither by the GPL nor a license
e3ffab
- * compatible with the GPL.
e3ffab
- *
e3ffab
- * You may link the code of this Program with Non-GPL Code and convey
e3ffab
- * linked combinations including the two, provided that such Non-GPL
e3ffab
- * Code only links to the code of this Program through those well
e3ffab
- * defined interfaces identified in the file named EXCEPTION found in
e3ffab
- * the source code files (the "Approved Interfaces"). The files of
e3ffab
- * Non-GPL Code may instantiate templates or use macros or inline
e3ffab
- * functions from the Approved Interfaces without causing the resulting
e3ffab
- * work to be covered by the GPL. Only the copyright holders of this
e3ffab
- * Program may make changes or additions to the list of Approved
e3ffab
- * Interfaces.
e3ffab
- *
e3ffab
- * Authors:
e3ffab
- * Nathaniel McCallum <npmccallum@redhat.com>
e3ffab
- *
e3ffab
- * Copyright (C) 2013 Red Hat, Inc.
e3ffab
- * All rights reserved.
e3ffab
- * END COPYRIGHT BLOCK **/
e3ffab
-
e3ffab
-#ifndef LIBOTP_H_
e3ffab
-#define LIBOTP_H_
e3ffab
-
e3ffab
-#ifdef HAVE_CONFIG_H
e3ffab
-#  include <config.h>
e3ffab
-#endif
e3ffab
-
e3ffab
-#include <dirsrv/slapi-plugin.h>
e3ffab
-#include <stdbool.h>
e3ffab
-#include <stdlib.h>
e3ffab
-
e3ffab
-struct otptoken;
e3ffab
-
e3ffab
-/* Frees the token array. */
e3ffab
-void otptoken_free_array(struct otptoken **tokens);
e3ffab
-
e3ffab
-/* Find tokens.
e3ffab
- *
e3ffab
- * All criteria below are cumulative. For example, if you specify both dn and
e3ffab
- * active and the token at the dn specified isn't active, an empty array will
e3ffab
- * be returned.
e3ffab
- *
e3ffab
- * If user_dn is not NULL, the user's tokens are returned.
e3ffab
- *
e3ffab
- * If token_dn is not NULL, only this specified token is returned.
e3ffab
- *
e3ffab
- * If active is true, only tokens that are active are returned.
e3ffab
- *
e3ffab
- * If filter is not NULL, the filter will be added to the search criteria.
e3ffab
- *
e3ffab
- * Returns NULL on error. If no tokens are found, an empty array is returned.
e3ffab
- * The array is NULL terminated.
e3ffab
- */
e3ffab
-struct otptoken **otptoken_find(Slapi_ComponentId *id, const char *user_dn,
e3ffab
-                                const char *token_dn, bool active,
e3ffab
-                                const char *filter);
e3ffab
-
e3ffab
-/* Get the length of the token code. */
e3ffab
-int otptoken_get_digits(struct otptoken *token);
e3ffab
-
e3ffab
-/* Get the SDN of the token. */
e3ffab
-const Slapi_DN *otptoken_get_sdn(struct otptoken *token);
e3ffab
-
e3ffab
-/* Validate the token code within a range of steps. If tail is true,
e3ffab
- * it will be assumed that the token is specified at the end of the string. */
e3ffab
-bool otptoken_validate_berval(struct otptoken *token, size_t steps,
e3ffab
-                              const struct berval *code, bool tail);
e3ffab
-
e3ffab
-/* Synchronize the token within a range of steps. */
e3ffab
-bool otptoken_sync_berval(struct otptoken * const *tokens, size_t steps,
e3ffab
-                          const struct berval *first_code,
e3ffab
-                          const struct berval *second_code);
e3ffab
-
e3ffab
-#endif /* LIBOTP_H_ */
e3ffab
diff --git a/daemons/ipa-slapi-plugins/libotp/librfc.c b/daemons/ipa-slapi-plugins/libotp/librfc.c
e3ffab
deleted file mode 100644
e3ffab
index d74820e957e3753834f376cd3ea0ae748e450243..0000000000000000000000000000000000000000
e3ffab
--- a/daemons/ipa-slapi-plugins/libotp/librfc.c
e3ffab
+++ /dev/null
e3ffab
@@ -1,170 +0,0 @@
e3ffab
-/** BEGIN COPYRIGHT BLOCK
e3ffab
- * This program is free software; you can redistribute it and/or modify
e3ffab
- * it under the terms of the GNU General Public License as published by
e3ffab
- * the Free Software Foundation, either version 3 of the License, or
e3ffab
- * (at your option) any later version.
e3ffab
- *
e3ffab
- * This program is distributed in the hope that it will be useful,
e3ffab
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
e3ffab
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
e3ffab
- * GNU General Public License for more details.
e3ffab
- *
e3ffab
- * You should have received a copy of the GNU General Public License
e3ffab
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
e3ffab
- *
e3ffab
- * Additional permission under GPLv3 section 7:
e3ffab
- *
e3ffab
- * In the following paragraph, "GPL" means the GNU General Public
e3ffab
- * License, version 3 or any later version, and "Non-GPL Code" means
e3ffab
- * code that is governed neither by the GPL nor a license
e3ffab
- * compatible with the GPL.
e3ffab
- *
e3ffab
- * You may link the code of this Program with Non-GPL Code and convey
e3ffab
- * linked combinations including the two, provided that such Non-GPL
e3ffab
- * Code only links to the code of this Program through those well
e3ffab
- * defined interfaces identified in the file named EXCEPTION found in
e3ffab
- * the source code files (the "Approved Interfaces"). The files of
e3ffab
- * Non-GPL Code may instantiate templates or use macros or inline
e3ffab
- * functions from the Approved Interfaces without causing the resulting
e3ffab
- * work to be covered by the GPL. Only the copyright holders of this
e3ffab
- * Program may make changes or additions to the list of Approved
e3ffab
- * Interfaces.
e3ffab
- *
e3ffab
- * Authors:
e3ffab
- * Nathaniel McCallum <npmccallum@redhat.com>
e3ffab
- *
e3ffab
- * Copyright (C) 2013 Red Hat, Inc.
e3ffab
- * All rights reserved.
e3ffab
- * END COPYRIGHT BLOCK **/
e3ffab
-
e3ffab
-/*
e3ffab
- * This file contains an implementation of HOTP (RFC 4226) and TOTP (RFC 6238).
e3ffab
- * For details of how these algorithms work, please see the relevant RFCs.
e3ffab
- */
e3ffab
-
e3ffab
-#include "librfc.h"
e3ffab
-#include <time.h>
e3ffab
-
e3ffab
-#include <nss.h>
e3ffab
-#include <pk11pub.h>
e3ffab
-#include <hasht.h>
e3ffab
-#include <prnetdb.h>
e3ffab
-
e3ffab
-struct digest_buffer {
e3ffab
-    uint8_t buf[SHA512_LENGTH];
e3ffab
-    unsigned int len;
e3ffab
-};
e3ffab
-
e3ffab
-static const struct {
e3ffab
-    const char *algo;
e3ffab
-    CK_MECHANISM_TYPE mech;
e3ffab
-} algo2mech[] = {
e3ffab
-    { "sha1",   CKM_SHA_1_HMAC },
e3ffab
-    { "sha256", CKM_SHA256_HMAC },
e3ffab
-    { "sha384", CKM_SHA384_HMAC },
e3ffab
-    { "sha512", CKM_SHA512_HMAC },
e3ffab
-    { }
e3ffab
-};
e3ffab
-
e3ffab
-/*
e3ffab
- * This code is mostly cargo-cult taken from here:
e3ffab
- *   http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn5.html
e3ffab
- *
e3ffab
- * It should implement HMAC with the given mechanism (SHA: 1, 256, 384, 512).
e3ffab
- */
e3ffab
-static bool hmac(SECItem *key, CK_MECHANISM_TYPE mech, const SECItem *in,
e3ffab
-                 struct digest_buffer *out)
e3ffab
-{
e3ffab
-    SECItem param = { siBuffer, NULL, 0 };
e3ffab
-    PK11SlotInfo *slot = NULL;
e3ffab
-    PK11SymKey *symkey = NULL;
e3ffab
-    PK11Context *ctx = NULL;
e3ffab
-    bool ret = false;
e3ffab
-    SECStatus s;
e3ffab
-
e3ffab
-    slot = PK11_GetBestSlot(mech, NULL);
e3ffab
-    if (slot == NULL) {
e3ffab
-        slot = PK11_GetInternalKeySlot();
e3ffab
-        if (slot == NULL) {
e3ffab
-            goto done;
e3ffab
-        }
e3ffab
-    }
e3ffab
-
e3ffab
-    symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap,
e3ffab
-                               CKA_SIGN, key, NULL);
e3ffab
-    if (symkey == NULL)
e3ffab
-        goto done;
e3ffab
-
e3ffab
-    ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN, symkey, ¶m;;
e3ffab
-    if (ctx == NULL)
e3ffab
-        goto done;
e3ffab
-
e3ffab
-    s = PK11_DigestBegin(ctx);
e3ffab
-    if (s != SECSuccess)
e3ffab
-        goto done;
e3ffab
-
e3ffab
-    s = PK11_DigestOp(ctx, in->data, in->len);
e3ffab
-    if (s != SECSuccess)
e3ffab
-        goto done;
e3ffab
-
e3ffab
-    s = PK11_DigestFinal(ctx, out->buf, &out->len, sizeof(out->buf));
e3ffab
-    if (s != SECSuccess)
e3ffab
-        goto done;
e3ffab
-
e3ffab
-    ret = true;
e3ffab
-
e3ffab
-done:
e3ffab
-    if (ctx != NULL)
e3ffab
-        PK11_DestroyContext(ctx, PR_TRUE);
e3ffab
-    if (symkey != NULL)
e3ffab
-        PK11_FreeSymKey(symkey);
e3ffab
-    if (slot != NULL)
e3ffab
-        PK11_FreeSlot(slot);
e3ffab
-    return ret;
e3ffab
-}
e3ffab
-
e3ffab
-/*
e3ffab
- * An implementation of HOTP (RFC 4226).
e3ffab
- */
e3ffab
-bool hotp(const struct hotp_token *token, uint64_t counter, uint32_t *out)
e3ffab
-{
e3ffab
-    const SECItem cntr = { siBuffer, (uint8_t *) &counter, sizeof(counter) };
e3ffab
-    SECItem keyitm = { siBuffer, token->key.bytes, token->key.len };
e3ffab
-    CK_MECHANISM_TYPE mech = CKM_SHA_1_HMAC;
e3ffab
-    PRUint64 offset, binary, div;
e3ffab
-    struct digest_buffer digest;
e3ffab
-    int digits = token->digits;
e3ffab
-    int i;
e3ffab
-
e3ffab
-    /* Convert counter to network byte order. */
e3ffab
-    counter = PR_htonll(counter);
e3ffab
-
e3ffab
-    /* Find the mech. */
e3ffab
-    for (i = 0; algo2mech[i].algo; i++) {
e3ffab
-        if (strcasecmp(algo2mech[i].algo, token->algo) == 0) {
e3ffab
-            mech = algo2mech[i].mech;
e3ffab
-            break;
e3ffab
-        }
e3ffab
-    }
e3ffab
-
e3ffab
-    /* Create the digits divisor. */
e3ffab
-    for (div = 1; digits > 0; digits--) {
e3ffab
-        div *= 10;
e3ffab
-    }
e3ffab
-
e3ffab
-    /* Do the digest. */
e3ffab
-    if (!hmac(&keyitm, mech, &cntr, &digest)) {
e3ffab
-        return false;
e3ffab
-    }
e3ffab
-
e3ffab
-    /* Truncate. */
e3ffab
-    offset  = digest.buf[digest.len - 1] & 0xf;
e3ffab
-    binary  = (digest.buf[offset + 0] & 0x7f) << 0x18;
e3ffab
-    binary |= (digest.buf[offset + 1] & 0xff) << 0x10;
e3ffab
-    binary |= (digest.buf[offset + 2] & 0xff) << 0x08;
e3ffab
-    binary |= (digest.buf[offset + 3] & 0xff) << 0x00;
e3ffab
-    binary  = binary % div;
e3ffab
-
e3ffab
-    *out = binary;
e3ffab
-    return true;
e3ffab
-}
e3ffab
diff --git a/daemons/ipa-slapi-plugins/libotp/librfc.h b/daemons/ipa-slapi-plugins/libotp/librfc.h
e3ffab
deleted file mode 100644
e3ffab
index 04b117600fbcb03fd61258afdd892ae3d514a121..0000000000000000000000000000000000000000
e3ffab
--- a/daemons/ipa-slapi-plugins/libotp/librfc.h
e3ffab
+++ /dev/null
e3ffab
@@ -1,63 +0,0 @@
e3ffab
-/** BEGIN COPYRIGHT BLOCK
e3ffab
- * This program is free software; you can redistribute it and/or modify
e3ffab
- * it under the terms of the GNU General Public License as published by
e3ffab
- * the Free Software Foundation, either version 3 of the License, or
e3ffab
- * (at your option) any later version.
e3ffab
- *
e3ffab
- * This program is distributed in the hope that it will be useful,
e3ffab
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
e3ffab
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
e3ffab
- * GNU General Public License for more details.
e3ffab
- *
e3ffab
- * You should have received a copy of the GNU General Public License
e3ffab
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
e3ffab
- *
e3ffab
- * Additional permission under GPLv3 section 7:
e3ffab
- *
e3ffab
- * In the following paragraph, "GPL" means the GNU General Public
e3ffab
- * License, version 3 or any later version, and "Non-GPL Code" means
e3ffab
- * code that is governed neither by the GPL nor a license
e3ffab
- * compatible with the GPL.
e3ffab
- *
e3ffab
- * You may link the code of this Program with Non-GPL Code and convey
e3ffab
- * linked combinations including the two, provided that such Non-GPL
e3ffab
- * Code only links to the code of this Program through those well
e3ffab
- * defined interfaces identified in the file named EXCEPTION found in
e3ffab
- * the source code files (the "Approved Interfaces"). The files of
e3ffab
- * Non-GPL Code may instantiate templates or use macros or inline
e3ffab
- * functions from the Approved Interfaces without causing the resulting
e3ffab
- * work to be covered by the GPL. Only the copyright holders of this
e3ffab
- * Program may make changes or additions to the list of Approved
e3ffab
- * Interfaces.
e3ffab
- *
e3ffab
- * Authors:
e3ffab
- * Nathaniel McCallum <npmccallum@redhat.com>
e3ffab
- *
e3ffab
- * Copyright (C) 2013 Red Hat, Inc.
e3ffab
- * All rights reserved.
e3ffab
- * END COPYRIGHT BLOCK **/
e3ffab
-
e3ffab
-#ifndef LIBRFC_H_
e3ffab
-#define LIBRFC_H_
e3ffab
-
e3ffab
-#include <stdbool.h>
e3ffab
-#include <stdint.h>
e3ffab
-#include <stdlib.h>
e3ffab
-
e3ffab
-struct hotp_token_key {
e3ffab
-    uint8_t *bytes;
e3ffab
-    size_t len;
e3ffab
-};
e3ffab
-
e3ffab
-struct hotp_token {
e3ffab
-    struct hotp_token_key key;
e3ffab
-    char *algo;
e3ffab
-    int digits;
e3ffab
-};
e3ffab
-
e3ffab
-/*
e3ffab
- * An implementation of HOTP (RFC 4226).
e3ffab
- */
e3ffab
-bool hotp(const struct hotp_token *token, uint64_t counter, uint32_t *out);
e3ffab
-
e3ffab
-#endif /* LIBRFC_H_ */
e3ffab
diff --git a/daemons/ipa-slapi-plugins/libotp/otp_token.c b/daemons/ipa-slapi-plugins/libotp/otp_token.c
e3ffab
new file mode 100644
e3ffab
index 0000000000000000000000000000000000000000..7860c8aba6e12e319d633ee8e165403289a7528b
e3ffab
--- /dev/null
e3ffab
+++ b/daemons/ipa-slapi-plugins/libotp/otp_token.c
e3ffab
@@ -0,0 +1,572 @@
e3ffab
+/** BEGIN COPYRIGHT BLOCK
e3ffab
+ * This program is free software; you can redistribute it and/or modify
e3ffab
+ * it under the terms of the GNU General Public License as published by
e3ffab
+ * the Free Software Foundation, either version 3 of the License, or
e3ffab
+ * (at your option) any later version.
e3ffab
+ *
e3ffab
+ * This program is distributed in the hope that it will be useful,
e3ffab
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
e3ffab
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
e3ffab
+ * GNU General Public License for more details.
e3ffab
+ *
e3ffab
+ * You should have received a copy of the GNU General Public License
e3ffab
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
e3ffab
+ *
e3ffab
+ * Additional permission under GPLv3 section 7:
e3ffab
+ *
e3ffab
+ * In the following paragraph, "GPL" means the GNU General Public
e3ffab
+ * License, version 3 or any later version, and "Non-GPL Code" means
e3ffab
+ * code that is governed neither by the GPL nor a license
e3ffab
+ * compatible with the GPL.
e3ffab
+ *
e3ffab
+ * You may link the code of this Program with Non-GPL Code and convey
e3ffab
+ * linked combinations including the two, provided that such Non-GPL
e3ffab
+ * Code only links to the code of this Program through those well
e3ffab
+ * defined interfaces identified in the file named EXCEPTION found in
e3ffab
+ * the source code files (the "Approved Interfaces"). The files of
e3ffab
+ * Non-GPL Code may instantiate templates or use macros or inline
e3ffab
+ * functions from the Approved Interfaces without causing the resulting
e3ffab
+ * work to be covered by the GPL. Only the copyright holders of this
e3ffab
+ * Program may make changes or additions to the list of Approved
e3ffab
+ * Interfaces.
e3ffab
+ *
e3ffab
+ * Authors:
e3ffab
+ * Nathaniel McCallum <npmccallum@redhat.com>
e3ffab
+ *
e3ffab
+ * Copyright (C) 2013 Red Hat, Inc.
e3ffab
+ * All rights reserved.
e3ffab
+ * END COPYRIGHT BLOCK **/
e3ffab
+
e3ffab
+#include "otp_token.h"
e3ffab
+#include "hotp.h"
e3ffab
+
e3ffab
+#include <time.h>
e3ffab
+#include <errno.h>
e3ffab
+
e3ffab
+#define TOKEN(s) "ipaToken" s
e3ffab
+#define O(s) TOKEN("OTP" s)
e3ffab
+#define T(s) TOKEN("TOTP" s)
e3ffab
+#define H(s) TOKEN("HOTP" s)
e3ffab
+
e3ffab
+#define IPA_OTP_DEFAULT_TOKEN_STEP 30
e3ffab
+#define IPA_OTP_OBJCLS_FILTER \
e3ffab
+    "(|(objectClass=ipaTokenTOTP)(objectClass=ipaTokenHOTP))"
e3ffab
+
e3ffab
+enum type {
e3ffab
+    TYPE_NONE = 0,
e3ffab
+    TYPE_TOTP,
e3ffab
+    TYPE_HOTP,
e3ffab
+};
e3ffab
+
e3ffab
+struct otp_token {
e3ffab
+    Slapi_ComponentId *plugin_id;
e3ffab
+    Slapi_DN *sdn;
e3ffab
+    struct hotp_token token;
e3ffab
+    enum type type;
e3ffab
+    union {
e3ffab
+        struct {
e3ffab
+            uint64_t watermark;
e3ffab
+            unsigned int step;
e3ffab
+            int offset;
e3ffab
+        } totp;
e3ffab
+        struct {
e3ffab
+            uint64_t counter;
e3ffab
+        } hotp;
e3ffab
+    };
e3ffab
+};
e3ffab
+
e3ffab
+static const char *get_basedn(Slapi_DN *dn)
e3ffab
+{
e3ffab
+    Slapi_DN *suffix = NULL;
e3ffab
+    void *node = NULL;
e3ffab
+
e3ffab
+    for (suffix = slapi_get_first_suffix(&node, 0);
e3ffab
+         suffix != NULL;
e3ffab
+         suffix = slapi_get_next_suffix(&node, 0)) {
e3ffab
+        if (slapi_sdn_issuffix(dn, suffix))
e3ffab
+            return (char *) slapi_sdn_get_dn(suffix);
e3ffab
+    }
e3ffab
+
e3ffab
+    return NULL;
e3ffab
+}
e3ffab
+
e3ffab
+static inline bool is_algo_valid(const char *algo)
e3ffab
+{
e3ffab
+    static const char *valid_algos[] = { "sha1", "sha256", "sha384",
e3ffab
+                                         "sha512", NULL };
e3ffab
+    int i, ret;
e3ffab
+
e3ffab
+    for (i = 0; valid_algos[i]; i++) {
e3ffab
+        ret = strcasecmp(algo, valid_algos[i]);
e3ffab
+        if (ret == 0)
e3ffab
+            return true;
e3ffab
+    }
e3ffab
+
e3ffab
+    return false;
e3ffab
+}
e3ffab
+
e3ffab
+static const struct berval *entry_attr_get_berval(const Slapi_Entry* e,
e3ffab
+                                                  const char *type)
e3ffab
+{
e3ffab
+    Slapi_Attr* attr = NULL;
e3ffab
+    Slapi_Value *v;
e3ffab
+    int ret;
e3ffab
+
e3ffab
+    ret = slapi_entry_attr_find(e, type, &attr);
e3ffab
+    if (ret != 0 || attr == NULL)
e3ffab
+        return NULL;
e3ffab
+
e3ffab
+    ret = slapi_attr_first_value(attr, &v);
e3ffab
+    if (ret < 0)
e3ffab
+        return NULL;
e3ffab
+
e3ffab
+    return slapi_value_get_berval(v);
e3ffab
+}
e3ffab
+
e3ffab
+static bool writeattr(const struct otp_token *token, const char *attr,
e3ffab
+                      long long val)
e3ffab
+{
e3ffab
+    Slapi_PBlock *pb = NULL;
e3ffab
+    bool success = false;
e3ffab
+    char value[32];
e3ffab
+    int ret;
e3ffab
+
e3ffab
+    LDAPMod *mods[] = {
e3ffab
+        &(LDAPMod) {
e3ffab
+            LDAP_MOD_REPLACE, (char *) attr,
e3ffab
+            .mod_values = (char *[]) { value, NULL }
e3ffab
+        },
e3ffab
+        NULL
e3ffab
+    };
e3ffab
+
e3ffab
+    snprintf(value, sizeof(value), "%lld", val);
e3ffab
+
e3ffab
+    pb = slapi_pblock_new();
e3ffab
+    slapi_modify_internal_set_pb(pb, slapi_sdn_get_dn(token->sdn),
e3ffab
+                                 mods, NULL, NULL, token->plugin_id, 0);
e3ffab
+    if (slapi_modify_internal_pb(pb) != 0)
e3ffab
+        goto error;
e3ffab
+    if (slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret) != 0)
e3ffab
+        goto error;
e3ffab
+    if (ret != LDAP_SUCCESS)
e3ffab
+        goto error;
e3ffab
+
e3ffab
+    success = true;
e3ffab
+
e3ffab
+error:
e3ffab
+    slapi_pblock_destroy(pb);
e3ffab
+    return success;
e3ffab
+}
e3ffab
+
e3ffab
+/**
e3ffab
+ * Validate a token.
e3ffab
+ *
e3ffab
+ * If the second token code is specified, perform synchronization.
e3ffab
+ */
e3ffab
+static bool validate(struct otp_token *token, time_t now, ssize_t step,
e3ffab
+                     uint32_t first, const uint32_t *second)
e3ffab
+{
e3ffab
+    const char *attr;
e3ffab
+    uint32_t tmp;
e3ffab
+
e3ffab
+    /* Calculate the absolute step. */
e3ffab
+    switch (token->type) {
e3ffab
+    case TYPE_TOTP:
e3ffab
+        attr = T("watermark");
e3ffab
+        step = (now + token->totp.offset) / token->totp.step + step;
e3ffab
+        if (token->totp.watermark > 0 && step < token->totp.watermark)
e3ffab
+            return false;
e3ffab
+        break;
e3ffab
+    case TYPE_HOTP:
e3ffab
+        if (step < 0) /* NEVER go backwards! */
e3ffab
+            return false;
e3ffab
+        attr = H("counter");
e3ffab
+        step = token->hotp.counter + step;
e3ffab
+        break;
e3ffab
+    default:
e3ffab
+        return false;
e3ffab
+    }
e3ffab
+
e3ffab
+    /* Validate the first code. */
e3ffab
+    if (!hotp(&token->token, step++, &tmp))
e3ffab
+        return false;
e3ffab
+
e3ffab
+    if (first != tmp)
e3ffab
+        return false;
e3ffab
+
e3ffab
+    /* Validate the second code if specified. */
e3ffab
+    if (second != NULL) {
e3ffab
+        if (!hotp(&token->token, step++, &tmp))
e3ffab
+            return false;
e3ffab
+
e3ffab
+        if (*second != tmp)
e3ffab
+            return false;
e3ffab
+    }
e3ffab
+
e3ffab
+    /* Write the step value. */
e3ffab
+    if (!writeattr(token, attr, step))
e3ffab
+        return false;
e3ffab
+
e3ffab
+    /* Save our modifications to the object. */
e3ffab
+    switch (token->type) {
e3ffab
+    case TYPE_TOTP:
e3ffab
+        /* Perform optional synchronization steps. */
e3ffab
+        if (second != NULL) {
e3ffab
+            tmp = (step - now / token->totp.step) * token->totp.step;
e3ffab
+            if (!writeattr(token, T("clockOffset"), tmp))
e3ffab
+                return false;
e3ffab
+            token->totp.offset = tmp;
e3ffab
+        }
e3ffab
+        token->totp.watermark = step;
e3ffab
+        break;
e3ffab
+    case TYPE_HOTP:
e3ffab
+        token->hotp.counter = step;
e3ffab
+        break;
e3ffab
+    default:
e3ffab
+        break;
e3ffab
+    }
e3ffab
+
e3ffab
+    return true;
e3ffab
+}
e3ffab
+
e3ffab
+static void otp_token_free(struct otp_token *token)
e3ffab
+{
e3ffab
+    if (token == NULL)
e3ffab
+        return;
e3ffab
+
e3ffab
+    slapi_sdn_free(&token->sdn);
e3ffab
+    free(token->token.key.bytes);
e3ffab
+    slapi_ch_free_string(&token->token.algo);
e3ffab
+    free(token);
e3ffab
+}
e3ffab
+
e3ffab
+void otp_token_free_array(struct otp_token **tokens)
e3ffab
+{
e3ffab
+    if (tokens == NULL)
e3ffab
+        return;
e3ffab
+
e3ffab
+    for (size_t i = 0; tokens[i] != NULL; i++)
e3ffab
+        otp_token_free(tokens[i]);
e3ffab
+
e3ffab
+    free(tokens);
e3ffab
+}
e3ffab
+
e3ffab
+static struct otp_token *otp_token_new(Slapi_ComponentId *id,
e3ffab
+                                       Slapi_Entry *entry)
e3ffab
+{
e3ffab
+    const struct berval *tmp;
e3ffab
+    struct otp_token *token;
e3ffab
+    char **vals;
e3ffab
+
e3ffab
+    token = calloc(1, sizeof(struct otp_token));
e3ffab
+    if (token == NULL)
e3ffab
+        return NULL;
e3ffab
+    token->plugin_id = id;
e3ffab
+
e3ffab
+    /* Get the token type. */
e3ffab
+    vals = slapi_entry_attr_get_charray(entry, "objectClass");
e3ffab
+    if (vals == NULL)
e3ffab
+        goto error;
e3ffab
+    token->type = TYPE_NONE;
e3ffab
+    for (int i = 0; vals[i] != NULL; i++) {
e3ffab
+        if (strcasecmp(vals[i], "ipaTokenTOTP") == 0)
e3ffab
+            token->type = TYPE_TOTP;
e3ffab
+        else if (strcasecmp(vals[i], "ipaTokenHOTP") == 0)
e3ffab
+            token->type = TYPE_HOTP;
e3ffab
+    }
e3ffab
+    slapi_ch_array_free(vals);
e3ffab
+    if (token->type == TYPE_NONE)
e3ffab
+        goto error;
e3ffab
+
e3ffab
+    /* Get SDN. */
e3ffab
+    token->sdn = slapi_sdn_dup(slapi_entry_get_sdn(entry));
e3ffab
+    if (token->sdn == NULL)
e3ffab
+        goto error;
e3ffab
+
e3ffab
+    /* Get key. */
e3ffab
+    tmp = entry_attr_get_berval(entry, O("key"));
e3ffab
+    if (tmp == NULL)
e3ffab
+        goto error;
e3ffab
+    token->token.key.len = tmp->bv_len;
e3ffab
+    token->token.key.bytes = malloc(token->token.key.len);
e3ffab
+    if (token->token.key.bytes == NULL)
e3ffab
+        goto error;
e3ffab
+    memcpy(token->token.key.bytes, tmp->bv_val, token->token.key.len);
e3ffab
+
e3ffab
+    /* Get length. */
e3ffab
+    token->token.digits = slapi_entry_attr_get_int(entry, O("digits"));
e3ffab
+    if (token->token.digits != 6 && token->token.digits != 8)
e3ffab
+        goto error;
e3ffab
+
e3ffab
+    /* Get algorithm. */
e3ffab
+    token->token.algo = slapi_entry_attr_get_charptr(entry, O("algorithm"));
e3ffab
+    if (token->token.algo == NULL)
e3ffab
+        token->token.algo = slapi_ch_strdup("sha1");
e3ffab
+    if (!is_algo_valid(token->token.algo))
e3ffab
+        goto error;
e3ffab
+
e3ffab
+    switch (token->type) {
e3ffab
+    case TYPE_TOTP:
e3ffab
+        /* Get offset. */
e3ffab
+        token->totp.offset = slapi_entry_attr_get_int(entry, T("clockOffset"));
e3ffab
+
e3ffab
+        /* Get watermark. */
e3ffab
+        token->totp.watermark = slapi_entry_attr_get_int(entry, T("watermark"));
e3ffab
+
e3ffab
+        /* Get step. */
e3ffab
+        token->totp.step = slapi_entry_attr_get_uint(entry, T("timeStep"));
e3ffab
+        if (token->totp.step == 0)
e3ffab
+            token->totp.step = IPA_OTP_DEFAULT_TOKEN_STEP;
e3ffab
+        break;
e3ffab
+    case TYPE_HOTP:
e3ffab
+        /* Get counter. */
e3ffab
+        token->hotp.counter = slapi_entry_attr_get_int(entry, H("counter"));
e3ffab
+        break;
e3ffab
+    default:
e3ffab
+        break;
e3ffab
+    }
e3ffab
+
e3ffab
+    return token;
e3ffab
+
e3ffab
+error:
e3ffab
+    otp_token_free(token);
e3ffab
+    return NULL;
e3ffab
+}
e3ffab
+
e3ffab
+static struct otp_token **find(Slapi_ComponentId *id, const char *user_dn,
e3ffab
+                               const char *token_dn, const char *intfilter,
e3ffab
+                               const char *extfilter)
e3ffab
+{
e3ffab
+    struct otp_token **tokens = NULL;
e3ffab
+    Slapi_Entry **entries = NULL;
e3ffab
+    Slapi_PBlock *pb = NULL;
e3ffab
+    Slapi_DN *sdn = NULL;
e3ffab
+    char *filter = NULL;
e3ffab
+    const char *basedn = NULL;
e3ffab
+    size_t count = 0;
e3ffab
+    int result = -1;
e3ffab
+
e3ffab
+    if (intfilter == NULL)
e3ffab
+        intfilter = "";
e3ffab
+
e3ffab
+    if (extfilter == NULL)
e3ffab
+        extfilter = "";
e3ffab
+
e3ffab
+    /* Create the filter. */
e3ffab
+    if (user_dn == NULL) {
e3ffab
+        filter = "(&" IPA_OTP_OBJCLS_FILTER "%s%s)";
e3ffab
+        filter = slapi_filter_sprintf(filter, intfilter, extfilter);
e3ffab
+    } else {
e3ffab
+        filter = "(&" IPA_OTP_OBJCLS_FILTER "(ipatokenOwner=%s%s)%s%s)";
e3ffab
+        filter = slapi_filter_sprintf(filter, ESC_AND_NORM_NEXT_VAL,
e3ffab
+                                      user_dn, intfilter, extfilter);
e3ffab
+    }
e3ffab
+
e3ffab
+    /* Create the search. */
e3ffab
+    pb = slapi_pblock_new();
e3ffab
+    if (token_dn != NULL) {
e3ffab
+        /* Find only the token specified. */
e3ffab
+        slapi_search_internal_set_pb(pb, token_dn, LDAP_SCOPE_BASE, filter,
e3ffab
+                                     NULL, 0, NULL, NULL, id, 0);
e3ffab
+    } else {
e3ffab
+        sdn = slapi_sdn_new_dn_byval(user_dn);
e3ffab
+        if (sdn == NULL)
e3ffab
+            goto error;
e3ffab
+
e3ffab
+        basedn = get_basedn(sdn);
e3ffab
+        if (basedn == NULL)
e3ffab
+            goto error;
e3ffab
+
e3ffab
+        /* Find all user tokens. */
e3ffab
+        slapi_search_internal_set_pb(pb, basedn,
e3ffab
+                                     LDAP_SCOPE_SUBTREE, filter, NULL,
e3ffab
+                                     0, NULL, NULL, id, 0);
e3ffab
+    }
e3ffab
+    slapi_search_internal_pb(pb);
e3ffab
+    slapi_ch_free_string(&filter);
e3ffab
+
e3ffab
+    /* Get the results. */
e3ffab
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
e3ffab
+    if (result != LDAP_SUCCESS)
e3ffab
+        goto error;
e3ffab
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
e3ffab
+    if (entries == NULL)
e3ffab
+        goto error;
e3ffab
+
e3ffab
+    /* TODO: Can I get the count another way? */
e3ffab
+    for (count = 0; entries[count] != NULL; count++)
e3ffab
+        continue;
e3ffab
+
e3ffab
+    /* Create the array. */
e3ffab
+    tokens = calloc(count + 1, sizeof(*tokens));
e3ffab
+    if (tokens == NULL)
e3ffab
+        goto error;
e3ffab
+    for (count = 0; entries[count] != NULL; count++) {
e3ffab
+        tokens[count] = otp_token_new(id, entries[count]);
e3ffab
+        if (tokens[count] == NULL) {
e3ffab
+            otp_token_free_array(tokens);
e3ffab
+            tokens = NULL;
e3ffab
+            goto error;
e3ffab
+        }
e3ffab
+    }
e3ffab
+
e3ffab
+error:
e3ffab
+    if (sdn != NULL)
e3ffab
+        slapi_sdn_free(&sdn;;
e3ffab
+    slapi_pblock_destroy(pb);
e3ffab
+    return tokens;
e3ffab
+}
e3ffab
+
e3ffab
+struct otp_token **
e3ffab
+otp_token_find(Slapi_ComponentId *id, const char *user_dn, const char *token_dn,
e3ffab
+               bool active, const char *filter)
e3ffab
+{
e3ffab
+    static const char template[] =
e3ffab
+    "(|(ipatokenNotBefore<=%04d%02d%02d%02d%02d%02dZ)(!(ipatokenNotBefore=*)))"
e3ffab
+    "(|(ipatokenNotAfter>=%04d%02d%02d%02d%02d%02dZ)(!(ipatokenNotAfter=*)))"
e3ffab
+    "(|(ipatokenDisabled=FALSE)(!(ipatokenDisabled=*)))";
e3ffab
+    char actfilt[sizeof(template)];
e3ffab
+    struct tm tm;
e3ffab
+    time_t now;
e3ffab
+
e3ffab
+    if (!active)
e3ffab
+        return find(id, user_dn, token_dn, NULL, filter);
e3ffab
+
e3ffab
+    /* Get the current time. */
e3ffab
+    if (time(&now) == (time_t) -1)
e3ffab
+        return NULL;
e3ffab
+    if (gmtime_r(&now, &tm) == NULL)
e3ffab
+        return NULL;
e3ffab
+
e3ffab
+    /* Get the current time string. */
e3ffab
+    if (snprintf(actfilt, sizeof(actfilt), template,
e3ffab
+                 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
e3ffab
+                 tm.tm_hour, tm.tm_min, tm.tm_sec,
e3ffab
+                 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
e3ffab
+                 tm.tm_hour, tm.tm_min, tm.tm_sec) < 0)
e3ffab
+        return NULL;
e3ffab
+
e3ffab
+    return find(id, user_dn, token_dn, actfilt, filter);
e3ffab
+}
e3ffab
+
e3ffab
+int otp_token_get_digits(struct otp_token *token)
e3ffab
+{
e3ffab
+    return token == NULL ? 0 : token->token.digits;
e3ffab
+}
e3ffab
+
e3ffab
+const Slapi_DN *otp_token_get_sdn(struct otp_token *token)
e3ffab
+{
e3ffab
+    return token->sdn;
e3ffab
+}
e3ffab
+
e3ffab
+static bool otp_token_validate(struct otp_token *token, size_t steps,
e3ffab
+                               uint32_t code)
e3ffab
+{
e3ffab
+    time_t now = 0;
e3ffab
+
e3ffab
+    if (token == NULL)
e3ffab
+        return false;
e3ffab
+
e3ffab
+    /* We only need the local time for time-based tokens. */
e3ffab
+    if (token->type == TYPE_TOTP && time(&now) == (time_t) -1)
e3ffab
+        return false;
e3ffab
+
e3ffab
+    for (int i = 0; i <= steps; i++) {
e3ffab
+        /* Validate the positive step. */
e3ffab
+        if (validate(token, now, i, code, NULL))
e3ffab
+            return true;
e3ffab
+
e3ffab
+        /* Validate the negative step. */
e3ffab
+        if (validate(token, now, 0 - i, code, NULL))
e3ffab
+            return true;
e3ffab
+    }
e3ffab
+
e3ffab
+    return false;
e3ffab
+}
e3ffab
+
e3ffab
+
e3ffab
+/*
e3ffab
+ *  Convert code berval to decimal.
e3ffab
+ *
e3ffab
+ *  NOTE: We can't use atol() or strtoul() because:
e3ffab
+ *    1. If we have leading zeros, atol() fails.
e3ffab
+ *    2. Neither support limiting conversion by length.
e3ffab
+ */
e3ffab
+static bool bvtod(const struct berval *code, uint32_t *out)
e3ffab
+{
e3ffab
+    *out = 0;
e3ffab
+
e3ffab
+    for (ber_len_t i = 0; i < code->bv_len; i++) {
e3ffab
+        if (code->bv_val[i] < '0' || code->bv_val[i] > '9')
e3ffab
+            return false;
e3ffab
+        *out *= 10;
e3ffab
+        *out += code->bv_val[i] - '0';
e3ffab
+    }
e3ffab
+
e3ffab
+    return code->bv_len != 0;
e3ffab
+}
e3ffab
+
e3ffab
+bool otp_token_validate_berval(struct otp_token *token, size_t steps,
e3ffab
+                               const struct berval *code, bool tail)
e3ffab
+{
e3ffab
+    struct berval tmp;
e3ffab
+    uint32_t otp;
e3ffab
+
e3ffab
+    if (token == NULL || code == NULL)
e3ffab
+        return false;
e3ffab
+    tmp = *code;
e3ffab
+
e3ffab
+    if (tmp.bv_len < token->token.digits)
e3ffab
+        return false;
e3ffab
+
e3ffab
+    if (tail)
e3ffab
+        tmp.bv_val = &tmp.bv_val[tmp.bv_len - token->token.digits];
e3ffab
+    tmp.bv_len = token->token.digits;
e3ffab
+
e3ffab
+    if (!bvtod(&tmp, &otp))
e3ffab
+        return false;
e3ffab
+
e3ffab
+    return otp_token_validate(token, steps, otp);
e3ffab
+}
e3ffab
+
e3ffab
+static bool otp_token_sync(struct otp_token * const *tokens, size_t steps,
e3ffab
+                           uint32_t first_code, uint32_t second_code)
e3ffab
+{
e3ffab
+    time_t now = 0;
e3ffab
+
e3ffab
+    if (tokens == NULL)
e3ffab
+        return false;
e3ffab
+
e3ffab
+    if (time(&now) == (time_t) -1)
e3ffab
+        return false;
e3ffab
+
e3ffab
+    for (int i = 0; i <= steps; i++) {
e3ffab
+        for (int j = 0; tokens[j] != NULL; j++) {
e3ffab
+            /* Validate the positive step. */
e3ffab
+            if (validate(tokens[j], now, i, first_code, &second_code))
e3ffab
+                return true;
e3ffab
+
e3ffab
+            /* Validate the negative step. */
e3ffab
+            if (validate(tokens[j], now, 0 - i, first_code, &second_code))
e3ffab
+                return true;
e3ffab
+        }
e3ffab
+    }
e3ffab
+
e3ffab
+    return false;
e3ffab
+}
e3ffab
+
e3ffab
+bool otp_token_sync_berval(struct otp_token * const *tokens, size_t steps,
e3ffab
+                           const struct berval *first_code,
e3ffab
+                           const struct berval *second_code)
e3ffab
+{
e3ffab
+    uint32_t second = 0;
e3ffab
+    uint32_t first = 0;
e3ffab
+
e3ffab
+    if (!bvtod(first_code, &first))
e3ffab
+        return false;
e3ffab
+
e3ffab
+    if (!bvtod(second_code, &second))
e3ffab
+        return false;
e3ffab
+
e3ffab
+    return otp_token_sync(tokens, steps, first, second);
e3ffab
+}
e3ffab
diff --git a/daemons/ipa-slapi-plugins/libotp/otp_token.h b/daemons/ipa-slapi-plugins/libotp/otp_token.h
e3ffab
new file mode 100644
e3ffab
index 0000000000000000000000000000000000000000..2f336780682b5ea2838b558079e2ae85f6e2afba
e3ffab
--- /dev/null
e3ffab
+++ b/daemons/ipa-slapi-plugins/libotp/otp_token.h
e3ffab
@@ -0,0 +1,87 @@
e3ffab
+/** BEGIN COPYRIGHT BLOCK
e3ffab
+ * This program is free software; you can redistribute it and/or modify
e3ffab
+ * it under the terms of the GNU General Public License as published by
e3ffab
+ * the Free Software Foundation, either version 3 of the License, or
e3ffab
+ * (at your option) any later version.
e3ffab
+ *
e3ffab
+ * This program is distributed in the hope that it will be useful,
e3ffab
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
e3ffab
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
e3ffab
+ * GNU General Public License for more details.
e3ffab
+ *
e3ffab
+ * You should have received a copy of the GNU General Public License
e3ffab
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
e3ffab
+ *
e3ffab
+ * Additional permission under GPLv3 section 7:
e3ffab
+ *
e3ffab
+ * In the following paragraph, "GPL" means the GNU General Public
e3ffab
+ * License, version 3 or any later version, and "Non-GPL Code" means
e3ffab
+ * code that is governed neither by the GPL nor a license
e3ffab
+ * compatible with the GPL.
e3ffab
+ *
e3ffab
+ * You may link the code of this Program with Non-GPL Code and convey
e3ffab
+ * linked combinations including the two, provided that such Non-GPL
e3ffab
+ * Code only links to the code of this Program through those well
e3ffab
+ * defined interfaces identified in the file named EXCEPTION found in
e3ffab
+ * the source code files (the "Approved Interfaces"). The files of
e3ffab
+ * Non-GPL Code may instantiate templates or use macros or inline
e3ffab
+ * functions from the Approved Interfaces without causing the resulting
e3ffab
+ * work to be covered by the GPL. Only the copyright holders of this
e3ffab
+ * Program may make changes or additions to the list of Approved
e3ffab
+ * Interfaces.
e3ffab
+ *
e3ffab
+ * Authors:
e3ffab
+ * Nathaniel McCallum <npmccallum@redhat.com>
e3ffab
+ *
e3ffab
+ * Copyright (C) 2013 Red Hat, Inc.
e3ffab
+ * All rights reserved.
e3ffab
+ * END COPYRIGHT BLOCK **/
e3ffab
+
e3ffab
+#pragma once
e3ffab
+
e3ffab
+#include <dirsrv/slapi-plugin.h>
e3ffab
+#include <stdbool.h>
e3ffab
+#include <stdlib.h>
e3ffab
+
e3ffab
+struct otp_token;
e3ffab
+
e3ffab
+/* Frees the token array. */
e3ffab
+void otp_token_free_array(struct otp_token **tokens);
e3ffab
+
e3ffab
+/* Find tokens.
e3ffab
+ *
e3ffab
+ * All criteria below are cumulative. For example, if you specify both dn and
e3ffab
+ * active and the token at the dn specified isn't active, an empty array will
e3ffab
+ * be returned.
e3ffab
+ *
e3ffab
+ * If user_dn is not NULL, the user's tokens are returned.
e3ffab
+ *
e3ffab
+ * If token_dn is not NULL, only this specified token is returned.
e3ffab
+ *
e3ffab
+ * If active is true, only tokens that are active are returned.
e3ffab
+ *
e3ffab
+ * If filter is not NULL, the filter will be added to the search criteria.
e3ffab
+ *
e3ffab
+ * Returns NULL on error. If no tokens are found, an empty array is returned.
e3ffab
+ * The array is NULL terminated.
e3ffab
+ */
e3ffab
+struct otp_token **otp_token_find(Slapi_ComponentId *id, const char *user_dn,
e3ffab
+                                  const char *token_dn, bool active,
e3ffab
+                                  const char *filter);
e3ffab
+
e3ffab
+/* Get the length of the token code. */
e3ffab
+int otp_token_get_digits(struct otp_token *token);
e3ffab
+
e3ffab
+/* Get the SDN of the token. */
e3ffab
+const Slapi_DN *otp_token_get_sdn(struct otp_token *token);
e3ffab
+
e3ffab
+/* Validate the token code within a range of steps. If tail is true,
e3ffab
+ * it will be assumed that the token is specified at the end of the string. */
e3ffab
+bool otp_token_validate_berval(struct otp_token *token, size_t steps,
e3ffab
+                               const struct berval *code, bool tail);
e3ffab
+
e3ffab
+/* Synchronize the token within a range of steps. */
e3ffab
+bool otp_token_sync_berval(struct otp_token * const *tokens, size_t steps,
e3ffab
+                           const struct berval *first_code,
e3ffab
+                           const struct berval *second_code);
e3ffab
+
e3ffab
diff --git a/daemons/ipa-slapi-plugins/libotp/t_hotp.c b/daemons/ipa-slapi-plugins/libotp/t_hotp.c
e3ffab
new file mode 100644
e3ffab
index 0000000000000000000000000000000000000000..2e995fdaa3b2aa067790ade307792a95ded269a9
e3ffab
--- /dev/null
e3ffab
+++ b/daemons/ipa-slapi-plugins/libotp/t_hotp.c
e3ffab
@@ -0,0 +1,121 @@
e3ffab
+/** BEGIN COPYRIGHT BLOCK
e3ffab
+ * This program is free software; you can redistribute it and/or modify
e3ffab
+ * it under the terms of the GNU General Public License as published by
e3ffab
+ * the Free Software Foundation, either version 3 of the License, or
e3ffab
+ * (at your option) any later version.
e3ffab
+ *
e3ffab
+ * This program is distributed in the hope that it will be useful,
e3ffab
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
e3ffab
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
e3ffab
+ * GNU General Public License for more details.
e3ffab
+ *
e3ffab
+ * You should have received a copy of the GNU General Public License
e3ffab
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
e3ffab
+ *
e3ffab
+ * Additional permission under GPLv3 section 7:
e3ffab
+ *
e3ffab
+ * In the following paragraph, "GPL" means the GNU General Public
e3ffab
+ * License, version 3 or any later version, and "Non-GPL Code" means
e3ffab
+ * code that is governed neither by the GPL nor a license
e3ffab
+ * compatible with the GPL.
e3ffab
+ *
e3ffab
+ * You may link the code of this Program with Non-GPL Code and convey
e3ffab
+ * linked combinations including the two, provided that such Non-GPL
e3ffab
+ * Code only links to the code of this Program through those well
e3ffab
+ * defined interfaces identified in the file named EXCEPTION found in
e3ffab
+ * the source code files (the "Approved Interfaces"). The files of
e3ffab
+ * Non-GPL Code may instantiate templates or use macros or inline
e3ffab
+ * functions from the Approved Interfaces without causing the resulting
e3ffab
+ * work to be covered by the GPL. Only the copyright holders of this
e3ffab
+ * Program may make changes or additions to the list of Approved
e3ffab
+ * Interfaces.
e3ffab
+ *
e3ffab
+ * Authors:
e3ffab
+ * Nathaniel McCallum <npmccallum@redhat.com>
e3ffab
+ *
e3ffab
+ * Copyright (C) 2013 Red Hat, Inc.
e3ffab
+ * All rights reserved.
e3ffab
+ * END COPYRIGHT BLOCK **/
e3ffab
+
e3ffab
+#include "hotp.h"
e3ffab
+
e3ffab
+#include <assert.h>
e3ffab
+#include <stddef.h>
e3ffab
+#include <time.h>
e3ffab
+#include <string.h>
e3ffab
+#include <nss.h>
e3ffab
+
e3ffab
+#define KEY(s) { (uint8_t *) s, sizeof(s) - 1 }
e3ffab
+
e3ffab
+/* All HOTP test examples from RFC 4226 (Appendix D). */
e3ffab
+static const struct hotp_token hotp_token = {
e3ffab
+    KEY("12345678901234567890"),
e3ffab
+    "sha1",
e3ffab
+    6
e3ffab
+};
e3ffab
+static const uint32_t hotp_answers[] = {
e3ffab
+    755224,
e3ffab
+    287082,
e3ffab
+    359152,
e3ffab
+    969429,
e3ffab
+    338314,
e3ffab
+    254676,
e3ffab
+    287922,
e3ffab
+    162583,
e3ffab
+    399871,
e3ffab
+    520489
e3ffab
+};
e3ffab
+
e3ffab
+/* All TOTP test examples from RFC 6238 (Appendix B). */
e3ffab
+#define SHA1   { KEY("12345678901234567890"), "sha1", 8 }
e3ffab
+#define SHA256 { KEY("12345678901234567890123456789012"), "sha256", 8 }
e3ffab
+#define SHA512 { KEY("12345678901234567890123456789012" \
e3ffab
+                     "34567890123456789012345678901234"), "sha512", 8 }
e3ffab
+static const struct {
e3ffab
+    struct hotp_token token;
e3ffab
+    time_t time;
e3ffab
+    uint32_t answer;
e3ffab
+} totp_tests[] = {
e3ffab
+    { SHA1,            59, 94287082 },
e3ffab
+    { SHA256,          59, 46119246 },
e3ffab
+    { SHA512,          59, 90693936 },
e3ffab
+    { SHA1,    1111111109,  7081804 },
e3ffab
+    { SHA256,  1111111109, 68084774 },
e3ffab
+    { SHA512,  1111111109, 25091201 },
e3ffab
+    { SHA1,    1111111111, 14050471 },
e3ffab
+    { SHA256,  1111111111, 67062674 },
e3ffab
+    { SHA512,  1111111111, 99943326 },
e3ffab
+    { SHA1,    1234567890, 89005924 },
e3ffab
+    { SHA256,  1234567890, 91819424 },
e3ffab
+    { SHA512,  1234567890, 93441116 },
e3ffab
+    { SHA1,    2000000000, 69279037 },
e3ffab
+    { SHA256,  2000000000, 90698825 },
e3ffab
+    { SHA512,  2000000000, 38618901 },
e3ffab
+#ifdef _LP64 /* Only do these tests on 64-bit systems. */
e3ffab
+    { SHA1,   20000000000, 65353130 },
e3ffab
+    { SHA256, 20000000000, 77737706 },
e3ffab
+    { SHA512, 20000000000, 47863826 },
e3ffab
+#endif
e3ffab
+};
e3ffab
+
e3ffab
+int
e3ffab
+main(int argc, const char *argv[])
e3ffab
+{
e3ffab
+    uint32_t otp;
e3ffab
+    int i;
e3ffab
+
e3ffab
+    NSS_NoDB_Init(".");
e3ffab
+
e3ffab
+    for (i = 0; i < sizeof(hotp_answers) / sizeof(*hotp_answers); i++) {
e3ffab
+        assert(hotp(&hotp_token, i, &otp));
e3ffab
+        assert(otp == hotp_answers[i]);
e3ffab
+    }
e3ffab
+
e3ffab
+    for (i = 0; i < sizeof(totp_tests) / sizeof(*totp_tests); i++) {
e3ffab
+        assert(hotp(&totp_tests[i].token, totp_tests[i].time / 30, &otp));
e3ffab
+        assert(otp == totp_tests[i].answer);
e3ffab
+    }
e3ffab
+
e3ffab
+    NSS_Shutdown();
e3ffab
+    return 0;
e3ffab
+}
e3ffab
diff --git a/daemons/ipa-slapi-plugins/libotp/t_librfc.c b/daemons/ipa-slapi-plugins/libotp/t_librfc.c
e3ffab
deleted file mode 100644
e3ffab
index f7eab7f78e99a825a444f8e496be924342d28b51..0000000000000000000000000000000000000000
e3ffab
--- a/daemons/ipa-slapi-plugins/libotp/t_librfc.c
e3ffab
+++ /dev/null
e3ffab
@@ -1,121 +0,0 @@
e3ffab
-/** BEGIN COPYRIGHT BLOCK
e3ffab
- * This program is free software; you can redistribute it and/or modify
e3ffab
- * it under the terms of the GNU General Public License as published by
e3ffab
- * the Free Software Foundation, either version 3 of the License, or
e3ffab
- * (at your option) any later version.
e3ffab
- *
e3ffab
- * This program is distributed in the hope that it will be useful,
e3ffab
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
e3ffab
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
e3ffab
- * GNU General Public License for more details.
e3ffab
- *
e3ffab
- * You should have received a copy of the GNU General Public License
e3ffab
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
e3ffab
- *
e3ffab
- * Additional permission under GPLv3 section 7:
e3ffab
- *
e3ffab
- * In the following paragraph, "GPL" means the GNU General Public
e3ffab
- * License, version 3 or any later version, and "Non-GPL Code" means
e3ffab
- * code that is governed neither by the GPL nor a license
e3ffab
- * compatible with the GPL.
e3ffab
- *
e3ffab
- * You may link the code of this Program with Non-GPL Code and convey
e3ffab
- * linked combinations including the two, provided that such Non-GPL
e3ffab
- * Code only links to the code of this Program through those well
e3ffab
- * defined interfaces identified in the file named EXCEPTION found in
e3ffab
- * the source code files (the "Approved Interfaces"). The files of
e3ffab
- * Non-GPL Code may instantiate templates or use macros or inline
e3ffab
- * functions from the Approved Interfaces without causing the resulting
e3ffab
- * work to be covered by the GPL. Only the copyright holders of this
e3ffab
- * Program may make changes or additions to the list of Approved
e3ffab
- * Interfaces.
e3ffab
- *
e3ffab
- * Authors:
e3ffab
- * Nathaniel McCallum <npmccallum@redhat.com>
e3ffab
- *
e3ffab
- * Copyright (C) 2013 Red Hat, Inc.
e3ffab
- * All rights reserved.
e3ffab
- * END COPYRIGHT BLOCK **/
e3ffab
-
e3ffab
-#include "librfc.h"
e3ffab
-
e3ffab
-#include <assert.h>
e3ffab
-#include <stddef.h>
e3ffab
-#include <time.h>
e3ffab
-#include <string.h>
e3ffab
-#include <nss.h>
e3ffab
-
e3ffab
-#define KEY(s) { (uint8_t *) s, sizeof(s) - 1 }
e3ffab
-
e3ffab
-/* All HOTP test examples from RFC 4226 (Appendix D). */
e3ffab
-static const struct hotp_token hotp_token = {
e3ffab
-    KEY("12345678901234567890"),
e3ffab
-    "sha1",
e3ffab
-    6
e3ffab
-};
e3ffab
-static const uint32_t hotp_answers[] = {
e3ffab
-    755224,
e3ffab
-    287082,
e3ffab
-    359152,
e3ffab
-    969429,
e3ffab
-    338314,
e3ffab
-    254676,
e3ffab
-    287922,
e3ffab
-    162583,
e3ffab
-    399871,
e3ffab
-    520489
e3ffab
-};
e3ffab
-
e3ffab
-/* All TOTP test examples from RFC 6238 (Appendix B). */
e3ffab
-#define SHA1   { KEY("12345678901234567890"), "sha1", 8 }
e3ffab
-#define SHA256 { KEY("12345678901234567890123456789012"), "sha256", 8 }
e3ffab
-#define SHA512 { KEY("12345678901234567890123456789012" \
e3ffab
-                     "34567890123456789012345678901234"), "sha512", 8 }
e3ffab
-static const struct {
e3ffab
-    struct hotp_token token;
e3ffab
-    time_t time;
e3ffab
-    uint32_t answer;
e3ffab
-} totp_tests[] = {
e3ffab
-    { SHA1,            59, 94287082 },
e3ffab
-    { SHA256,          59, 46119246 },
e3ffab
-    { SHA512,          59, 90693936 },
e3ffab
-    { SHA1,    1111111109,  7081804 },
e3ffab
-    { SHA256,  1111111109, 68084774 },
e3ffab
-    { SHA512,  1111111109, 25091201 },
e3ffab
-    { SHA1,    1111111111, 14050471 },
e3ffab
-    { SHA256,  1111111111, 67062674 },
e3ffab
-    { SHA512,  1111111111, 99943326 },
e3ffab
-    { SHA1,    1234567890, 89005924 },
e3ffab
-    { SHA256,  1234567890, 91819424 },
e3ffab
-    { SHA512,  1234567890, 93441116 },
e3ffab
-    { SHA1,    2000000000, 69279037 },
e3ffab
-    { SHA256,  2000000000, 90698825 },
e3ffab
-    { SHA512,  2000000000, 38618901 },
e3ffab
-#ifdef _LP64 /* Only do these tests on 64-bit systems. */
e3ffab
-    { SHA1,   20000000000, 65353130 },
e3ffab
-    { SHA256, 20000000000, 77737706 },
e3ffab
-    { SHA512, 20000000000, 47863826 },
e3ffab
-#endif
e3ffab
-};
e3ffab
-
e3ffab
-int
e3ffab
-main(int argc, const char *argv[])
e3ffab
-{
e3ffab
-    uint32_t otp;
e3ffab
-    int i;
e3ffab
-
e3ffab
-    NSS_NoDB_Init(".");
e3ffab
-
e3ffab
-    for (i = 0; i < sizeof(hotp_answers) / sizeof(*hotp_answers); i++) {
e3ffab
-        assert(hotp(&hotp_token, i, &otp));
e3ffab
-        assert(otp == hotp_answers[i]);
e3ffab
-    }
e3ffab
-
e3ffab
-    for (i = 0; i < sizeof(totp_tests) / sizeof(*totp_tests); i++) {
e3ffab
-        assert(hotp(&totp_tests[i].token, totp_tests[i].time / 30, &otp));
e3ffab
-        assert(otp == totp_tests[i].answer);
e3ffab
-    }
e3ffab
-
e3ffab
-    NSS_Shutdown();
e3ffab
-    return 0;
e3ffab
-}
e3ffab
-- 
e3ffab
2.1.0
e3ffab