Move authentication configuration cache into libotp

This enables plugins to share authentication configuration cache code.

Additionally, update the caching mechanism to be declarative and faster.

Reviewed-By: Thierry Bordaz <tbordaz@redhat.com>
This commit is contained in:
Nathaniel McCallum 2014-11-10 22:46:44 -05:00 committed by Martin Kosek
parent bdccb0c721
commit 953c6846b7
11 changed files with 346 additions and 408 deletions

View File

@ -44,7 +44,6 @@ libipa_pwd_extop_la_LIBADD = \
$(ASN1_UTIL_DIR)/libipaasn1.la \ $(ASN1_UTIL_DIR)/libipaasn1.la \
$(NULL) $(NULL)
libipa_pwd_extop_la_SOURCES = \ libipa_pwd_extop_la_SOURCES = \
authcfg.c \
common.c \ common.c \
encoding.c \ encoding.c \
prepost.c \ prepost.c \

View File

@ -1,280 +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, 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) 2014 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
#include "authcfg.h"
#include "ipapwd.h"
#include "pratom.h"
static struct config {
struct config *next;
Slapi_DN *suffix;
uint32_t config;
} *config;
static uint32_t string_to_config(const char *str)
{
static const struct {
const char *string;
uint32_t config;
} map[] = {
{ "disabled", AUTHCFG_AUTH_TYPE_DISABLED },
{ "password", AUTHCFG_AUTH_TYPE_PASSWORD },
{ "otp", AUTHCFG_AUTH_TYPE_OTP },
{ "pkinit", AUTHCFG_AUTH_TYPE_PKINIT },
{ "radius", AUTHCFG_AUTH_TYPE_RADIUS },
{}
};
for (uint32_t i = 0; map[i].string != NULL; i++) {
if (strcasecmp(map[i].string, str) == 0)
return map[i].config;
}
return AUTHCFG_AUTH_TYPE_NONE;
}
static uint32_t entry_to_config(Slapi_Entry *e)
{
char **auth_types = NULL;
if (e == NULL)
return AUTHCFG_AUTH_TYPE_NONE;
/* Fetch the auth type values from the config entry. */
auth_types = slapi_entry_attr_get_charray(e, "ipaUserAuthType");
if (auth_types == NULL)
return AUTHCFG_AUTH_TYPE_NONE;
uint32_t types = AUTHCFG_AUTH_TYPE_NONE;
for (uint32_t i = 0; auth_types[i] != NULL; i++)
types |= string_to_config(auth_types[i]);
slapi_ch_array_free(auth_types);
return types;
}
static Slapi_DN *suffix_to_config_dn(Slapi_DN *suffix)
{
Slapi_DN *sdn = NULL;
char *dn = NULL;
if (suffix == NULL)
return NULL;
dn = PR_smprintf("cn=ipaConfig,cn=etc,%s", slapi_sdn_get_dn(suffix));
if (dn == NULL)
return NULL;
sdn = slapi_sdn_new_dn_byval(dn);
PR_smprintf_free(dn);
return sdn;
}
static uint32_t suffix_to_config(Slapi_DN *suffix)
{
static char *attrs[] = { "ipaUserAuthType", NULL };
Slapi_Entry *entry = NULL;
Slapi_DN *sdn = NULL;
uint32_t types;
int ret;
sdn = suffix_to_config_dn(suffix);
if (sdn == NULL)
return AUTHCFG_AUTH_TYPE_NONE;
ret = slapi_search_internal_get_entry(sdn, attrs, &entry,
ipapwd_get_plugin_id());
slapi_sdn_free(&sdn);
if (ret != LDAP_SUCCESS)
return AUTHCFG_AUTH_TYPE_NONE;
types = entry_to_config(entry);
slapi_entry_free(entry);
return types;
}
static Slapi_DN *sdn_to_suffix(Slapi_DN *sdn)
{
Slapi_DN *suffix = NULL;
void *node = NULL;
if (sdn == NULL)
return NULL;
for (suffix = slapi_get_first_suffix(&node, 0); suffix != NULL;
suffix = slapi_get_next_suffix(&node, 0)) {
if (slapi_sdn_issuffix(sdn, suffix))
return suffix;
}
return NULL;
}
static bool sdn_is_config(Slapi_DN *sdn)
{
Slapi_DN *sfx = NULL;
Slapi_DN *cfg = NULL;
int cmp;
if (sdn == NULL)
return false;
sfx = sdn_to_suffix(sdn);
if (sfx == NULL)
return false;
cfg = suffix_to_config_dn(sfx);
if (cfg == NULL)
return false;
cmp = slapi_sdn_compare(cfg, sdn);
slapi_sdn_free(&cfg);
return cmp == 0;
}
void cache_free(struct config **cfg)
{
if (cfg == NULL || *cfg == NULL)
return;
cache_free(&(*cfg)->next);
free(*cfg);
*cfg = NULL;
}
bool authcfg_init(void)
{
struct config *cfg = NULL;
Slapi_DN *sfx = NULL;
void *node = NULL;
/* If we are already initialized, return true. */
if (config != NULL)
return true;
/* Look up the config for each suffix. */
for (sfx = slapi_get_first_suffix(&node, 0); sfx != NULL;
sfx = slapi_get_next_suffix(&node, 0)) {
cfg = calloc(1, sizeof(*cfg));
if (cfg == NULL) {
authcfg_fini();
return false;
}
cfg->suffix = sfx;
cfg->config = suffix_to_config(sfx);
cfg->next = config;
config = cfg;
}
return true;
}
void authcfg_fini(void)
{
cache_free(&config);
}
uint32_t authcfg_get_auth_types(Slapi_Entry *user_entry)
{
uint32_t glbl = AUTHCFG_AUTH_TYPE_NONE;
uint32_t user = AUTHCFG_AUTH_TYPE_NONE;
Slapi_DN *sfx = NULL;
Slapi_DN *sdn = NULL;
/* Find the root suffix. */
sdn = slapi_entry_get_sdn(user_entry);
sfx = sdn_to_suffix(sdn);
/* Find the global config. */
if (sfx != NULL) {
for (struct config *cfg = config; cfg && sfx; cfg = cfg->next) {
if (slapi_sdn_compare(sfx, cfg->suffix) == 0) {
glbl = PR_ATOMIC_ADD(&cfg->config, 0);
break;
}
}
}
/* Global disabled overrides user settings. */
if (glbl & AUTHCFG_AUTH_TYPE_DISABLED)
return AUTHCFG_AUTH_TYPE_DISABLED;
/* Get the user's config. */
user = entry_to_config(user_entry);
if (user == AUTHCFG_AUTH_TYPE_NONE) {
if (glbl == AUTHCFG_AUTH_TYPE_NONE)
return AUTHCFG_AUTH_TYPE_PASSWORD;
return glbl;
}
return user & ~AUTHCFG_AUTH_TYPE_DISABLED;
}
void authcfg_reload_global_config(Slapi_DN *sdn, Slapi_Entry *config_entry)
{
uint32_t glbl = AUTHCFG_AUTH_TYPE_NONE;
Slapi_DN *sfx = NULL;
Slapi_DN *dest;
/* Get the destination DN. */
dest = config_entry == NULL ? NULL : slapi_entry_get_sdn(config_entry);
/* Added, modified, moved into place. */
if (sdn_is_config(dest)) {
sfx = sdn_to_suffix(dest);
glbl = entry_to_config(config_entry);
/* Deleted, moved out of place. */
} else if (sdn_is_config(sdn)) {
sfx = sdn_to_suffix(sdn);
}
/* Reload config. */
for (struct config *cfg = config; cfg && sfx; cfg = cfg->next) {
if (slapi_sdn_compare(sfx, cfg->suffix) == 0) {
PR_ATOMIC_SET(&cfg->config, glbl);
break;
}
}
}

