Attached.
>From 8b0fa10fcbf87097bcafaba0c19749856b66bbf4 Mon Sep 17 00:00:00 2001
From: Nathaniel McCallum <npmccal...@redhat.com>
Date: Tue, 7 Jan 2014 12:42:29 -0500
Subject: [PATCH] Migrate ipa-pwd-extop to use libotp

---
 .../ipa-slapi-plugins/ipa-pwd-extop/Makefile.am    |  12 +-
 daemons/ipa-slapi-plugins/ipa-pwd-extop/auth.c     | 398 ---------------------
 daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h   |   7 +-
 daemons/ipa-slapi-plugins/ipa-pwd-extop/otp.c      | 180 ----------
 daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c  | 217 +++++------
 daemons/ipa-slapi-plugins/ipa-pwd-extop/t_hotp.c   |  82 -----
 daemons/ipa-slapi-plugins/ipa-pwd-extop/t_totp.c   | 103 ------
 7 files changed, 118 insertions(+), 881 deletions(-)
 delete mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/auth.c
 delete mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/otp.c
 delete mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/t_hotp.c
 delete mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/t_totp.c

diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am b/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
index 8bd89653de51ab33e295fc6b1f1d6d93576d3c64..98987446a56aab96d604ecc37e0a686d765b7f6c 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/Makefile.am
@@ -10,6 +10,7 @@ KRB5_UTIL_SRCS = $(KRB5_UTIL_DIR)/ipa_krb5.c \
 AM_CPPFLAGS =							\
 	-I.							\
 	-I$(srcdir)						\
+	-I$(srcdir)/../libotp					\
 	-I$(PLUGIN_COMMON_DIR)					\
 	-I$(KRB5_UTIL_DIR)					\
 	-I$(COMMON_BER_DIR)					\
@@ -33,20 +34,11 @@ AM_LDFLAGS = \
 	-avoid-version	\
 	-export-symbols-regex ^ipapwd_init$
 
-# OTP Convenience Library and Tests
-noinst_LTLIBRARIES = libotp.la
-libotp_la_SOURCES = otp.c
-check_PROGRAMS = t_hotp t_totp
-t_hotp_LDADD = libotp.la
-t_totp_LDADD = libotp.la
-TESTS = $(check_PROGRAMS)
-
 # Plugin Binary
 plugindir = $(libdir)/dirsrv/plugins
 plugin_LTLIBRARIES = libipa_pwd_extop.la
-libipa_pwd_extop_la_LIBADD  = libotp.la
+libipa_pwd_extop_la_LIBADD  = $(builddir)/../libotp/libotp.la
 libipa_pwd_extop_la_SOURCES = 		\
