From 05b620c6009bac717ec21bffe331dc819f2f0256 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Tue, 28 Jan 2014 17:09:58 -0500 Subject: Add OTP sync support to ipa-pwd-extop --- .../ipa-slapi-plugins/ipa-pwd-extop/Makefile.am | 13 +- 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 | 386 ++++++++++---------- daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c | 111 ++++++ daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.h | 63 ++++ daemons/ipa-slapi-plugins/ipa-pwd-extop/t_hotp.c | 82 ----- daemons/ipa-slapi-plugins/ipa-pwd-extop/t_totp.c | 103 ------ 9 files changed, 373 insertions(+), 970 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 create mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c create mode 100644 daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.h 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 03e36a21e..b8d987853 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) \ @@ -34,24 +35,16 @@ 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 \ ipa_pwd_extop.c \ + syncreq.c \ $(KRB5_UTIL_SRCS) \ $(NULL) 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 cccddeb91..000000000 --- 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 b4087488c..1ef3627e0 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 #endif +#include + #include #include #include #include #include #include -#include #include #include -#include #include #include #include @@ -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 6c0f8554b..000000000 --- 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 -#include - -#include -#include -#include -#include - -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, ¶m); - 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 ef37b5e17..850cdebc4 100644 --- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c +++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c @@ -62,13 +62,13 @@ #include "ipapwd.h" #include "util.h" +#include "syncreq.h" #define IPAPWD_OP_NULL 0 #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,73 +1241,78 @@ 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(const char *dn, Slapi_Entry *bind_entry, + 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; - - /* If we didn't start successfully, bail. */ - if (!ipapwd_get_plugin_started()) { - goto done; - } - - /* 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); + struct otptoken **tokens = NULL; + bool success = false; + + /* Find all of the user's active tokens. */ + tokens = otptoken_find(ipapwd_plugin_id, 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__, 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'; } - } - if (i != 0) { - LOG_FATAL("Not handled (can't retrieve bind parameters)\n"); - goto done; - } - bind_dn = slapi_sdn_get_dn(bind_sdn); - - /* 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) { - LOG_TRACE("Not handled (not simple bind or NULL dn/credentials)\n"); - goto done; + slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, + "%s: token authentication %s " + "(user: '%s', token: '%s\').\n", __func__, + success ? "succeeded" : "failed", dn, + slapi_sdn_get_ndn(otptoken_get_sdn(tokens[i]))); } - /* Check if any allowed authentication types are set in the user entry. - * If not, we just use the global settings from the config entry. */ - result = slapi_search_internal_get_entry(bind_sdn, user_attrs, &bind_entry, - ipapwd_get_plugin_id()); - 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; - } - if (bind_entry == NULL) { - LOG_FATAL("Not handled (could not find entry for BIND dn %s)\n", bind_dn); - goto done; - } + otptoken_free_array(tokens); + return success; +} - 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; - } +/* + * This function handles the bind functionality for OTP. The return value + * indicates if the OTP portion of authentication was successful. + * + * NOTE: This function may modify creds. See explanation in the comment for + * ipapwd_do_otp_auth() above. + */ +static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry, + struct berval *creds) +{ + char **auth_types = NULL; + bool otpauth; + bool pwdauth; - auth_types = slapi_entry_attr_get_charray(bind_entry, IPA_USER_AUTH_TYPE); + /* If we didn't start successfully, bail. */ + if (!ipapwd_get_plugin_started()) + return true; + + /* If global disabled flag is set, just punt. */ + if (ipapwd_otp_is_disabled()) + return true; /* * IMPORTANT SECTION! @@ -1315,149 +1320,37 @@ static int ipapwd_pre_bind_otp(Slapi_PBlock * pb) * 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. + * 2. If PWD is enabled or OTP succeeded, fall through to PWD validation. */ + auth_types = slapi_entry_attr_get_charray(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 OTP is allowed, attempt to do OTP authentication. */ - if (ipapwd_is_auth_type_allowed(auth_types, IPA_OTP_AUTH_TYPE_OTP)) { + if (otpauth) { 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 (ipapwd_do_otp_auth(bind_dn, entry, creds)) + return true; } - /* 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: - slapi_ch_array_free(auth_types); - slapi_entry_free(bind_entry); - return ret; + return pwdauth; } -/* PRE BIND Operation: - * Used for password migration from DS to IPA. - * Gets the clean text password, authenticates the user and generates - * a kerberos key if missing. - * Person to blame if anything blows up: Pavel Zuna - */ -static int ipapwd_pre_bind(Slapi_PBlock *pb) +static int ipapwd_authenticate(const char *dn, Slapi_Entry *entry, + const struct berval *credentials) { - struct ipapwd_krbcfg *krbcfg = NULL; - struct ipapwd_data pwdata; - struct berval *credentials; /* bind credentials */ - Slapi_Entry *entry = NULL; Slapi_Value **pwd_values = NULL; /* values of userPassword attribute */ Slapi_Value *value = NULL; Slapi_Attr *attr = NULL; - struct tm expire_tm; - char *errMesg = "Internal operations error\n"; /* error message */ - char *expire = NULL; /* passwordExpirationTime attribute value */ - char *dn = NULL; /* bind DN */ - Slapi_Value *objectclass; - int method; /* authentication method */ - int ret = 0; - char *principal = NULL; - - 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; - } - - /* we're only interested in simple authentication */ - if (method != LDAP_AUTH_SIMPLE) - goto done; - - /* list of attributes to retrieve */ - const char *attrs_list[] = {SLAPI_USERPWD_ATTR, "krbprincipalkey", "uid", - "krbprincipalname", "objectclass", - "passwordexpirationtime", "passwordhistory", - NULL}; - - /* retrieve user entry */ - ret = ipapwd_getEntry(dn, &entry, (char **) attrs_list); - if (ret) { - LOG("failed to retrieve user entry: %s\n", dn); - goto done; - } - - /* check the krbPrincipalName attribute is present */ - ret = slapi_entry_attr_find(entry, "krbprincipalname", &attr); - if (ret) { - LOG("no krbPrincipalName in user entry: %s\n", dn); - goto done; - } - - /* we aren't interested in host principals */ - objectclass = slapi_value_new_string("ipaHost"); - if ((slapi_entry_attr_has_syntax_value(entry, SLAPI_ATTR_OBJECTCLASS, objectclass)) == 1) { - slapi_value_free(&objectclass); - goto done; - } - slapi_value_free(&objectclass); - - /* check the krbPrincipalKey attribute is NOT present */ - ret = slapi_entry_attr_find(entry, "krbprincipalkey", &attr); - if (!ret) { - LOG("kerberos key already present in user entry: %s\n", dn); - goto done; - } + int ret; /* retrieve userPassword attribute */ ret = slapi_entry_attr_find(entry, SLAPI_USERPWD_ATTR, &attr); if (ret) { LOG("no " SLAPI_USERPWD_ATTR " in user entry: %s\n", dn); - goto done; + return ret; } /* get the number of userPassword values and allocate enough memory */ @@ -1467,7 +1360,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb) if (!pwd_values) { /* probably not required: should terminate the server anyway */ LOG_OOM(); - goto done; + return ret; } /* zero-fill the allocated memory; we need the array ending with NULL */ memset(pwd_values, 0, ret); @@ -1487,8 +1380,45 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb) slapi_ch_free((void **) &pwd_values); slapi_value_free(&value); - if (ret) { + if (ret) LOG("invalid BIND password for user entry: %s\n", dn); + return ret; +} + +static void ipapwd_write_krb_keys(Slapi_PBlock *pb, char *dn, + Slapi_Entry *entry, + const struct berval *credentials) +{ + char *errMesg = "Internal operations error\n"; + struct ipapwd_krbcfg *krbcfg = NULL; + struct ipapwd_data pwdata; + Slapi_Value *objectclass; + Slapi_Attr *attr = NULL; + char *principal = NULL; + struct tm expire_tm; + char *expire = NULL; + int ret; + + /* check the krbPrincipalName attribute is present */ + ret = slapi_entry_attr_find(entry, "krbprincipalname", &attr); + if (ret) { + LOG("no krbPrincipalName in user entry: %s\n", dn); + goto done; + } + + /* we aren't interested in host principals */ + objectclass = slapi_value_new_string("ipaHost"); + if ((slapi_entry_attr_has_syntax_value(entry, SLAPI_ATTR_OBJECTCLASS, + objectclass)) == 1) { + slapi_value_free(&objectclass); + goto done; + } + slapi_value_free(&objectclass); + + /* check the krbPrincipalKey attribute is NOT present */ + ret = slapi_entry_attr_find(entry, "krbprincipalkey", &attr); + if (!ret) { + LOG("kerberos key already present in user entry: %s\n", dn); goto done; } @@ -1556,10 +1486,80 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb) done: slapi_ch_free_string(&principal); slapi_ch_free_string(&expire); - if (entry) - slapi_entry_free(entry); free_ipapwd_krbcfg(&krbcfg); +} + + +/* PRE BIND Operation + * + * Used for: + * 1. Password migration from DS to IPA -- Gets the clean text password, + * authenticates the user and generates a kerberos key if missing. + * 2. OTP validation + * 3. OTP synchronization + */ +static int ipapwd_pre_bind(Slapi_PBlock *pb) +{ + static const char *attrs_list[] = { + SLAPI_USERPWD_ATTR, IPA_USER_AUTH_TYPE, "krbprincipalkey", "uid", + "krbprincipalname", "objectclass", "passwordexpirationtime", + "passwordhistory", + NULL + }; + struct berval *credentials = NULL; + Slapi_Entry *entry = NULL; + char *dn = NULL; + int method = 0; + bool syncreq; + int ret = 0; + + /* 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"); + return 0; + } + /* We're only interested in simple authentication. */ + if (method != LDAP_AUTH_SIMPLE || credentials->bv_len == 0) + return 0; + + /* Retrieve the user's entry. */ + ret = ipapwd_getEntry(dn, &entry, (char **) attrs_list); + if (ret) { + LOG("failed to retrieve user entry: %s\n", dn); + return 0; + } + + /* Try to do OTP first. */ + syncreq = sync_request_present(pb); + if (!syncreq && !ipapwd_pre_bind_otp(dn, entry, credentials)) { + slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, + NULL, NULL, 0, NULL); + return 1; + } + + /* Authenticate the user. */ + ret = ipapwd_authenticate(dn, entry, credentials); + if (ret) { + slapi_entry_free(entry); + return 0; + } + + /* Attempt to handle a token synchronization request. */ + if (syncreq && !sync_request_handle(ipapwd_get_plugin_id(), pb, dn)) { + slapi_entry_free(entry); + slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, + NULL, NULL, 0, NULL); + return 1; + } + + /* Attempt to write out kerberos keys for the user. */ + ipapwd_write_krb_keys(pb, dn, entry, credentials); + + slapi_entry_free(entry); return 0; } @@ -1568,6 +1568,8 @@ int ipapwd_pre_init(Slapi_PBlock *pb) { int ret; + slapi_register_supported_control(OTP_SYNC_REQUEST_OID, SLAPI_OPERATION_BIND); + ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&ipapwd_plugin_desc); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_BIND_FN, (void *)ipapwd_pre_bind); diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c new file mode 100644 index 000000000..27878776f --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c @@ -0,0 +1,111 @@ +/** 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + * + * Additional permission under GPLv3 section 7: + * + * In the following paragraph, "GPL" means the GNU General Public + * License, version 3 or any later version, and "Non-GPL Code" means + * code that is governed neither by the GPL nor a license + * compatible with the GPL. + * + * You may link the code of this Program with Non-GPL Code and convey + * linked combinations including the two, provided that such Non-GPL + * Code only links 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 GPL. Only the copyright holders of this + * Program may make changes or additions to the list of Approved + * Interfaces. + * + * Authors: + * Nathaniel McCallum + * + * Copyright (C) 2013 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + + +#include +#include "syncreq.h" + +#define OTP_SYNC_MAX_STEPS 25 + +bool sync_request_present(Slapi_PBlock *pb) +{ + LDAPControl **controls = NULL; + + if (slapi_pblock_get(pb, SLAPI_REQCONTROLS, &controls) != 0) + return false; + + return ldap_control_find(OTP_SYNC_REQUEST_OID, controls, NULL) != NULL; +} + +bool sync_request_handle(Slapi_ComponentId *plugin_id, Slapi_PBlock *pb, + const char *user_dn) +{ + struct otptoken **tokens = NULL; + LDAPControl **controls = NULL; + BerElement *ber = NULL; + char *token_dn = NULL; + int second = 0; + int first = 0; + + if (slapi_pblock_get(pb, SLAPI_REQCONTROLS, &controls) != 0) + return false; + + if (controls == NULL || controls[0] == NULL) + return false; + + for (int i = 0; controls[i] != NULL; i++) { + if (strcmp(controls[i]->ldctl_oid, OTP_SYNC_REQUEST_OID) != 0) + continue; + + /* Decode the request. */ + ber = ber_init(&controls[i]->ldctl_value); + if (ber == NULL) + return false; + + /* Decode the token codes. */ + if (ber_scanf(ber, "{ii", &first, &second) == LBER_ERROR) { + ber_free(ber, 1); + return false; + } + + /* Decode the optional token DN. */ + ber_scanf(ber, "a", &token_dn); + if (ber_scanf(ber, "}") == LBER_ERROR) { + ber_free(ber, 1); + return false; + } + ber_free(ber, 1); + + /* Find all the tokens. */ + tokens = otptoken_find(plugin_id, user_dn, token_dn, true, NULL); + ber_memfree(token_dn); + if (tokens == NULL) + return false; + + /* Synchronize the token. */ + if (!otptoken_sync(tokens, OTP_SYNC_MAX_STEPS, first, second)) { + otptoken_free_array(tokens); + return false; + } + + otptoken_free_array(tokens); + } + + return true; +} diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.h b/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.h new file mode 100644 index 000000000..049a62102 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.h @@ -0,0 +1,63 @@ +/** 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + * + * Additional permission under GPLv3 section 7: + * + * In the following paragraph, "GPL" means the GNU General Public + * License, version 3 or any later version, and "Non-GPL Code" means + * code that is governed neither by the GPL nor a license + * compatible with the GPL. + * + * You may link the code of this Program with Non-GPL Code and convey + * linked combinations including the two, provided that such Non-GPL + * Code only links 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 GPL. Only the copyright holders of this + * Program may make changes or additions to the list of Approved + * Interfaces. + * + * Authors: + * Nathaniel McCallum + * + * Copyright (C) 2013 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + + +#ifndef SYNCREQ_H_ +#define SYNCREQ_H_ + +#include +#include + +/* + * The ASN.1 encoding of the request structure: + * + * OTPSyncRequest ::= SEQUENCE { + * firstCode INTEGER, + * secondCode INTEGER, + * tokenDN OCTET STRING OPTIONAL + * } + */ +#define OTP_SYNC_REQUEST_OID "2.16.840.1.113730.3.8.10.6" + +bool sync_request_present(Slapi_PBlock *pb); + +bool sync_request_handle(Slapi_ComponentId *plugin_id, Slapi_PBlock *pb, + const char *user_dn); + +#endif /* SYNCREQ_H_ */ 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 d57f9ab68..000000000 --- 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 -#include -#include -#include -#include -#include -#include - -/* - * 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 2df8d2458..000000000 --- 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 -#include -#include -#include -#include -#include -#include - -/* - * 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; -} -- cgit