View File

@ -39,7 +39,7 @@
#include "ipapwd.h" #include "ipapwd.h"
#include "util.h" #include "util.h"
#include "authcfg.h" #include "../libotp/otp_config.h"
#include "ipa_asn1.h" #include "ipa_asn1.h"
/* /*
@ -89,6 +89,8 @@ Slapi_PluginDesc ipapwd_plugin_desc = {
void *ipapwd_plugin_id; void *ipapwd_plugin_id;
static int usetxn = 0; static int usetxn = 0;
extern struct otp_config *otp_config;
void *ipapwd_get_plugin_id(void) void *ipapwd_get_plugin_id(void)
{ {
return ipapwd_plugin_id; return ipapwd_plugin_id;
@ -1792,16 +1794,6 @@ static int ipapwd_start( Slapi_PBlock *pb )
Slapi_Entry *config_entry = NULL; Slapi_Entry *config_entry = NULL;
int ret; int ret;
/* NOTE: We never call authcfg_fini() from a destructor. This is because
* it may race with threaded requests at shutdown. This leak should
* only occur when the DS is exiting, so it isn't a big deal.
*/
if (!authcfg_init()) {
LOG_FATAL("AuthConf initialization failed!\n");
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
krberr = krb5_init_context(&krbctx); krberr = krb5_init_context(&krbctx);
if (krberr) { if (krberr) {
LOG_FATAL("krb5_init_context failed\n"); LOG_FATAL("krb5_init_context failed\n");
@ -1871,11 +1863,16 @@ static int ipapwd_start( Slapi_PBlock *pb )
ret = LDAP_SUCCESS; ret = LDAP_SUCCESS;
/* NOTE: We never call otp_config_fini() from a destructor. This is because
* it may race with threaded requests at shutdown. This leak should
* only occur when the DS is exiting, so it isn't a big deal.
*/
otp_config = otp_config_init(ipapwd_plugin_id);
done: done:
free(realm); free(realm);
krb5_free_context(krbctx); krb5_free_context(krbctx);
if (config_entry) slapi_entry_free(config_entry); if (config_entry) slapi_entry_free(config_entry);
if (ret != LDAP_SUCCESS) authcfg_fini();
return ret; return ret;
} }

View File

@ -63,7 +63,6 @@
#include "ipapwd.h" #include "ipapwd.h"
#include "util.h" #include "util.h"
#include "syncreq.h" #include "syncreq.h"
#include "authcfg.h"
#define IPAPWD_OP_NULL 0 #define IPAPWD_OP_NULL 0
#define IPAPWD_OP_ADD 1 #define IPAPWD_OP_ADD 1
@ -75,6 +74,8 @@ extern Slapi_PluginDesc ipapwd_plugin_desc;
extern void *ipapwd_plugin_id; extern void *ipapwd_plugin_id;
extern const char *ipa_realm_tree; extern const char *ipa_realm_tree;
struct otp_config *otp_config = NULL;
/* structure with information for each extension */ /* structure with information for each extension */
struct ipapwd_op_ext { struct ipapwd_op_ext {
char *object_name; /* name of the object extended */ char *object_name; /* name of the object extended */
@ -967,23 +968,9 @@ static int ipapwd_regen_nthash(Slapi_PBlock *pb, Slapi_Mods *smods,
return ret; return ret;
} }
static int ipapwd_post_authcfg(Slapi_PBlock *pb) static int ipapwd_post_updatecfg(Slapi_PBlock *pb)
{ {
Slapi_Entry *config_entry = NULL; otp_config_update(otp_config, pb);
Slapi_DN *sdn = NULL;
int oprc = 0;
/* Just bail if the operation failed. */
if (slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0 || oprc != 0)
return 0;
if (slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn) != 0)
return 0;
/* Ignore the error here (delete operations). */
slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &config_entry);
authcfg_reload_global_config(sdn, config_entry);
return 0; return 0;
} }
@ -1003,8 +990,7 @@ static int ipapwd_post_modadd(Slapi_PBlock *pb)
LOG_TRACE("=>\n"); LOG_TRACE("=>\n");
/* Ignore error when parsing configuration. */ otp_config_update(otp_config, pb);
ipapwd_post_authcfg(pb);
/* time to get the operation handler */ /* time to get the operation handler */
ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op); ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op);
@ -1144,7 +1130,7 @@ static bool ipapwd_do_otp_auth(const char *dn, Slapi_Entry *bind_entry,
bool success = false; bool success = false;
/* Find all of the user's active tokens. */ /* Find all of the user's active tokens. */
tokens = otp_token_find(ipapwd_plugin_id, dn, NULL, true, NULL); tokens = otp_token_find(otp_config, dn, NULL, true, NULL);
if (tokens == NULL) { if (tokens == NULL) {
slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME,
"%s: can't find tokens for '%s'.\n", __func__, dn); "%s: can't find tokens for '%s'.\n", __func__, dn);
@ -1190,11 +1176,7 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry,
uint32_t auth_types; uint32_t auth_types;
/* Get the configured authentication types. */ /* Get the configured authentication types. */
auth_types = authcfg_get_auth_types(entry); auth_types = otp_config_auth_types(otp_config, entry);
/* If global disabled flag is set, just punt. */
if (auth_types & AUTHCFG_AUTH_TYPE_DISABLED)
return true;
/* /*
* IMPORTANT SECTION! * IMPORTANT SECTION!
@ -1206,14 +1188,14 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry,
* 2. If PWD is enabled or OTP succeeded, fall through to PWD validation. * 2. If PWD is enabled or OTP succeeded, fall through to PWD validation.
*/ */
if (auth_types & AUTHCFG_AUTH_TYPE_OTP) { if (auth_types & OTP_CONFIG_AUTH_TYPE_OTP) {
LOG_PLUGIN_NAME(IPAPWD_PLUGIN_NAME, LOG_PLUGIN_NAME(IPAPWD_PLUGIN_NAME,
"Attempting OTP authentication for '%s'.\n", bind_dn); "Attempting OTP authentication for '%s'.\n", bind_dn);
if (ipapwd_do_otp_auth(bind_dn, entry, creds)) if (ipapwd_do_otp_auth(bind_dn, entry, creds))
return true; return true;
} }
return auth_types & AUTHCFG_AUTH_TYPE_PASSWORD; return auth_types & OTP_CONFIG_AUTH_TYPE_PASSWORD;
} }
static int ipapwd_authenticate(const char *dn, Slapi_Entry *entry, static int ipapwd_authenticate(const char *dn, Slapi_Entry *entry,
@ -1461,7 +1443,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
} }
/* Attempt to handle a token synchronization request. */ /* Attempt to handle a token synchronization request. */
if (syncreq && !sync_request_handle(ipapwd_get_plugin_id(), pb, dn)) if (syncreq && !sync_request_handle(otp_config, pb, dn))
goto invalid_creds; goto invalid_creds;
/* Attempt to write out kerberos keys for the user. */ /* Attempt to write out kerberos keys for the user. */
@ -1513,9 +1495,9 @@ int ipapwd_post_init(Slapi_PBlock *pb)
ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); 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_DESCRIPTION, (void *)&ipapwd_plugin_desc);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, (void *)ipapwd_post_modadd); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, (void *)ipapwd_post_modadd);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *)ipapwd_post_authcfg); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *)ipapwd_post_updatecfg);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *)ipapwd_post_modadd); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *)ipapwd_post_modadd);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *)ipapwd_post_authcfg); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *)ipapwd_post_updatecfg);
return ret; return ret;
} }
@ -1526,10 +1508,10 @@ int ipapwd_intpost_init(Slapi_PBlock *pb)
ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_03); ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_03);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&ipapwd_plugin_desc); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&ipapwd_plugin_desc);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN, (void *)ipapwd_post_authcfg); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN, (void *)ipapwd_post_updatecfg);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN, (void *)ipapwd_post_authcfg); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN, (void *)ipapwd_post_updatecfg);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN, (void *)ipapwd_post_authcfg); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN, (void *)ipapwd_post_updatecfg);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN, (void *)ipapwd_post_authcfg); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN, (void *)ipapwd_post_updatecfg);
return ret; return ret;
} }