-	auth.c				\
 	common.c			\
 	encoding.c			\
 	prepost.c			\
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/auth.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/auth.c
deleted file mode 100644
index cccddeb91d84b45aa23d13792725ff20d7af2c7f..0000000000000000000000000000000000000000
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/auth.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/** BEGIN COPYRIGHT BLOCK
- * This Program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation; version 3 of the License.
- *
- * This Program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * In addition, as a special exception, Red Hat, Inc. gives You the additional
- * right to link the code of this Program with code not covered under the GNU
- * General Public License ("Non-GPL Code") and to distribute linked combinations
- * including the two, subject to the limitations in this paragraph. Non-GPL Code
- * permitted under this exception must only link to the code of this Program
- * through those well defined interfaces identified in the file named EXCEPTION
- * found in the source code files (the "Approved Interfaces"). The files of
- * Non-GPL Code may instantiate templates or use macros or inline functions from
- * the Approved Interfaces without causing the resulting work to be covered by
- * the GNU General Public License. Only Red Hat, Inc. may make changes or
- * additions to the list of Approved Interfaces. You must obey the GNU General
- * Public License in all respects for all of the Program code and other code used
- * in conjunction with the Program except the Non-GPL Code covered by this
- * exception. If you modify this file, you may extend this exception to your
- * version of the file, but you are not obligated to do so. If you do not wish to
- * provide this exception without modification, you must delete this exception
- * statement from your version and license this file solely under the GPL without
- * exception.
- *
- *
- * Copyright (C) 2013 Red Hat, Inc.
- * All rights reserved.
- * END COPYRIGHT BLOCK **/
-
-#include "ipapwd.h"
-
-#define IPA_OTP_TOKEN_TOTP_OC "ipaTokenTOTP"
-#define IPA_OTP_DEFAULT_TOKEN_ALGORITHM "sha1"
-#define IPA_OTP_DEFAULT_TOKEN_OFFSET 0
-#define IPA_OTP_DEFAULT_TOKEN_STEP 30
-
-/*
- * From otp.c
- */
-bool ipapwd_hotp(const uint8_t *key, size_t len, const char *algo, int digits,
-                 uint64_t counter, uint32_t *out);
-
-bool ipapwd_totp(const uint8_t *key, size_t len, const char *algo, int digits,
-                 time_t time, int offset, unsigned int step, uint32_t *out);
-
-/* From ipa_pwd_extop.c */
-extern void *ipapwd_plugin_id;
-
-/* Data types. */
-struct token {
-    struct {
-        uint8_t *data;
-        size_t len;
-    } key;
-    char *algo;
-    int len;
-    union {
-        struct {
-            uint64_t counter;
-        } hotp;
-        struct {
-            unsigned int step;
-            int offset;
-        } totp;
-    };
-    bool (*auth)(const struct token *token, uint32_t otp);
-};
-
-struct credentials {
-    struct token token;
-    Slapi_Value *ltp;
-    uint32_t otp;
-};
-
-static const char *valid_algos[] = { "sha1", "sha256", "sha384",
-                                     "sha512", NULL };
-
-static inline bool is_algo_valid(const char *algo)
-{
-    int i, ret;
-
-    for (i = 0; valid_algos[i]; i++) {
-        ret = strcasecmp(algo, valid_algos[i]);
-        if (ret == 0)
-            return true;
-    }
-
-    return false;
-}
-
-static const struct berval *entry_attr_get_berval(const Slapi_Entry* e,
-                                                  const char *type)
-{
-    Slapi_Attr* attr = NULL;
-    Slapi_Value *v;
-    int ret;
-
-    ret = slapi_entry_attr_find(e, type, &attr);
-    if (ret != 0 || attr == NULL)
-        return NULL;
-
-    ret = slapi_attr_first_value(attr, &v);
-    if (ret < 0)
-        return NULL;
-
-    return slapi_value_get_berval(v);
-}
-
-/* Authenticate a totp token. Return zero on success. */
-static bool auth_totp(const struct token *token, uint32_t otp)
-{
-    time_t times[5];
-    uint32_t val;
-    int i;
-
-    /* Get the token value for now and two steps in either direction. */
-    times[0] = time(NULL);
-    times[1] = times[0] + token->totp.step * 1;
-    times[2] = times[0] - token->totp.step * 1;
-    times[3] = times[0] + token->totp.step * 2;
-    times[4] = times[0] - token->totp.step * 2;
-    if (times[0] == -1)
-        return false;
-
-    /* Check all the times for a match. */
-    for (i = 0; i < sizeof(times) / sizeof(times[0]); i++) {
-        if (!ipapwd_totp(token->key.data, token->key.len, token->algo,
-                         token->len, times[i], token->totp.offset,
-                         token->totp.step, &val)) {
-            return false;
-        }
-
-        if (val == otp) {
-            return true;
-        }
-    }
-
-    return false;
-}
-
-static void token_free_contents(struct token *token)
-{
-    if (token == NULL)
-        return;
-
-    slapi_ch_free_string(&token->algo);
-    slapi_ch_free((void **) &token->key.data);
-}
-
-/* Decode an OTP token entry. Return zero on success. */
-static bool token_decode(Slapi_Entry *te, struct token *token)
-{
-    const struct berval *tmp;
-
-    /* Get key. */
-    tmp = entry_attr_get_berval(te, IPA_OTP_TOKEN_KEY_TYPE);
-    if (tmp == NULL) {
-        slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
-                        "token_decode: key not set for token \"%s\".\n",
-                        slapi_entry_get_ndn(te));
-        return false;
-    }
-    token->key.len = tmp->bv_len;
-    token->key.data = (void *) slapi_ch_malloc(token->key.len);
-    memcpy(token->key.data, tmp->bv_val, token->key.len);
-
-    /* Get length. */
-    token->len = slapi_entry_attr_get_int(te, IPA_OTP_TOKEN_LENGTH_TYPE);
-    if (token->len < 6 || token->len > 10) {
-        slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
-                        "token_decode: %s is not defined or invalid "
-                        "for token \"%s\".\n", IPA_OTP_TOKEN_LENGTH_TYPE,
-                        slapi_entry_get_ndn(te));
-        token_free_contents(token);
-        return false;
-    }
-
-    /* Get algorithm. */
-    token->algo = slapi_entry_attr_get_charptr(te,
-                                               IPA_OTP_TOKEN_ALGORITHM_TYPE);
-    if (token->algo == NULL)
-        token->algo = slapi_ch_strdup(IPA_OTP_DEFAULT_TOKEN_ALGORITHM);
-    if (!is_algo_valid(token->algo)) {
-        slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
-                        "token_decode: invalid token algorithm "
-                        "specified for token \"%s\".\n",
-                        slapi_entry_get_ndn(te));
-        token_free_contents(token);
-        return false;
-    }
-
-    /* Currently, we only support TOTP. */
-    token->auth = auth_totp;
-
-    /* Get offset. */
-    token->totp.offset = slapi_entry_attr_get_int(te,
-                                                  IPA_OTP_TOKEN_OFFSET_TYPE);
-    if (token->totp.offset == 0)
-        token->totp.offset = IPA_OTP_DEFAULT_TOKEN_OFFSET;
-
-    /* Get step. */
-    token->totp.step = slapi_entry_attr_get_uint(te, IPA_OTP_TOKEN_STEP_TYPE);
-    if (token->totp.step == 0)
-        token->totp.step = IPA_OTP_DEFAULT_TOKEN_STEP;
-
-    return true;
-}
-
-static void credentials_free_contents(struct credentials *credentials)
-{
-    if (!credentials)
-        return;
-
-    token_free_contents(&credentials->token);
-    slapi_value_free(&credentials->ltp);
-}
-
-/* Parse credentials and token entry. Return zero on success. */
-static bool credentials_parse(Slapi_Entry *te, struct berval *creds,
-                              struct credentials *credentials)
-{
-    char *tmp;
-    int len;
-
-    if (!token_decode(te, &credentials->token))
-        return false;
-
-    /* Is the credential too short?  If so, error. */
-    if (credentials->token.len >= creds->bv_len) {
-        slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
-                        "credentials_parse: supplied credential is less "
-                        "than or equal to %s for token \"%s\".\n",
-                        IPA_OTP_TOKEN_LENGTH_TYPE, slapi_entry_get_ndn(te));
-        token_free_contents(&credentials->token);
-        return false;
-    }
-
-    /* Extract the password from the supplied credential.  We hand the
-     * memory off to a Slapi_Value, so we don't want to directly free the
-     * string. */
-    len = creds->bv_len - credentials->token.len;
-    tmp = slapi_ch_calloc(len + 1, sizeof(char));
-    strncpy(tmp, creds->bv_val, len);
-    credentials->ltp = slapi_value_new_string_passin(tmp);
-
-    /* Extract the token value as a (minimum) 32-bit unsigned integer. */
-    tmp = slapi_ch_calloc(credentials->token.len + 1, sizeof(char));
-    strncpy(tmp, creds->bv_val + len, credentials->token.len);
-    credentials->otp = strtoul(tmp, NULL, 10);
-    slapi_ch_free_string(&tmp);
-
-    return true;
-}
-
-/*
- * Attempts to perform OTP authentication for the passed in bind entry using
- * the passed in credentials.
- */
-bool ipapwd_do_otp_auth(Slapi_Entry *bind_entry, struct berval *creds)
-{
-    Slapi_PBlock *search_pb = NULL;
-    Slapi_Value **pwd_vals = NULL;
-    Slapi_Attr *pwd_attr = NULL;
-    Slapi_Entry **tokens = NULL;
-    Slapi_DN *base_sdn = NULL;
-    Slapi_Backend *be = NULL;
-    char *user_dn = NULL;
-    char *filter = NULL;
-    int pwd_numvals = 0;
-    bool ret = false;
-    int result = 0;
-    int hint = 0;
-    int i = 0;
-
-    search_pb = slapi_pblock_new();
-
-    /* Fetch the user DN. */
-    user_dn = slapi_entry_get_ndn(bind_entry);
-    if (user_dn == NULL) {
-        slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME,
-                        "ipapwd_do_otp_auth: error retrieving bind DN.\n");
-        goto done;
-    }
-
-    /* Search for TOTP tokens associated with this user.  We search for
-     * tokens who list this user as the owner in the same backend where
-     * the user entry is located. */
-    filter = slapi_ch_smprintf("(&(%s=%s)(%s=%s))", SLAPI_ATTR_OBJECTCLASS,
-                               IPA_OTP_TOKEN_TOTP_OC, IPA_OTP_TOKEN_OWNER_TYPE,
-                               user_dn);
-
-    be = slapi_be_select(slapi_entry_get_sdn(bind_entry));
-    if (be != NULL) {
-        base_sdn = (Slapi_DN *) slapi_be_getsuffix(be, 0);
-    }
-    if (base_sdn == NULL) {
-        slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME,
-                        "ipapwd_do_otp_auth: error determining the search "
-                        "base for user \"%s\".\n",
-                        user_dn);
-    }
-
-    slapi_search_internal_set_pb(search_pb, slapi_sdn_get_ndn(base_sdn),
-                                 LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL,
-                                 NULL, ipapwd_plugin_id, 0);
-
-    slapi_search_internal_pb(search_pb);
-    slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
-
-    if (LDAP_SUCCESS != result) {
-        slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME,
-                        "ipapwd_do_otp_auth: error searching for tokens "
-                        "associated with user \"%s\" (err=%d).\n",
-                        user_dn, result);
-        goto done;
-    }
-
-    slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &tokens);
-
-    if (tokens == NULL) {
-        /* This user has no associated tokens, so just bail out. */
-        goto done;
-    }
-
-    /* Fetch the userPassword values so we can perform the password checks
-     * when processing tokens below. */
-    if (slapi_entry_attr_find(bind_entry, SLAPI_USERPWD_ATTR, &pwd_attr) != 0 ||
-        slapi_attr_get_numvalues(pwd_attr, &pwd_numvals) != 0) {
-        slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
-                        "ipapwd_do_otp_auth: no passwords are set for user "
-                        "\"%s\".\n", user_dn);
-        goto done;
-    }
-
-    /* We need to create a Slapi_Value  array of the present password values
-     * for the compare function.  There's no nicer way of doing this. */
-    pwd_vals = (Slapi_Value **) slapi_ch_calloc(pwd_numvals,
-                                                sizeof(Slapi_Value *));
-
-    for (hint = slapi_attr_first_value(pwd_attr, &pwd_vals[i]); hint != -1;
-         hint = slapi_attr_next_value(pwd_attr, hint, &pwd_vals[i])) {
-        ++i;
-    }
-
-    /* Loop through each token and attempt to authenticate. */
-    for (i = 0; tokens && tokens[i]; i++) {
-        struct credentials credentials = {};
-
-        /* Parse the token entry and the credentials. */
-        if (!credentials_parse(tokens[i], creds, &credentials))
-            continue;
-
-        /* Check if the password portion of the credential is correct. */
-        i = slapi_pw_find_sv(pwd_vals, credentials.ltp);
-        if (i != 0) {
-            slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
-                            "ipapwd_do_otp_auth: password check failed when "
-                            "processing token \"%s\" for user \"%s\".\n",
-                            slapi_entry_get_ndn(tokens[i]), user_dn);
-            credentials_free_contents(&credentials);
-            continue;
-        }
-
-        /* Attempt to perform OTP authentication for this token. */
-        if (!credentials.token.auth(&credentials.token, credentials.otp)) {
-            slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
-                            "ipapwd_do_otp_auth: OTP auth failed when "
-                            "processing token \"%s\" for user \"%s\".\n",
-                            slapi_entry_get_ndn(tokens[i]), user_dn);
-            credentials_free_contents(&credentials);
-            continue;
-        }
-
-        /* Authentication successful! */
-        credentials_free_contents(&credentials);
-        slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
-                        "ipapwd_do_otp_auth: successfully "
-                        "authenticated user \"%s\" using token "
-                        "\"%s\".\n",
-                        user_dn, slapi_entry_get_ndn(tokens[i]));
-        ret = true;
-        break;
-    }
-
-done:
-    slapi_ch_free_string(&filter);
-    slapi_free_search_results_internal(search_pb);
-    slapi_pblock_destroy(search_pb);
-    return ret;
-}
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
index b4087488ccf2081aeed9aa02d1a4f0d37f4cf7f4..1ef3627e0bcc109db8d9150cf21de91aabb5eee8 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
@@ -41,17 +41,17 @@
 #  include <config.h>
 #endif
 
