mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Add OTP sync support to ipa-pwd-extop
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
This commit is contained in:
parent
49038cda9f
commit
a51b07c275
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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);
|
||||
|
@ -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, ¶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);
|
||||
}
|
@ -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)
|
||||
{
|
||||
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';
|
||||
}
|
||||
|
||||
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])));
|
||||
}
|
||||
|
||||
otptoken_free_array(tokens);
|
||||
return success;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 *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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
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 <pzuna@redhat.com>
|
||||
*/
|
||||
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);
|
||||
|
111
daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c
Normal file
111
daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <npmccallum@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
|
||||
#include <libotp.h>
|
||||
#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;
|
||||
}
|
63
daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.h
Normal file
63
daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <npmccallum@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
* END COPYRIGHT BLOCK **/
|
||||
|
||||
|
||||
#ifndef SYNCREQ_H_
|
||||
#define SYNCREQ_H_
|
||||
|
||||
#include <dirsrv/slapi-plugin.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/*
|
||||
* 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_ */
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user