View File

@ -52,7 +52,7 @@ bool sync_request_present(Slapi_PBlock *pb)
return ldap_control_find(OTP_SYNC_REQUEST_OID, controls, NULL) != NULL; return ldap_control_find(OTP_SYNC_REQUEST_OID, controls, NULL) != NULL;
} }
bool sync_request_handle(Slapi_ComponentId *plugin_id, Slapi_PBlock *pb, bool sync_request_handle(const struct otp_config *cfg, Slapi_PBlock *pb,
const char *user_dn) const char *user_dn)
{ {
struct otp_token **tokens = NULL; struct otp_token **tokens = NULL;
@ -90,7 +90,7 @@ bool sync_request_handle(Slapi_ComponentId *plugin_id, Slapi_PBlock *pb,
/* Process the synchronization. */ /* Process the synchronization. */
success = false; success = false;
if (ber_scanf(ber, "}") != LBER_ERROR) { if (ber_scanf(ber, "}") != LBER_ERROR) {
tokens = otp_token_find(plugin_id, user_dn, token_dn, true, NULL); tokens = otp_token_find(cfg, user_dn, token_dn, true, NULL);
if (tokens != NULL) { if (tokens != NULL) {
success = otp_token_sync_berval(tokens, OTP_SYNC_MAX_STEPS, first, second); success = otp_token_sync_berval(tokens, OTP_SYNC_MAX_STEPS, first, second);
otp_token_free_array(tokens); otp_token_free_array(tokens);

View File

@ -41,7 +41,7 @@
#ifndef SYNCREQ_H_ #ifndef SYNCREQ_H_
#define SYNCREQ_H_ #define SYNCREQ_H_
#include <dirsrv/slapi-plugin.h> #include "../libotp/otp_config.h"
#include <stdbool.h> #include <stdbool.h>
/* /*
@ -57,7 +57,7 @@
bool sync_request_present(Slapi_PBlock *pb); bool sync_request_present(Slapi_PBlock *pb);
bool sync_request_handle(Slapi_ComponentId *plugin_id, Slapi_PBlock *pb, bool sync_request_handle(const struct otp_config *cfg, Slapi_PBlock *pb,
const char *user_dn); const char *user_dn);
#endif /* SYNCREQ_H_ */ #endif /* SYNCREQ_H_ */

View File

@ -3,7 +3,7 @@ AM_CPPFLAGS = -I/usr/include/dirsrv
noinst_LTLIBRARIES = libhotp.la libotp.la noinst_LTLIBRARIES = libhotp.la libotp.la
libhotp_la_SOURCES = hotp.c hotp.h libhotp_la_SOURCES = hotp.c hotp.h
libotp_la_SOURCES = otp_token.c otp_token.h libotp_la_SOURCES = otp_config.c otp_config.h otp_token.c otp_token.h
libotp_la_LIBADD = libhotp.la libotp_la_LIBADD = libhotp.la
check_PROGRAMS = t_hotp check_PROGRAMS = t_hotp

View File

@ -0,0 +1,274 @@
/** 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) 2014 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
#include "otp_config.h"
#include <pratom.h>
#include <plstr.h>
#define OTP_CONFIG_AUTH_TYPE_DISABLED (1 << 31)
struct spec {
uint32_t (*func)(Slapi_Entry *, const char *attr);
const char *prefix;
const char *attr;
uint32_t dflt;
};
struct record {
struct record *next;
const struct spec *spec;
Slapi_DN *sdn;
uint32_t value;
};
struct otp_config {
Slapi_ComponentId *plugin_id;
struct record *records;
};
static uint32_t string_to_types(const char *str)
{
static const struct {
const char *string;
uint32_t config;
} map[] = {
{ "disabled", OTP_CONFIG_AUTH_TYPE_DISABLED },
{ "password", OTP_CONFIG_AUTH_TYPE_PASSWORD },
{ "otp", OTP_CONFIG_AUTH_TYPE_OTP },
{ "pkinit", OTP_CONFIG_AUTH_TYPE_PKINIT },
{ "radius", OTP_CONFIG_AUTH_TYPE_RADIUS },
{}
};
for (uint32_t i = 0; map[i].string != NULL; i++) {
if (strcasecmp(map[i].string, str) == 0)
return map[i].config;
}
return OTP_CONFIG_AUTH_TYPE_NONE;
}
static uint32_t entry_to_authtypes(Slapi_Entry *e, const char *attr)
{
char **auth_types = NULL;
if (e == NULL)
return OTP_CONFIG_AUTH_TYPE_NONE;
/* Fetch the auth type values from the config entry. */
auth_types = slapi_entry_attr_get_charray(e, attr);
if (auth_types == NULL)
return OTP_CONFIG_AUTH_TYPE_NONE;
uint32_t types = OTP_CONFIG_AUTH_TYPE_NONE;
for (uint32_t i = 0; auth_types[i] != NULL; i++)
types |= string_to_types(auth_types[i]);
slapi_ch_array_free(auth_types);
return types;
}
static const struct spec authtypes = {
entry_to_authtypes,
"cn=ipaConfig,cn=etc,%s",
"ipaUserAuthType",
OTP_CONFIG_AUTH_TYPE_PASSWORD
};
static Slapi_DN *make_sdn(const char *prefix, const Slapi_DN *suffix)
{
char *dn = slapi_ch_smprintf(prefix, slapi_sdn_get_dn(suffix));
return slapi_sdn_new_dn_passin(dn);
}
static uint32_t find_value(const struct otp_config *cfg,
const Slapi_DN *suffix, const struct spec *spec)
{
uint32_t value = 0;
Slapi_DN *sdn;
sdn = make_sdn(spec->prefix, suffix);
for (struct record *rec = cfg->records; rec != NULL; rec = rec->next) {
if (rec->spec == spec) {
value = PR_ATOMIC_ADD(&rec->value, 0);
break;
}
}
slapi_sdn_free(&sdn);
return value;
}
static void update(const struct otp_config *cfg, Slapi_DN *src,
Slapi_Entry *entry)
{
Slapi_DN *dst = entry == NULL ? NULL : slapi_entry_get_sdn(entry);
for (struct record *rec = cfg->records; rec != NULL; rec = rec->next) {
uint32_t val = rec->spec->dflt;
/* If added, modified or moved into place... */
if (dst != NULL && slapi_sdn_compare(rec->sdn, dst) == 0) {
Slapi_Attr *attr = NULL;
if (slapi_entry_attr_find(entry, rec->spec->attr, &attr) == 0)
val = rec->spec->func(entry, rec->spec->attr);
/* If NOT deleted or moved out of place... */
} else if (slapi_sdn_compare(rec->sdn, src) != 0)
continue;
PR_ATOMIC_SET(&rec->value, val);
}
}
struct otp_config *otp_config_init(Slapi_ComponentId *plugin_id)
{
static const struct spec *specs[] = {
&authtypes,
NULL
};
struct otp_config *cfg = NULL;
void *node = NULL;
cfg = (typeof(cfg)) slapi_ch_calloc(1, sizeof(*cfg));
cfg->plugin_id = plugin_id;
/* Build the config table. */
for (Slapi_DN *sfx = slapi_get_first_suffix(&node, 0);
sfx != NULL;
sfx = slapi_get_next_suffix(&node, 0)) {
for (size_t i = 0; specs[i] != NULL; i++) {
Slapi_Entry *entry = NULL;
struct record *rec;
/* Create the config entry. */
rec = (typeof(rec)) slapi_ch_calloc(1, sizeof(*rec));
rec->spec = specs[i];
rec->sdn = make_sdn(rec->spec->prefix, sfx);
/* Add config to the list. */
rec->next = cfg->records;
cfg->records = rec;
/* Load the specified entry. */
slapi_search_internal_get_entry(rec->sdn, NULL, &entry, plugin_id);
update(cfg, rec->sdn, entry);
slapi_entry_free(entry);
}
}
return cfg;
}
static void record_fini(struct record **rec)
{
if (rec == NULL || *rec == NULL)
return;
record_fini(&(*rec)->next);
slapi_sdn_free(&(*rec)->sdn);
slapi_ch_free((void **) rec);
}
void otp_config_fini(struct otp_config **cfg)
{
if (cfg == NULL || *cfg == NULL)
return;
record_fini(&(*cfg)->records);
slapi_ch_free((void **) cfg);
}
void otp_config_update(struct otp_config *cfg, Slapi_PBlock *pb)
{
Slapi_Entry *entry = NULL;
Slapi_DN *src = NULL;
int oprc = 0;
/* Just bail if the operation failed. */
if (slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0 || oprc != 0)
return;
/* Get the source SDN. */
if (slapi_pblock_get(pb, SLAPI_TARGET_SDN, &src) != 0)
return;
/* Ignore the error here (delete operations). */
(void) slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &entry);
update(cfg, src, entry);
}
Slapi_ComponentId *otp_config_plugin_id(const struct otp_config *cfg)
{
if (cfg == NULL)
return NULL;
return cfg->plugin_id;
}
uint32_t otp_config_auth_types(const struct otp_config *cfg,
Slapi_Entry *user_entry)
{
uint32_t glbl = OTP_CONFIG_AUTH_TYPE_NONE;
uint32_t user = OTP_CONFIG_AUTH_TYPE_NONE;
const Slapi_DN *sfx;
/* Load the global value. */
sfx = slapi_get_suffix_by_dn(slapi_entry_get_sdn(user_entry));
glbl = find_value(cfg, sfx, &authtypes);
/* Load the user value if not disabled. */
if ((glbl & OTP_CONFIG_AUTH_TYPE_DISABLED) == 0)
user = entry_to_authtypes(user_entry, authtypes.attr);
/* Filter out the disabled flag. */
glbl &= ~OTP_CONFIG_AUTH_TYPE_DISABLED;
user &= ~OTP_CONFIG_AUTH_TYPE_DISABLED;
if (user != OTP_CONFIG_AUTH_TYPE_NONE)
return user;
if (glbl != OTP_CONFIG_AUTH_TYPE_NONE)
return glbl;
return OTP_CONFIG_AUTH_TYPE_PASSWORD;
}

View File

@ -37,46 +37,29 @@
* All rights reserved. * All rights reserved.
* END COPYRIGHT BLOCK **/ * END COPYRIGHT BLOCK **/
#pragma once
#ifndef AUTHCFG_H_
#define AUTHCFG_H_
#include <dirsrv/slapi-plugin.h> #include <dirsrv/slapi-plugin.h>
#include <stdbool.h>
#define AUTHCFG_AUTH_TYPE_NONE 0 #define OTP_CONFIG_AUTH_TYPE_NONE 0
#define AUTHCFG_AUTH_TYPE_DISABLED 1 #define OTP_CONFIG_AUTH_TYPE_PASSWORD (1 << 0)
#define AUTHCFG_AUTH_TYPE_PASSWORD 2 #define OTP_CONFIG_AUTH_TYPE_OTP (1 << 1)
#define AUTHCFG_AUTH_TYPE_OTP 4 #define OTP_CONFIG_AUTH_TYPE_PKINIT (1 << 2)
#define AUTHCFG_AUTH_TYPE_PKINIT 8 #define OTP_CONFIG_AUTH_TYPE_RADIUS (1 << 3)
#define AUTHCFG_AUTH_TYPE_RADIUS 16
/* Initialize authentication configuration. struct otp_config;
*
* Thread Safety: NO
*/
bool authcfg_init(void);
/* Free global authentication configuration resources. struct otp_config *otp_config_init(Slapi_ComponentId *plugin_id);
*
* Thread Safety: NO void otp_config_fini(struct otp_config **cfg);
*/
void authcfg_fini(void); void otp_config_update(struct otp_config *cfg, Slapi_PBlock *pb);
Slapi_ComponentId *otp_config_plugin_id(const struct otp_config *cfg);
/* Gets the permitted authentication types for the given user entry. /* Gets the permitted authentication types for the given user entry.
* *
* The entry should be queried for the "ipaUserAuthType" attribute. * The entry should be queried for the "ipaUserAuthType" attribute.
*
* Thread Safety: YES
*/ */
uint32_t authcfg_get_auth_types(Slapi_Entry *user_entry); uint32_t otp_config_auth_types(const struct otp_config *cfg,
Slapi_Entry *user_entry);
/* Reloads configuration from the specified global config entry.
*
* If the provided entry isn't a global config entry, this is a no-op.
*
* Thread Safety: YES
*/
void authcfg_reload_global_config(Slapi_DN *sdn, Slapi_Entry *config_entry);
#endif /* AUTHCFG_H_ */

View File

@ -59,7 +59,7 @@ enum type {
}; };
struct otp_token { struct otp_token {
Slapi_ComponentId *plugin_id; const struct otp_config *cfg;
Slapi_DN *sdn; Slapi_DN *sdn;
struct hotp_token token; struct hotp_token token;
enum type type; enum type type;
@ -75,21 +75,6 @@ struct otp_token {
}; };
}; };
static const char *get_basedn(Slapi_DN *dn)
{
Slapi_DN *suffix = NULL;
void *node = NULL;
for (suffix = slapi_get_first_suffix(&node, 0);
suffix != NULL;
suffix = slapi_get_next_suffix(&node, 0)) {
if (slapi_sdn_issuffix(dn, suffix))
return (char *) slapi_sdn_get_dn(suffix);
}
return NULL;
}
static inline bool is_algo_valid(const char *algo) static inline bool is_algo_valid(const char *algo)
{ {
static const char *valid_algos[] = { "sha1", "sha256", "sha384", static const char *valid_algos[] = { "sha1", "sha256", "sha384",
@ -142,8 +127,8 @@ static bool writeattr(const struct otp_token *token, const char *attr,
snprintf(value, sizeof(value), "%lld", val); snprintf(value, sizeof(value), "%lld", val);
pb = slapi_pblock_new(); pb = slapi_pblock_new();
slapi_modify_internal_set_pb(pb, slapi_sdn_get_dn(token->sdn), slapi_modify_internal_set_pb(pb, slapi_sdn_get_dn(token->sdn), mods, NULL,
mods, NULL, NULL, token->plugin_id, 0); NULL, otp_config_plugin_id(token->cfg), 0);
if (slapi_modify_internal_pb(pb) != 0) if (slapi_modify_internal_pb(pb) != 0)
goto error; goto error;
if (slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret) != 0) if (slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret) != 0)
@ -251,7 +236,7 @@ void otp_token_free_array(struct otp_token **tokens)
free(tokens); free(tokens);
} }
static struct otp_token *otp_token_new(Slapi_ComponentId *id, static struct otp_token *otp_token_new(const struct otp_config *cfg,
Slapi_Entry *entry) Slapi_Entry *entry)
{ {
const struct berval *tmp; const struct berval *tmp;
@ -261,7 +246,7 @@ static struct otp_token *otp_token_new(Slapi_ComponentId *id,
token = calloc(1, sizeof(struct otp_token)); token = calloc(1, sizeof(struct otp_token));
if (token == NULL) if (token == NULL)
return NULL; return NULL;
token->plugin_id = id; token->cfg = cfg;
/* Get the token type. */ /* Get the token type. */
vals = slapi_entry_attr_get_charray(entry, "objectClass"); vals = slapi_entry_attr_get_charray(entry, "objectClass");
@ -333,16 +318,16 @@ error:
return NULL; return NULL;
} }
static struct otp_token **find(Slapi_ComponentId *id, const char *user_dn, static struct otp_token **find(const struct otp_config *cfg, const char *user_dn,
const char *token_dn, const char *intfilter, const char *token_dn, const char *intfilter,
const char *extfilter) const char *extfilter)
{ {
struct otp_token **tokens = NULL; struct otp_token **tokens = NULL;
const Slapi_DN *basedn = NULL;
Slapi_Entry **entries = NULL; Slapi_Entry **entries = NULL;
Slapi_PBlock *pb = NULL; Slapi_PBlock *pb = NULL;
Slapi_DN *sdn = NULL; Slapi_DN *sdn = NULL;
char *filter = NULL; char *filter = NULL;
const char *basedn = NULL;
size_t count = 0; size_t count = 0;
int result = -1; int result = -1;
@ -367,20 +352,19 @@ static struct otp_token **find(Slapi_ComponentId *id, const char *user_dn,
if (token_dn != NULL) { if (token_dn != NULL) {
/* Find only the token specified. */ /* Find only the token specified. */
slapi_search_internal_set_pb(pb, token_dn, LDAP_SCOPE_BASE, filter, slapi_search_internal_set_pb(pb, token_dn, LDAP_SCOPE_BASE, filter,
NULL, 0, NULL, NULL, id, 0); NULL, 0, NULL, NULL,
otp_config_plugin_id(cfg), 0);
} else { } else {
sdn = slapi_sdn_new_dn_byval(user_dn); sdn = slapi_sdn_new_dn_byval(user_dn);
if (sdn == NULL) basedn = slapi_get_suffix_by_dn(sdn);
goto error; slapi_sdn_free(&sdn);
basedn = get_basedn(sdn);
if (basedn == NULL) if (basedn == NULL)
goto error; goto error;
/* Find all user tokens. */ /* Find all user tokens. */
slapi_search_internal_set_pb(pb, basedn, slapi_search_internal_set_pb(pb, slapi_sdn_get_dn(basedn),
LDAP_SCOPE_SUBTREE, filter, NULL, LDAP_SCOPE_SUBTREE, filter, NULL, 0,
0, NULL, NULL, id, 0); NULL, NULL, otp_config_plugin_id(cfg), 0);
} }
slapi_search_internal_pb(pb); slapi_search_internal_pb(pb);
slapi_ch_free_string(&filter); slapi_ch_free_string(&filter);
@ -402,7 +386,7 @@ static struct otp_token **find(Slapi_ComponentId *id, const char *user_dn,
if (tokens == NULL) if (tokens == NULL)
goto error; goto error;
for (count = 0; entries[count] != NULL; count++) { for (count = 0; entries[count] != NULL; count++) {
tokens[count] = otp_token_new(id, entries[count]); tokens[count] = otp_token_new(cfg, entries[count]);
if (tokens[count] == NULL) { if (tokens[count] == NULL) {
otp_token_free_array(tokens); otp_token_free_array(tokens);
tokens = NULL; tokens = NULL;
@ -411,15 +395,13 @@ static struct otp_token **find(Slapi_ComponentId *id, const char *user_dn,
} }
error: error:
if (sdn != NULL)
slapi_sdn_free(&sdn);
slapi_pblock_destroy(pb); slapi_pblock_destroy(pb);
return tokens; return tokens;
} }
struct otp_token ** struct otp_token **otp_token_find(const struct otp_config *cfg,
otp_token_find(Slapi_ComponentId *id, const char *user_dn, const char *token_dn, const char *user_dn, const char *token_dn,
bool active, const char *filter) bool active, const char *filter)
{ {
static const char template[] = static const char template[] =
"(|(ipatokenNotBefore<=%04d%02d%02d%02d%02d%02dZ)(!(ipatokenNotBefore=*)))" "(|(ipatokenNotBefore<=%04d%02d%02d%02d%02d%02dZ)(!(ipatokenNotBefore=*)))"
@ -430,7 +412,7 @@ otp_token_find(Slapi_ComponentId *id, const char *user_dn, const char *token_dn,
time_t now; time_t now;
if (!active) if (!active)
return find(id, user_dn, token_dn, NULL, filter); return find(cfg, user_dn, token_dn, NULL, filter);
/* Get the current time. */ /* Get the current time. */
if (time(&now) == (time_t) -1) if (time(&now) == (time_t) -1)
@ -446,7 +428,7 @@ otp_token_find(Slapi_ComponentId *id, const char *user_dn, const char *token_dn,
tm.tm_hour, tm.tm_min, tm.tm_sec) < 0) tm.tm_hour, tm.tm_min, tm.tm_sec) < 0)
return NULL; return NULL;
return find(id, user_dn, token_dn, actfilt, filter); return find(cfg, user_dn, token_dn, actfilt, filter);
} }
int otp_token_get_digits(struct otp_token *token) int otp_token_get_digits(struct otp_token *token)

View File

@ -39,14 +39,15 @@
#pragma once #pragma once
#include <dirsrv/slapi-plugin.h> #include "otp_config.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
struct otp_token; struct otp_token;
/* Frees the token array. */ /* Frees the token array. */
void otp_token_free_array(struct otp_token **tokens); void
otp_token_free_array(struct otp_token **tokens);
/* Find tokens. /* Find tokens.
* *
@ -65,9 +66,9 @@ void otp_token_free_array(struct otp_token **tokens);
* Returns NULL on error. If no tokens are found, an empty array is returned. * Returns NULL on error. If no tokens are found, an empty array is returned.
* The array is NULL terminated. * The array is NULL terminated.
*/ */
struct otp_token **otp_token_find(Slapi_ComponentId *id, const char *user_dn, struct otp_token **otp_token_find(const struct otp_config *cfg,
const char *token_dn, bool active, const char *user_dn, const char *token_dn,
const char *filter); bool active, const char *filter);
/* Get the length of the token code. */ /* Get the length of the token code. */
int otp_token_get_digits(struct otp_token *token); int otp_token_get_digits(struct otp_token *token);