+#include <libotp.h>
+
 #include <stdio.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <stdbool.h>
 
 #include <prio.h>
 #include <ssl.h>
-#include <dirsrv/slapi-plugin.h>
 #include <krb5.h>
 #include <kdb.h>
 #include <lber.h>
@@ -187,6 +187,3 @@ void *ipapwd_get_plugin_id(void);
 Slapi_DN *ipapwd_get_otp_config_area(void);
 Slapi_DN *ipapwd_get_plugin_sdn(void);
 bool ipapwd_get_plugin_started(void);
-
-/* from auth.c */
-bool ipapwd_do_otp_auth(Slapi_Entry *bind_entry, struct berval *creds);
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/otp.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/otp.c
deleted file mode 100644
index 6c0f8554b5ee3afd8e78333b30f56272048d8c4d..0000000000000000000000000000000000000000
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/otp.c
+++ /dev/null
@@ -1,180 +0,0 @@
-/** BEGIN COPYRIGHT BLOCK
- * This Program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation; version 3 of the License.
- *
- * This Program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * In addition, as a special exception, Red Hat, Inc. gives You the additional
- * right to link the code of this Program with code not covered under the GNU
- * General Public License ("Non-GPL Code") and to distribute linked combinations
- * including the two, subject to the limitations in this paragraph. Non-GPL Code
- * permitted under this exception must only link to the code of this Program
- * through those well defined interfaces identified in the file named EXCEPTION
- * found in the source code files (the "Approved Interfaces"). The files of
- * Non-GPL Code may instantiate templates or use macros or inline functions from
- * the Approved Interfaces without causing the resulting work to be covered by
- * the GNU General Public License. Only Red Hat, Inc. may make changes or
- * additions to the list of Approved Interfaces. You must obey the GNU General
- * Public License in all respects for all of the Program code and other code used
- * in conjunction with the Program except the Non-GPL Code covered by this
- * exception. If you modify this file, you may extend this exception to your
- * version of the file, but you are not obligated to do so. If you do not wish to
- * provide this exception without modification, you must delete this exception
- * statement from your version and license this file solely under the GPL without
- * exception.
- *
- *
- * Copyright (C) 2013 Red Hat, Inc.
- * All rights reserved.
- * END COPYRIGHT BLOCK **/
-
-/*
- * This file contains an implementation of HOTP (RFC 4226) and TOTP (RFC 6238).
- * For details of how these algorithms work, please see the relevant RFCs.
- */
-
-#include <stdbool.h>
-#include <time.h>
-
-#include <nss.h>
-#include <pk11pub.h>
-#include <hasht.h>
-#include <prnetdb.h>
-
-struct digest_buffer {
-    uint8_t buf[SHA512_LENGTH];
-    unsigned int len;
-};
-
-static const struct {
-    const char *algo;
-    CK_MECHANISM_TYPE mech;
-} algo2mech[] = {
-    { "sha1",   CKM_SHA_1_HMAC },
-    { "sha256", CKM_SHA256_HMAC },
-    { "sha384", CKM_SHA384_HMAC },
-    { "sha512", CKM_SHA512_HMAC },
-    { }
-};
-
-/*
- * This code is mostly cargo-cult taken from here:
- *   http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn5.html
- *
- * It should implement HMAC with the given mechanism (SHA: 1, 256, 384, 512).
- */
-static bool hmac(SECItem *key, CK_MECHANISM_TYPE mech, const SECItem *in,
-                 struct digest_buffer *out)
-{
-    SECItem param = { siBuffer, NULL, 0 };
-    PK11SlotInfo *slot = NULL;
-    PK11SymKey *symkey = NULL;
-    PK11Context *ctx = NULL;
-    bool ret = false;
-    SECStatus s;
-
-    slot = PK11_GetBestSlot(mech, NULL);
-    if (slot == NULL) {
-        slot = PK11_GetInternalKeySlot();
-        if (slot == NULL) {
-            goto done;
-        }
-    }
-
-    symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap,
-                               CKA_SIGN, key, NULL);
-    if (symkey == NULL)
-        goto done;
-
-    ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN, symkey, &param);
-    if (ctx == NULL)
-        goto done;
-
-    s = PK11_DigestBegin(ctx);
-    if (s != SECSuccess)
-        goto done;
-
-    s = PK11_DigestOp(ctx, in->data, in->len);
-    if (s != SECSuccess)
-        goto done;
-
-    s = PK11_DigestFinal(ctx, out->buf, &out->len, sizeof(out->buf));
-    if (s != SECSuccess)
-        goto done;
-
-    ret = true;
-
-done:
-    if (ctx != NULL)
-        PK11_DestroyContext(ctx, PR_TRUE);
-    if (symkey != NULL)
-        PK11_FreeSymKey(symkey);
-    if (slot != NULL)
-        PK11_FreeSlot(slot);
-    return ret;
-}
-
-/*
- * An implementation of HOTP (RFC 4226).
- */
-bool ipapwd_hotp(const uint8_t *key, size_t len, const char *algo, int digits,
-                 uint64_t counter, uint32_t *out)
-{
-    const SECItem cntr = { siBuffer, (uint8_t *) &counter, sizeof(counter) };
-    SECItem keyitm = { siBuffer, (uint8_t *) key, len };
-    CK_MECHANISM_TYPE mech = CKM_SHA_1_HMAC;
-    PRUint64 offset, binary, div;
-    struct digest_buffer digest;
-    int i;
-
-    /* Convert counter to network byte order. */
-    counter = PR_htonll(counter);
-
-    /* Find the mech. */
-    for (i = 0; algo2mech[i].algo; i++) {
-        if (strcasecmp(algo2mech[i].algo, algo) == 0) {
-            mech = algo2mech[i].mech;
-            break;
-        }
-    }
-
-    /* Create the digits divisor. */
-    for (div = 1; digits > 0; digits--) {
-        div *= 10;
-    }
-
-    /* Do the digest. */
-    if (!hmac(&keyitm, mech, &cntr, &digest)) {
-        return false;
-    }
-
-    /* Truncate. */
-    offset  = digest.buf[digest.len - 1] & 0xf;
-    binary  = (digest.buf[offset + 0] & 0x7f) << 0x18;
-    binary |= (digest.buf[offset + 1] & 0xff) << 0x10;
-    binary |= (digest.buf[offset + 2] & 0xff) << 0x08;
-    binary |= (digest.buf[offset + 3] & 0xff) << 0x00;
-    binary  = binary % div;
-
-    *out = binary;
-    return true;
-}
-
-/*
- * An implementation of TOTP (RFC 6238).
- */
-bool ipapwd_totp(const uint8_t *key, size_t len, const char *algo, int digits,
-                 time_t time, int offset, unsigned int step, uint32_t *out)
-{
-    if (step == 0)
-        return false;
-
-    return ipapwd_hotp(key, len, algo, digits, (time - offset) / step, out);
-}
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
index ef37b5e173359f9404b241c12d8a6dc6e77da128..ab9f00aaff44208cf24f65acefeba2eda1fb65d9 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
@@ -67,8 +67,7 @@
 #define IPAPWD_OP_ADD 1
 #define IPAPWD_OP_MOD 2
 
-#define IPAPWD_OP_NOT_HANDLED 0
-#define IPAPWD_OP_HANDLED     1
+#define OTP_VALIDATE_STEPS 3
 
 extern Slapi_PluginDesc ipapwd_plugin_desc;
 extern void *ipapwd_plugin_id;
@@ -1241,50 +1240,104 @@ done:
     return ret;
 }
 
-/* Handle OTP authentication. */
-static int ipapwd_pre_bind_otp(Slapi_PBlock * pb)
+/*
+ * Authenticates creds against OTP tokens. Returns true when authentication
+ * completed successfully against a token OR when a user has no active tokens.
+ *
+ * WARNING: This function DOES NOT authenticate the first factor. Only the OTP
+ *          code is validated! You still need to validate the first factor.
+ *
+ * NOTE: When successful, this function truncates creds to remove the token
+ *       value at the end. This leaves only the password in creds for later
+ *       validation.
+ */
+static bool ipapwd_do_otp_auth(Slapi_Entry *bind_entry, struct berval *creds)
+{
+    struct otptoken **tokens = NULL;
+    Slapi_DN *user_dn = NULL;
+    bool success = false;
+
+    /* Fetch the user DN. */
+    user_dn = slapi_entry_get_sdn(bind_entry);
+    if (user_dn == NULL) {
+        slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME,
+                        "%s: error retrieving bind DN.\n", __func__);
+        return false;
+    }
+
+    /* Find all of the user's active tokens. */
+    tokens = otptoken_find(ipapwd_plugin_id, user_dn, NULL, true, NULL);
+    if (tokens == NULL) {
+        slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME,
+                        "%s: can't find tokens for '%s'.\n", __func__,
+                        slapi_sdn_get_dn(user_dn));
+        return false;
+    }
+
+    /* If the user has no active tokens, succeed. */
+    success = tokens[0] == NULL;
+
+    /* Loop through each token. */
+    for (int i = 0; tokens[i] && !success; i++) {
+        /* Attempt authentication. */
+        success = otptoken_validate_string(tokens[i], OTP_VALIDATE_STEPS,
+                                           creds->bv_val, creds->bv_len, true);
+
+        /* Truncate the password to remove the OTP code at the end. */
+        if (success) {
+            creds->bv_len -= otptoken_get_digits(tokens[i]);
+            creds->bv_val[creds->bv_len] = '\0';
+        }
+
+        slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME,
+                        "%s: token authentication %s "
+                        "(user: '%s', token: '%s\').\n", __func__,
+                        success ? "succeeded" : "failed",
+                        slapi_sdn_get_dn(user_dn),
+                        slapi_sdn_get_ndn(otptoken_get_sdn(tokens[i])));
+    }
+
+    otptoken_free_array(tokens);
+    return success;
+}
+
+/*
+ * This function handles the bind functionality for OTP. The return value
+ * indicates if a response was sent to the client.
+ *
+ * NOTE: This function may modify creds. See explanation in the comment for
+ *       ipapwd_do_otp_auth() above.
+ */
+static bool ipapwd_pre_bind_otp(Slapi_PBlock * pb, const char *bind_dn,
+                                struct berval *creds)
 {
     char *user_attrs[] = { IPA_USER_AUTH_TYPE, NULL };
-    int ret = IPAPWD_OP_NOT_HANDLED;
     Slapi_Entry *bind_entry = NULL;
-    struct berval *creds = NULL;
-    const char *bind_dn = NULL;
     Slapi_DN *bind_sdn = NULL;
     int result = LDAP_SUCCESS;
     char **auth_types = NULL;
-    int method;
-    int i;
+    bool otpauth;
+    bool pwdauth;
 
     /* If we didn't start successfully, bail. */
-    if (!ipapwd_get_plugin_started()) {
-        goto done;
-    }
+    if (!ipapwd_get_plugin_started())
+        return true;
 
     /* If global disabled flag is set, just punt. */
-    if (ipapwd_otp_is_disabled()) {
-        goto done;
-    }
-
-    /* Retrieve parameters for bind operation. */
-    i = slapi_pblock_get(pb, SLAPI_BIND_METHOD, &method);
-    if (i == 0) {
-        i = slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &bind_sdn);
-        if (i == 0) {
-            i = slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &creds);
-        }
-    }
-    if (i != 0) {
-        LOG_FATAL("Not handled (can't retrieve bind parameters)\n");
-        goto done;
-    }
-
-    bind_dn = slapi_sdn_get_dn(bind_sdn);
+    if (ipapwd_otp_is_disabled())
+        return true;
 
     /* We only handle non-anonymous simple binds.  We just pass everything
      * else through to the server. */
-    if (method != LDAP_AUTH_SIMPLE || *bind_dn == '\0' || creds->bv_len == 0) {
+    if (*bind_dn == '\0' || creds->bv_len == 0) {
         LOG_TRACE("Not handled (not simple bind or NULL dn/credentials)\n");
-        goto done;
+        return true;
+    }
+
+    /* Retrieve parameters for bind operation. */
+    if (slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &bind_sdn) != 0) {
+        LOG_FATAL("Not handled (can't retrieve bind parameters)\n");
+        return true;
     }
 
     /* Check if any allowed authentication types are set in the user entry.
@@ -1294,83 +1347,43 @@ static int ipapwd_pre_bind_otp(Slapi_PBlock * pb)
     if (result != LDAP_SUCCESS) {
         LOG_FATAL("Not handled (could not search for BIND dn %s - error "
                   "%d : %s)\n", bind_dn, result, ldap_err2string(result));
-        goto done;
+        return true;
     }
     if (bind_entry == NULL) {
         LOG_FATAL("Not handled (could not find entry for BIND dn %s)\n", bind_dn);
-        goto done;
+        return true;
     }
 
-    i = slapi_check_account_lock(pb, bind_entry, 0, 0, 0);
-    if (i == 1) {
-        LOG_TRACE("Not handled (account %s inactivated.)\n", bind_dn);
-        goto done;
-    }
-
-    auth_types = slapi_entry_attr_get_charray(bind_entry, IPA_USER_AUTH_TYPE);
-
     /*
      * IMPORTANT SECTION!
      *
      * This section handles authentication logic, so be careful!
      *
      * The basic idea of this section is:
-     * 1. If OTP is enabled, try to use it first. If successful, send response.
-     * 2. If OTP was not enabled/successful, check if password is enabled.
-     * 3. If password is not enabled, send failure response.
-     * 4. Otherwise, fall through to standard server password authentication.
-     *
+     * 1. If OTP is enabled, validate OTP. If successful, force PWD validation.
+     * 2. If PWD is enabled or forced, fall through to password validation.
+     * 4. Otherwise, send a failure response to the client.
      */
-
-    /* If OTP is allowed, attempt to do OTP authentication. */
-    if (ipapwd_is_auth_type_allowed(auth_types, IPA_OTP_AUTH_TYPE_OTP)) {
-        LOG_PLUGIN_NAME(IPAPWD_PLUGIN_NAME,
-                        "Attempting OTP authentication for '%s'.\n", bind_dn);
-        if (ipapwd_do_otp_auth(bind_entry, creds)) {
-            /* FIXME - NGK - If the auth type request control was sent,
-             * construct the response control to indicate what auth type was
-             * used.  We might be able to do this in the
-             * SLAPI_PLUGIN_PRE_RESULT_FN callback instead of here. */
-
-            /* FIXME - NGK - What about other controls, like the pwpolicy
-             * control? If any other critical controls are set, we need to
-             * either process them properly or reject the operation with an
-             * unsupported critical control error. */
-
-            /* Send response approving authentication. */
-            slapi_send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
-            ret = IPAPWD_OP_HANDLED;
-        }
-    }
-
-    /* If OTP failed or was not enabled, we need to figure out if we can fall
-     * back to standard password authentication or give an error. */
-    if (ret != IPAPWD_OP_HANDLED) {
-        if (!ipapwd_is_auth_type_allowed(auth_types,
-                                         IPA_OTP_AUTH_TYPE_PASSWORD)) {
-            /* Password authentication is disabled, so we have failed. */
-            slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS,
-                                   NULL, NULL, 0, NULL);
-            ret = IPAPWD_OP_HANDLED;
-            goto done;
-        }
-
-        /* Password authentication is permitted, so tell the server that we
-         * didn't handle this request. Then the server will perform standard
-         * password authentication. */
-        LOG_PLUGIN_NAME(IPAPWD_PLUGIN_NAME,
-                        "Attempting PASSWORD authentication for \"%s\".\n",
-                        bind_dn);
-
-        /* FIXME - NGK - Do we need to figure out how to build
-         * the reponse control in this case?  Maybe we can use a
-         * SLAPI_PLUGIN_PRE_RESULT_FN callback to handle that? */
-    }
-
-done:
+    auth_types = slapi_entry_attr_get_charray(bind_entry, IPA_USER_AUTH_TYPE);
+    otpauth = ipapwd_is_auth_type_allowed(auth_types, IPA_OTP_AUTH_TYPE_OTP);
+    pwdauth = ipapwd_is_auth_type_allowed(auth_types, IPA_OTP_AUTH_TYPE_PASSWORD);
     slapi_ch_array_free(auth_types);
+
+    if (otpauth) {
+        LOG_PLUGIN_NAME(IPAPWD_PLUGIN_NAME,
+                        "Attempting OTP authentication for '%s'.\n", bind_dn);
+        if (ipapwd_do_otp_auth(bind_entry, creds))
+            pwdauth = true;
+    }
     slapi_entry_free(bind_entry);
-    return ret;
+
+    /* We have failed if:
+     *   1. Password authentication is disabled.
+     *   2. OTP authentication either failed or is disabled.
+     */
+    if (!pwdauth)
+        slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL);
+    return pwdauth;
 }
 
 /* PRE BIND Operation:
@@ -1399,24 +1412,22 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
 
     LOG_TRACE("=>\n");
 
-    /* Try to do OTP first. */
-    ret = ipapwd_pre_bind_otp(pb);
-    if (ret == IPAPWD_OP_HANDLED) {
-        return ret;
-    }
-
     /* get BIND parameters */
     ret |= slapi_pblock_get(pb, SLAPI_BIND_TARGET, &dn);
     ret |= slapi_pblock_get(pb, SLAPI_BIND_METHOD, &method);
     ret |= slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &credentials);
     if (ret) {
         LOG_FATAL("slapi_pblock_get failed!?\n");
-        goto done;
+        return 0;
     }
 
     /* we're only interested in simple authentication */
     if (method != LDAP_AUTH_SIMPLE)
-        goto done;
+        return 0;
+
+    /* Try to do OTP first. */
+    if (!ipapwd_pre_bind_otp(pb, dn, credentials))
+        return 1;
 
     /* list of attributes to retrieve */
     const char *attrs_list[] = {SLAPI_USERPWD_ATTR, "krbprincipalkey", "uid",
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/t_hotp.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/t_hotp.c
deleted file mode 100644
index d57f9ab68bebb2c77f3bc327c50bdd6eb480f67e..0000000000000000000000000000000000000000
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/t_hotp.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/** BEGIN COPYRIGHT BLOCK
- * This Program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation; version 3 of the License.
- *
- * This Program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * In addition, as a special exception, Red Hat, Inc. gives You the additional
- * right to link the code of this Program with code not covered under the GNU
- * General Public License ("Non-GPL Code") and to distribute linked combinations
- * including the two, subject to the limitations in this paragraph. Non-GPL Code
- * permitted under this exception must only link to the code of this Program
- * through those well defined interfaces identified in the file named EXCEPTION
- * found in the source code files (the "Approved Interfaces"). The files of
- * Non-GPL Code may instantiate templates or use macros or inline functions from
- * the Approved Interfaces without causing the resulting work to be covered by
- * the GNU General Public License. Only Red Hat, Inc. may make changes or
- * additions to the list of Approved Interfaces. You must obey the GNU General
- * Public License in all respects for all of the Program code and other code used
- * in conjunction with the Program except the Non-GPL Code covered by this
- * exception. If you modify this file, you may extend this exception to your
- * version of the file, but you are not obligated to do so. If you do not wish to
- * provide this exception without modification, you must delete this exception
- * statement from your version and license this file solely under the GPL without
- * exception.
- *
- *
- * Copyright (C) 2013 Red Hat, Inc.
- * All rights reserved.
- * END COPYRIGHT BLOCK **/
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stddef.h>
-#include <time.h>
-#include <string.h>
-#include <nss.h>
-
-/*
- * From otp.c
- */
-bool ipapwd_hotp(const uint8_t *key, size_t len, const char *algo, int digits,
-                 uint64_t counter, uint32_t *out);
-
-/* All HOTP test examples from RFC 4226 (Appendix D). */
-static const uint8_t *key = (uint8_t *) "12345678901234567890";
-static const uint32_t answers[] = {
-    755224,
-    287082,
-    359152,
-    969429,
-    338314,
-    254676,
-    287922,
-    162583,
-    399871,
-    520489
-};
-
-int
-main(int argc, const char *argv[])
-{
-    uint32_t otp;
-    int i;
-
-    NSS_NoDB_Init(".");
-
-    for (i = 0; i < sizeof(answers) / sizeof(*answers); i++) {
-        assert(ipapwd_hotp(key, 20, "sha1", 6, i, &otp));
-        assert(otp == answers[i]);
-    }
-
-    NSS_Shutdown();
-    return 0;
-}
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/t_totp.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/t_totp.c
deleted file mode 100644
index 2df8d245818f90277ece273a8f0591538a4707a6..0000000000000000000000000000000000000000
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/t_totp.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/** BEGIN COPYRIGHT BLOCK
- * This Program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation; version 3 of the License.
- *
- * This Program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * In addition, as a special exception, Red Hat, Inc. gives You the additional
- * right to link the code of this Program with code not covered under the GNU
- * General Public License ("Non-GPL Code") and to distribute linked combinations
- * including the two, subject to the limitations in this paragraph. Non-GPL Code
- * permitted under this exception must only link to the code of this Program
- * through those well defined interfaces identified in the file named EXCEPTION
- * found in the source code files (the "Approved Interfaces"). The files of
- * Non-GPL Code may instantiate templates or use macros or inline functions from
- * the Approved Interfaces without causing the resulting work to be covered by
- * the GNU General Public License. Only Red Hat, Inc. may make changes or
- * additions to the list of Approved Interfaces. You must obey the GNU General
- * Public License in all respects for all of the Program code and other code used
- * in conjunction with the Program except the Non-GPL Code covered by this
- * exception. If you modify this file, you may extend this exception to your
- * version of the file, but you are not obligated to do so. If you do not wish to
- * provide this exception without modification, you must delete this exception
- * statement from your version and license this file solely under the GPL without
- * exception.
- *
- *
- * Copyright (C) 2013 Red Hat, Inc.
- * All rights reserved.
- * END COPYRIGHT BLOCK **/
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stddef.h>
-#include <time.h>
-#include <string.h>
-#include <nss.h>
-
-/*
- * From otp.c
- */
-bool ipapwd_totp(const uint8_t *key, size_t len, const char *algo, int digits,
-                 time_t time, int offset, unsigned int step, uint32_t *out);
-
-#define SHA1   "sha1",   (uint8_t *) "12345678901234567890",             20
-#define SHA256 "sha256", (uint8_t *) "12345678901234567890123456789012", 32
-#define SHA512 "sha512", (uint8_t *) "12345678901234567890123456789012" \
-                                     "34567890123456789012345678901234", 64
-
-/* All TOTP test examples from RFC 6238 (Appendix B). */
-const static struct {
-    const char *algo;
-    const uint8_t *key;
-    size_t len;
-    time_t time;
-    uint32_t answer;
-} tests[] = {
-    { SHA1,            59, 94287082 },
-    { SHA256,          59, 46119246 },
-    { SHA512,          59, 90693936 },
-    { SHA1,    1111111109,  7081804 },
-    { SHA256,  1111111109, 68084774 },
-    { SHA512,  1111111109, 25091201 },
-    { SHA1,    1111111111, 14050471 },
-    { SHA256,  1111111111, 67062674 },
-    { SHA512,  1111111111, 99943326 },
-    { SHA1,    1234567890, 89005924 },
-    { SHA256,  1234567890, 91819424 },
-    { SHA512,  1234567890, 93441116 },
-    { SHA1,    2000000000, 69279037 },
-    { SHA256,  2000000000, 90698825 },
-    { SHA512,  2000000000, 38618901 },
-#ifdef _LP64 /* Only do these tests on 64-bit systems. */
-    { SHA1,   20000000000, 65353130 },
-    { SHA256, 20000000000, 77737706 },
-    { SHA512, 20000000000, 47863826 },
-#endif
-};
-
-int
-main(int argc, const char *argv[])
-{
-    uint32_t otp;
-    int i;
-
-    NSS_NoDB_Init(".");
-
-    for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
-        assert(ipapwd_totp(tests[i].key, tests[i].len, tests[i].algo,
-                           8, tests[i].time, 0, 30, &otp));
-        assert(otp == tests[i].answer);
-    }
-
-    NSS_Shutdown();
-    return 0;
-}
-- 
1.8.4.2

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to