krb5, kadm5: refactor plugin API

Refactor plugin framework to use a single list of loaded plugins; add a new
plugin API where DSOs export a load function that can declare dependencies and
export multiple plugins; refactor kadm5 hook API to use krb5 plugin framework.

More information in krb5-plugin(7).
This commit is contained in:
Luke Howard
2019-01-01 21:55:36 +11:00
committed by Nico Williams
parent e9b3b2326d
commit 803efebca5
37 changed files with 1293 additions and 639 deletions

View File

@@ -39,6 +39,21 @@ static int have_plugin = 0;
* Pick the first WINDC module that we find.
*/
static const char *windc_plugin_deps[] = {
"kdc",
"krb5",
"hdb",
NULL
};
static struct krb5_plugin_data windc_plugin_data = {
"krb5",
"windc",
KRB5_WINDC_PLUGIN_MINOR,
windc_plugin_deps,
kdc_get_instance
};
static krb5_error_code KRB5_LIB_CALL
load(krb5_context context, const void *plug, void *plugctx, void *userctx)
{
@@ -49,8 +64,8 @@ load(krb5_context context, const void *plug, void *plugctx, void *userctx)
krb5_error_code
krb5_kdc_windc_init(krb5_context context)
{
(void)_krb5_plugin_run_f(context, "krb5", "windc",
KRB5_WINDC_PLUGIN_MINOR, 0, NULL, load);
(void)_krb5_plugin_run_f(context, &windc_plugin_data, 0, NULL, load);
return 0;
}
@@ -84,8 +99,8 @@ _kdc_pac_generate(krb5_context context,
uc.client = client;
uc.pac = pac;
(void)_krb5_plugin_run_f(context, "krb5", "windc",
KRB5_WINDC_PLUGIN_MINOR, 0, &uc, generate);
(void)_krb5_plugin_run_f(context, &windc_plugin_data,
0, &uc, generate);
return 0;
}
@@ -141,8 +156,8 @@ _kdc_pac_verify(krb5_context context,
uc.pac = pac;
uc.verified = verified;
(void)_krb5_plugin_run_f(context, "krb5", "windc",
KRB5_WINDC_PLUGIN_MINOR, 0, &uc, verify);
(void)_krb5_plugin_run_f(context, &windc_plugin_data,
0, &uc, verify);
return 0;
}
@@ -191,8 +206,8 @@ _kdc_check_access(krb5_context context,
uc.req = req;
uc.method_data = method_data;
ret = _krb5_plugin_run_f(context, "krb5", "windc",
KRB5_WINDC_PLUGIN_MINOR, 0, &uc, check);
ret = _krb5_plugin_run_f(context, &windc_plugin_data,
0, &uc, check);
}
if (ret == KRB5_PLUGIN_NO_HANDLE)
@@ -202,3 +217,18 @@ _kdc_check_access(krb5_context context,
req->msg_type == krb_as_req);
return ret;
}
uintptr_t
kdc_get_instance(const char *libname)
{
static const char *instance = "libkdc";
if (strcmp(libname, "kdc") == 0)
return (uintptr_t)instance;
else if (strcmp(libname, "hdb") == 0)
return hdb_get_instance(libname);
else if (strcmp(libname, "krb5") == 0)
return krb5_get_instance(libname);
return 0;
}

View File

@@ -390,6 +390,8 @@ make_sym(const char *prefix)
return sym;
}
static const char *hdb_plugin_deps[] = { "hdb", "krb5", NULL };
krb5_error_code
hdb_list_builtin(krb5_context context, char **list)
{
@@ -414,12 +416,17 @@ hdb_list_builtin(krb5_context context, char **list)
if (h->create == NULL) {
struct cb_s cb_ctx;
char *f;
char *sym;
struct krb5_plugin_data hdb_plugin_data;
hdb_plugin_data.module = "krb5";
hdb_plugin_data.min_version = HDB_INTERFACE_VERSION;
hdb_plugin_data.deps = hdb_plugin_deps;
hdb_plugin_data.get_instance = hdb_get_instance;
/* Try loading the plugin */
if (asprintf(&f, "%sfoo", h->prefix) == -1)
f = NULL;
if ((sym = make_sym(h->prefix)) == NULL) {
if ((hdb_plugin_data.name = make_sym(h->prefix)) == NULL) {
free(buf);
free(f);
return krb5_enomem(context);
@@ -427,11 +434,10 @@ hdb_list_builtin(krb5_context context, char **list)
cb_ctx.filename = f;
cb_ctx.residual = NULL;
cb_ctx.h = NULL;
(void)_krb5_plugin_run_f(context, "krb5", sym,
HDB_INTERFACE_VERSION, 0, &cb_ctx,
callback);
(void)_krb5_plugin_run_f(context, &hdb_plugin_data, 0,
&cb_ctx, callback);
free(f);
free(sym);
free(rk_UNCONST(hdb_plugin_data.name));
if (cb_ctx.h == NULL || cb_ctx.h->create == NULL)
continue;
}
@@ -483,17 +489,35 @@ hdb_create(krb5_context context, HDB **db, const char *filename)
cb_ctx.filename = filename;
if (cb_ctx.h == NULL || cb_ctx.h->create == NULL) {
char *sym;
struct krb5_plugin_data hdb_plugin_data;
if ((sym = make_sym(filename)) == NULL)
hdb_plugin_data.module = "krb5";
hdb_plugin_data.min_version = HDB_INTERFACE_VERSION;
hdb_plugin_data.deps = hdb_plugin_deps;
hdb_plugin_data.get_instance = hdb_get_instance;
if ((hdb_plugin_data.name = make_sym(filename)) == NULL)
return krb5_enomem(context);
(void)_krb5_plugin_run_f(context, "krb5", sym, HDB_INTERFACE_VERSION,
(void)_krb5_plugin_run_f(context, &hdb_plugin_data,
0, &cb_ctx, callback);
free(sym);
free(rk_UNCONST(hdb_plugin_data.name));
}
if (cb_ctx.h == NULL)
krb5_errx(context, 1, "No database support for %s", cb_ctx.filename);
return (*cb_ctx.h->create)(context, db, cb_ctx.residual);
}
uintptr_t
hdb_get_instance(const char *libname)
{
static const char *instance = "libhdb";
if (strcmp(libname, "hdb") == 0)
return (uintptr_t)instance;
else if (strcmp(libname, "krb5") == 0)
return krb5_get_instance(libname);
return 0;
}

View File

@@ -31,6 +31,7 @@
* SUCH DAMAGE.
*/
#include "krb5_locl.h"
#include "hdb_locl.h"
struct hx509_certs_data;
@@ -45,7 +46,6 @@ struct _krb5_key_data;
struct _krb5_encryption_type;
struct _krb5_key_type;
#include <pkinit_asn1.h>
#include <krb5-private.h>
#include <base64.h>
/*

View File

@@ -43,6 +43,7 @@ EXPORTS
hdb_generate_key_set_password
hdb_generate_key_set_password_with_ks_tuple
hdb_get_dbinfo
hdb_get_instance
hdb_init_db
hdb_interface_version DATA
hdb_key2principal

View File

@@ -46,6 +46,7 @@ HEIMDAL_HDB_1.0 {
hdb_generate_key_set_password;
hdb_generate_key_set_password_with_ks_tuple;
hdb_get_dbinfo;
hdb_get_instance;
hdb_init_db;
hdb_key2principal;
hdb_kvno2keys;

View File

@@ -3,6 +3,7 @@
include $(top_srcdir)/Makefile.am.common
libkadm5srv_la_CPPFLAGS = -I$(srcdir)/../krb5
libkadm5clnt_la_CPPFLAGS = -I$(srcdir)/../krb5
lib_LTLIBRARIES = libkadm5srv.la libkadm5clnt.la
libkadm5srv_la_LDFLAGS = -version-info 8:1:0
@@ -34,6 +35,7 @@ libkadm5clnt_la_LIBADD = \
libexec_PROGRAMS = ipropd-master ipropd-slave
default_keys_SOURCES = default_keys.c
default_keys_CPPFLAGS = -I$(srcdir)/../krb5
kadm5includedir = $(includedir)/kadm5
buildkadm5include = $(buildinclude)/kadm5
@@ -130,8 +132,10 @@ dist_iprop_log_SOURCES = iprop-log.c
nodist_iprop_log_SOURCES = iprop-commands.c
ipropd_master_SOURCES = ipropd_master.c ipropd_common.c iprop.h kadm5_locl.h
ipropd_master_CPPFLAGS = -I$(srcdir)/../krb5
ipropd_slave_SOURCES = ipropd_slave.c ipropd_common.c iprop.h kadm5_locl.h
ipropd_slave_CPPFLAGS = -I$(srcdir)/../krb5
man_MANS = kadm5_pwcheck.3 iprop.8 iprop-log.8
@@ -146,6 +150,7 @@ LDADD = \
$(LIB_dlopen) \
$(LIB_pidfile)
iprop_log_LDADD = \
libkadm5srv.la \
$(top_builddir)/lib/hdb/libhdb.la \
@@ -159,6 +164,7 @@ iprop_log_LDADD = \
$(LIB_dlopen) \
$(LIB_pidfile)
iprop_log_CPPFLAGS = -I$(srcdir)/../krb5
iprop-commands.c iprop-commands.h: iprop-commands.in
$(SLC) $(srcdir)/iprop-commands.in
@@ -182,6 +188,7 @@ ALL_OBJECTS += $(ipropd_slave_OBJECTS)
ALL_OBJECTS += $(iprop_log_OBJECTS)
ALL_OBJECTS += $(test_pw_quality_OBJECTS)
ALL_OBJECTS += $(sample_passwd_check_la_OBJECTS)
ALL_OBJECTS += $(sample_hook_la_OBJECTS)
ALL_OBJECTS += $(default_keys_OBJECTS)
$(ALL_OBJECTS): $(srcdir)/kadm5-protos.h $(srcdir)/kadm5-private.h

View File

@@ -268,10 +268,10 @@ EXPORTS
<<
$(DLLPREP_NODIST)
$(OBJ)\sample_hook.dll: $(OBJ)\sample_hook.obj $(LIBHEIMDAL)
$(OBJ)\sample_hook.dll: $(OBJ)\sample_hook.obj $(LIBKADM5SRV) $(LIBHEIMDAL)
$(DLLGUILINK) /DEF:<<
EXPORTS
kadm5_hook_init
kadm5_hook_plugin_load
<<
$(DLLPREP_NODIST)

View File

@@ -35,6 +35,42 @@
RCSID("$Id$");
struct chpass_principal_hook_ctx {
kadm5_server_context *context;
enum kadm5_hook_stage stage;
krb5_error_code code;
krb5_const_principal princ;
uint32_t flags;
size_t n_ks_tuple;
krb5_key_salt_tuple *ks_tuple;
const char *password;
};
static krb5_error_code
chpass_principal_hook_cb(krb5_context context,
const void *hook,
void *hookctx,
void *userctx)
{
krb5_error_code ret;
const struct kadm5_hook_ftable *ftable = hook;
struct chpass_principal_hook_ctx *ctx = userctx;
ret = ftable->chpass(context, hookctx,
ctx->stage, ctx->code, ctx->princ,
ctx->flags, ctx->n_ks_tuple, ctx->ks_tuple,
ctx->password);
if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
_kadm5_s_set_hook_error_message(ctx->context, ret, "chpass",
hook, ctx->stage);
/* only pre-commit plugins can abort */
if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
ret = KRB5_PLUGIN_NO_HANDLE;
return ret;
}
static kadm5_ret_t
chpass_principal_hook(kadm5_server_context *context,
enum kadm5_hook_stage stage,
@@ -45,24 +81,22 @@ chpass_principal_hook(kadm5_server_context *context,
krb5_key_salt_tuple *ks_tuple,
const char *password)
{
krb5_error_code ret = 0;
size_t i;
krb5_error_code ret;
struct chpass_principal_hook_ctx ctx;
for (i = 0; i < context->num_hooks; i++) {
kadm5_hook_context *hook = context->hooks[i];
ctx.context = context;
ctx.stage = stage;
ctx.code = code;
ctx.princ = princ;
ctx.flags = flags;
ctx.n_ks_tuple = n_ks_tuple;
ctx.ks_tuple = ks_tuple;
ctx.password = password;
if (hook->hook->chpass != NULL) {
ret = hook->hook->chpass(context->context, hook->data,
stage, code, princ, flags,
n_ks_tuple, ks_tuple, password);
if (ret != 0) {
_kadm5_s_set_hook_error_message(context, ret, "chpass",
hook->hook, stage);
if (stage == KADM5_HOOK_STAGE_PRECOMMIT)
break;
}
}
}
ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
0, &ctx, chpass_principal_hook_cb);
if (ret == KRB5_PLUGIN_NO_HANDLE)
ret = 0;
return ret;
}

View File

@@ -102,6 +102,39 @@ create_principal(kadm5_server_context *context,
&ent->entry.created_by.principal);
}
struct create_principal_hook_ctx {
kadm5_server_context *context;
enum kadm5_hook_stage stage;
krb5_error_code code;
kadm5_principal_ent_t princ;
uint32_t mask;
const char *password;
};
static krb5_error_code
create_principal_hook_cb(krb5_context context,
const void *hook,
void *hookctx,
void *userctx)
{
krb5_error_code ret;
const struct kadm5_hook_ftable *ftable = hook;
struct create_principal_hook_ctx *ctx = userctx;
ret = ftable->create(context, hookctx,
ctx->stage, ctx->code, ctx->princ,
ctx->mask, ctx->password);
if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
_kadm5_s_set_hook_error_message(ctx->context, ret, "create",
hook, ctx->stage);
/* only pre-commit plugins can abort */
if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
ret = KRB5_PLUGIN_NO_HANDLE;
return ret;
}
static kadm5_ret_t
create_principal_hook(kadm5_server_context *context,
enum kadm5_hook_stage stage,
@@ -110,23 +143,20 @@ create_principal_hook(kadm5_server_context *context,
uint32_t mask,
const char *password)
{
krb5_error_code ret = 0;
size_t i;
krb5_error_code ret;
struct create_principal_hook_ctx ctx;
for (i = 0; i < context->num_hooks; i++) {
kadm5_hook_context *hook = context->hooks[i];
ctx.context = context;
ctx.stage = stage;
ctx.code = code;
ctx.princ = princ;
ctx.mask = mask;
ctx.password = password;
if (hook->hook->create != NULL) {
ret = hook->hook->create(context->context, hook->data,
stage, code, princ, mask, password);
if (ret != 0) {
_kadm5_s_set_hook_error_message(context, ret, "create",
hook->hook, stage);
if (stage == KADM5_HOOK_STAGE_PRECOMMIT)
break;
}
}
}
ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
0, &ctx, create_principal_hook_cb);
if (ret == KRB5_PLUGIN_NO_HANDLE)
ret = 0;
return ret;
}

View File

@@ -35,29 +35,54 @@
RCSID("$Id$");
struct delete_principal_hook_ctx {
kadm5_server_context *context;
enum kadm5_hook_stage stage;
krb5_error_code code;
krb5_const_principal princ;
};
static krb5_error_code
delete_principal_hook_cb(krb5_context context,
const void *hook,
void *hookctx,
void *userctx)
{
krb5_error_code ret;
const struct kadm5_hook_ftable *ftable = hook;
struct delete_principal_hook_ctx *ctx = userctx;
ret = ftable->delete(context, hookctx,
ctx->stage, ctx->code, ctx->princ);
if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
_kadm5_s_set_hook_error_message(ctx->context, ret, "delete",
hook, ctx->stage);
/* only pre-commit plugins can abort */
if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
ret = KRB5_PLUGIN_NO_HANDLE;
return ret;
}
static kadm5_ret_t
delete_principal_hook(kadm5_server_context *context,
enum kadm5_hook_stage stage,
krb5_error_code code,
krb5_const_principal princ)
{
krb5_error_code ret = 0;
size_t i;
krb5_error_code ret;
struct delete_principal_hook_ctx ctx;
for (i = 0; i < context->num_hooks; i++) {
kadm5_hook_context *hook = context->hooks[i];
ctx.context = context;
ctx.stage = stage;
ctx.code = code;
ctx.princ = princ;
if (hook->hook->delete != NULL) {
ret = hook->hook->delete(context->context, hook->data,
stage, code, princ);
if (ret != 0) {
_kadm5_s_set_hook_error_message(context, ret, "delete",
hook->hook, stage);
if (stage == KADM5_HOOK_STAGE_PRECOMMIT)
break;
}
}
}
ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
0, &ctx, delete_principal_hook_cb);
if (ret == KRB5_PLUGIN_NO_HANDLE)
ret = 0;
return ret;
}

View File

@@ -37,9 +37,12 @@
/*
* Each hook is called before the operation using KADM5_STAGE_PRECOMMIT and
* then after the operation using KADM5_STAGE_POSTCOMMIT. If the hook returns
* then after the operation using KADM5_STAGE_POSTCOMMIT. If the hook returns
* failure during precommit, the operation is aborted without changes to the
* database.
* database. All post-commit hook are invoked if the operation was attempted.
*
* Note that unlike libkrb5 plugins, returning success does not prevent other
* plugins being called (i.e. it is equivalent to KRB5_PLUGIN_NO_HANDLE).
*/
enum kadm5_hook_stage {
KADM5_HOOK_STAGE_PRECOMMIT,
@@ -49,25 +52,14 @@ enum kadm5_hook_stage {
#define KADM5_HOOK_FLAG_KEEPOLD 0x1 /* keep old password */
#define KADM5_HOOK_FLAG_CONDITIONAL 0x2 /* only change password if different */
/*
* libkadm5srv expects a symbol named kadm5_hook_init that must be a function
* of type kadm5_hook_init_t. The function will be called with the maximum
* version of the hook API supported by libkadm5; the plugin may return an
* earlier version.
*/
typedef struct kadm5_hook {
typedef struct kadm5_hook_ftable {
int version;
krb5_error_code (KRB5_CALLCONV *init)(krb5_context, void **data);
void (KRB5_CALLCONV *fini)(void *data);
const char *name;
uint32_t version;
const char *vendor;
/*
* Set this to krb5_init_context(): kadmin will use this to verify
* that we are linked against the same libkrb5.
*/
krb5_error_code (KRB5_CALLCONV *init_context)(krb5_context *);
void (KRB5_CALLCONV *fini)(krb5_context, void *data);
/*
* Hook functions; NULL functions are ignored. code is only valid on
* post-commit hooks and represents the result of the commit. Post-
@@ -128,12 +120,23 @@ typedef struct kadm5_hook {
size_t n_keys,
krb5_keyblock *keyblocks);
} kadm5_hook;
krb5_error_code (KRB5_CALLCONV *prune)(krb5_context context,
void *data,
enum kadm5_hook_stage stage,
krb5_error_code code,
krb5_const_principal princ,
int kvno);
} kadm5_hook_ftable;
/*
* libkadm5srv expects a symbol named kadm5_hook_plugin_load that must be a
* function of type kadm5_hook_plugin_load_t.
*/
typedef krb5_error_code
(KRB5_CALLCONV *kadm5_hook_init_t)(krb5_context context,
uint32_t kadm5_version_max,
const kadm5_hook **hook,
void **data);
(KRB5_CALLCONV *kadm5_hook_plugin_load_t)(krb5_context context,
krb5_get_instance_func_t *func,
size_t *n_hooks,
const kadm5_hook_ftable *const **hooks);
#endif /* !KADM5_HOOK_H */

View File

@@ -74,6 +74,7 @@
#include <netdb.h>
#endif
#include <fnmatch.h>
#include <krb5_locl.h>
#include "admin.h"
#include "kadm5_err.h"
#include <hdb.h>

View File

@@ -20,6 +20,7 @@ EXPORTS
kadm5_free_name_list
kadm5_free_policy_ent
kadm5_free_principal_ent
kadm5_get_instance
kadm5_get_policies
kadm5_get_policy
kadm5_get_principal

View File

@@ -35,6 +35,37 @@
RCSID("$Id$");
struct modify_principal_hook_ctx {
kadm5_server_context *context;
enum kadm5_hook_stage stage;
krb5_error_code code;
kadm5_principal_ent_t princ;
uint32_t mask;
};
static krb5_error_code
modify_principal_hook_cb(krb5_context context,
const void *hook,
void *hookctx,
void *userctx)
{
krb5_error_code ret;
const struct kadm5_hook_ftable *ftable = hook;
struct modify_principal_hook_ctx *ctx = userctx;
ret = ftable->modify(context, hookctx, ctx->stage,
ctx->code, ctx->princ, ctx->mask);
if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
_kadm5_s_set_hook_error_message(ctx->context, ret, "modify",
hook, ctx->stage);
/* only pre-commit plugins can abort */
if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
ret = KRB5_PLUGIN_NO_HANDLE;
return ret;
}
static kadm5_ret_t
modify_principal_hook(kadm5_server_context *context,
enum kadm5_hook_stage stage,
@@ -42,23 +73,19 @@ modify_principal_hook(kadm5_server_context *context,
kadm5_principal_ent_t princ,
uint32_t mask)
{
krb5_error_code ret = 0;
size_t i;
krb5_error_code ret;
struct modify_principal_hook_ctx ctx;
for (i = 0; i < context->num_hooks; i++) {
kadm5_hook_context *hook = context->hooks[i];
ctx.context = context;
ctx.stage = stage;
ctx.code = code;
ctx.princ = princ;
ctx.mask = mask;
if (hook->hook->modify != NULL) {
ret = hook->hook->modify(context->context, hook->data,
stage, code, princ, mask);
if (ret != 0) {
_kadm5_s_set_hook_error_message(context, ret, "modify",
hook->hook, stage);
if (stage == KADM5_HOOK_STAGE_PRECOMMIT)
break;
}
}
}
ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
0, &ctx, modify_principal_hook_cb);
if (ret == KRB5_PLUGIN_NO_HANDLE)
ret = 0;
return ret;
}

View File

@@ -71,8 +71,8 @@ struct kadm_func {
};
typedef struct kadm5_hook_context {
void *handle;
const kadm5_hook *hook;
void *dsohandle;
const kadm5_hook_ftable *hook;
void *data;
} kadm5_hook_context;
@@ -197,6 +197,8 @@ enum kadm_recover_mode {
#define KADMIN_APPL_VERSION "KADM0.1"
#define KADMIN_OLD_APPL_VERSION "KADM0.0"
extern struct krb5_plugin_data kadm5_hook_plugin_data;
#include "kadm5-private.h"
#endif /* __kadm5_privatex_h__ */

View File

@@ -34,6 +34,61 @@
RCSID("$Id$");
struct prune_principal_hook_ctx {
kadm5_server_context *context;
enum kadm5_hook_stage stage;
krb5_error_code code;
krb5_const_principal princ;
int kvno;
};
static krb5_error_code
prune_principal_hook_cb(krb5_context context,
const void *hook,
void *hookctx,
void *userctx)
{
krb5_error_code ret;
const struct kadm5_hook_ftable *ftable = hook;
struct prune_principal_hook_ctx *ctx = userctx;
ret = ftable->prune(context, hookctx,
ctx->stage, ctx->code, ctx->princ, ctx->kvno);
if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
_kadm5_s_set_hook_error_message(ctx->context, ret, "prune",
hook, ctx->stage);
/* only pre-commit plugins can abort */
if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
ret = KRB5_PLUGIN_NO_HANDLE;
return ret;
}
static kadm5_ret_t
prune_principal_hook(kadm5_server_context *context,
enum kadm5_hook_stage stage,
krb5_error_code code,
krb5_const_principal princ,
int kvno)
{
krb5_error_code ret;
struct prune_principal_hook_ctx ctx;
ctx.context = context;
ctx.stage = stage;
ctx.code = code;
ctx.princ = princ;
ctx.kvno = kvno;
ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
0, &ctx, prune_principal_hook_cb);
if (ret == KRB5_PLUGIN_NO_HANDLE)
ret = 0;
return ret;
}
kadm5_ret_t
kadm5_s_prune_principal(void *server_handle,
krb5_principal princ,
@@ -59,6 +114,11 @@ kadm5_s_prune_principal(void *server_handle,
if (ret)
goto out2;
ret = prune_principal_hook(context, KADM5_HOOK_STAGE_PRECOMMIT,
0, princ, kvno);
if (ret)
goto out3;
ret = hdb_prune_keys_kvno(context->context, &ent.entry, kvno);
if (ret)
goto out3;
@@ -69,6 +129,9 @@ kadm5_s_prune_principal(void *server_handle,
ret = kadm5_log_modify(context, &ent.entry, KADM5_KEY_DATA);
(void) prune_principal_hook(context, KADM5_HOOK_STAGE_POSTCOMMIT,
ret, princ, kvno);
out3:
hdb_free_entry(context->context, &ent);
out2:

View File

@@ -35,29 +35,54 @@
RCSID("$Id$");
struct randkey_principal_hook_ctx {
kadm5_server_context *context;
enum kadm5_hook_stage stage;
krb5_error_code code;
krb5_const_principal princ;
};
static krb5_error_code
randkey_principal_hook_cb(krb5_context context,
const void *hook,
void *hookctx,
void *userctx)
{
krb5_error_code ret;
const struct kadm5_hook_ftable *ftable = hook;
struct randkey_principal_hook_ctx *ctx = userctx;
ret = ftable->randkey(context, hookctx,
ctx->stage, ctx->code, ctx->princ);
if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
_kadm5_s_set_hook_error_message(ctx->context, ret, "randkey",
hook, ctx->stage);
/* only pre-commit plugins can abort */
if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
ret = KRB5_PLUGIN_NO_HANDLE;
return ret;
}
static kadm5_ret_t
randkey_principal_hook(kadm5_server_context *context,
enum kadm5_hook_stage stage,
krb5_error_code code,
krb5_const_principal princ)
enum kadm5_hook_stage stage,
krb5_error_code code,
krb5_const_principal princ)
{
krb5_error_code ret = 0;
size_t i;
krb5_error_code ret;
struct randkey_principal_hook_ctx ctx;
for (i = 0; i < context->num_hooks; i++) {
kadm5_hook_context *hook = context->hooks[i];
ctx.context = context;
ctx.stage = stage;
ctx.code = code;
ctx.princ = princ;
if (hook->hook->randkey != NULL) {
ret = hook->hook->randkey(context->context, hook->data,
stage, code, princ);
if (ret != 0) {
_kadm5_s_set_hook_error_message(context, ret, "randkey",
hook->hook, stage);
if (stage == KADM5_HOOK_STAGE_PRECOMMIT)
break;
}
}
}
ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
0, &ctx, randkey_principal_hook_cb);
if (ret == KRB5_PLUGIN_NO_HANDLE)
ret = 0;
return ret;
}

View File

@@ -35,6 +35,37 @@
RCSID("$Id$");
struct rename_principal_hook_ctx {
kadm5_server_context *context;
enum kadm5_hook_stage stage;
krb5_error_code code;
krb5_const_principal source, target;
};
static krb5_error_code
rename_principal_hook_cb(krb5_context context,
const void *hook,
void *hookctx,
void *userctx)
{
krb5_error_code ret;
const struct kadm5_hook_ftable *ftable = hook;
struct rename_principal_hook_ctx *ctx = userctx;
ret = ftable->rename(context, hookctx,
ctx->stage, ctx->code,
ctx->source, ctx->target);
if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
_kadm5_s_set_hook_error_message(ctx->context, ret, "rename",
hook, ctx->stage);
/* only pre-commit plugins can abort */
if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
ret = KRB5_PLUGIN_NO_HANDLE;
return ret;
}
static kadm5_ret_t
rename_principal_hook(kadm5_server_context *context,
enum kadm5_hook_stage stage,
@@ -42,23 +73,19 @@ rename_principal_hook(kadm5_server_context *context,
krb5_const_principal source,
krb5_const_principal target)
{
krb5_error_code ret = 0;
size_t i;
krb5_error_code ret;
struct rename_principal_hook_ctx ctx;
for (i = 0; i < context->num_hooks; i++) {
kadm5_hook_context *hook = context->hooks[i];
ctx.context = context;
ctx.stage = stage;
ctx.code = code;
ctx.source = source;
ctx.target = target;
if (hook->hook->rename != NULL) {
ret = hook->hook->rename(context->context, hook->data,
stage, code, source, target);
if (ret != 0) {
_kadm5_s_set_hook_error_message(context, ret, "rename",
hook->hook, stage);
if (stage == KADM5_HOOK_STAGE_PRECOMMIT)
break;
}
}
}
ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
0, &ctx, rename_principal_hook_cb);
if (ret == KRB5_PLUGIN_NO_HANDLE)
ret = 0;
return ret;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, AuriStor Inc.
* Copyright (c) 2018, AuriStor, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,9 +29,26 @@
*
*/
#include "kadm5_locl.h"
#include <assert.h>
static char sample_data[1];
#include <krb5.h>
#include <stdio.h>
#include <string.h>
#include "admin.h"
#include "kadm5-hook.h"
/*
* Sample kadm5 hook plugin that just logs when it is called. Install it
* somewhere and configure the path in the [kadmin] section of krb5.conf.
* e.g.
*
* [kadmin]
* plugin_dir = /usr/local/heimdal/lib/plugin/kadm5
*
*/
static char sample_data_1, sample_data_2;
static krb5_error_code
sample_log(krb5_context context,
@@ -43,28 +60,52 @@ sample_log(krb5_context context,
{
char *p = NULL;
krb5_error_code ret;
int which = 0;
if (data != sample_data)
return EINVAL;
if (code != 0 && stage == KADM5_HOOK_STAGE_PRECOMMIT)
return EINVAL;
/* verify we get called with the right contex tpointer */
if (data == &sample_data_1)
which = 1;
else if (data == &sample_data_2)
which = 2;
assert(which != 0);
/* code should always be zero on pre-commit */
assert(code == 0 || stage == KADM5_HOOK_STAGE_POSTCOMMIT);
if (princ)
ret = krb5_unparse_name(context, princ, &p);
krb5_warn(context, code, "sample_hook: %s %s hook princ '%s'", tag,
krb5_warn(context, code, "sample_hook_%d: %s %s hook princ '%s'", which, tag,
stage == KADM5_HOOK_STAGE_PRECOMMIT ? "pre-commit" : "post-commit",
p != NULL ? p : "<unknown>");
krb5_xfree(p);
/* returning zero and KRB5_PLUGIN_NO_HANDLE are the same for hook plugins */
return 0;
}
static krb5_error_code KRB5_CALLCONV
sample_init_1(krb5_context context, void **data)
{
*data = &sample_data_1;
krb5_warn(context, 0, "sample_hook_1: initializing");
return 0;
}
static krb5_error_code KRB5_CALLCONV
sample_init_2(krb5_context context, void **data)
{
*data = &sample_data_2;
krb5_warn(context, 0, "sample_hook_2: initializing");
return 0;
}
static void KRB5_CALLCONV
sample_fini(krb5_context context, void *data)
sample_fini(void *data)
{
krb5_warn(context, 0, "sample_hook: shutting down\n");
krb5_warn(NULL, 0, "sample_fini: finalizing");
}
static krb5_error_code KRB5_CALLCONV
@@ -150,36 +191,82 @@ sample_set_keys_hook(krb5_context context,
return sample_log(context, data, stage, "set_keys", code, princ);
}
static struct kadm5_hook sample_hook = {
"sample-hook",
static krb5_error_code KRB5_CALLCONV
sample_prune_hook(krb5_context context,
void *data,
enum kadm5_hook_stage stage,
krb5_error_code code,
krb5_const_principal princ,
int kvno)
{
return sample_log(context, data, stage, "prune", code, princ);
}
static const kadm5_hook_ftable sample_hook_1 = {
KADM5_HOOK_VERSION_V1,
"Heimdal",
krb5_init_context,
sample_init_1,
sample_fini,
"sample_hook_1",
"Heimdal",
sample_chpass_hook,
sample_create_hook,
sample_modify_hook,
sample_delete_hook,
sample_randkey_hook,
sample_rename_hook,
sample_set_keys_hook
sample_set_keys_hook,
sample_prune_hook,
};
static const kadm5_hook_ftable sample_hook_2 = {
KADM5_HOOK_VERSION_V1,
sample_init_2,
sample_fini,
"sample_hook_2",
"Heimdal",
sample_chpass_hook,
sample_create_hook,
sample_modify_hook,
sample_delete_hook,
sample_randkey_hook,
sample_rename_hook,
sample_set_keys_hook,
sample_prune_hook,
};
/* Arrays of pointers, because hooks may be different versions/sizes */
static const kadm5_hook_ftable *const sample_hooks[] = {
&sample_hook_1,
&sample_hook_2,
};
krb5_error_code
kadm5_hook_init(krb5_context context, uint32_t vers_max,
const kadm5_hook **hook, void **data);
kadm5_hook_plugin_load(krb5_context context,
krb5_get_instance_func_t *get_instance,
size_t *num_hooks,
const kadm5_hook_ftable *const **hooks);
krb5_error_code
kadm5_hook_init(krb5_context context, uint32_t vers_max,
const kadm5_hook **hook, void **data)
static uintptr_t
sample_hook_get_instance(const char *libname)
{
if (vers_max < KADM5_HOOK_VERSION_V1)
return EINVAL;
krb5_warn(context, 0, "sample_hook: init version %u\n", vers_max);
*hook = &sample_hook;
*data = sample_data;
if (strcmp(libname, "kadm5") == 0)
return kadm5_get_instance(libname);
else if (strcmp(libname, "krb5") == 0)
return krb5_get_instance(libname);
return 0;
}
krb5_error_code
kadm5_hook_plugin_load(krb5_context context,
krb5_get_instance_func_t *get_instance,
size_t *num_hooks,
const kadm5_hook_ftable *const **hooks)
{
*get_instance = sample_hook_get_instance;
*num_hooks = sizeof(sample_hooks) / sizeof(sample_hooks[0]);
*hooks = sample_hooks;
return 0;
}

View File

@@ -1,58 +1,55 @@
/*
* Copyright 2010
* The Board of Trustees of the Leland Stanford Junior University
* Copyright (c) 2018, AuriStor, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "kadm5_locl.h"
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#ifndef RTLD_NOW
# define RTLD_NOW 0
#endif
#ifndef RTLD_LOCAL
#define RTLD_LOCAL 0
#endif
#ifndef RTLD_GROUP
#define RTLD_GROUP 0
#endif
#endif /* HAVE_DLFCN_H */
static const char *kadm5_hook_plugin_deps[] = {
"kadm5",
"krb5",
NULL
};
struct krb5_plugin_data kadm5_hook_plugin_data = {
"kadm5",
"kadm5_hook",
KADM5_HOOK_VERSION_V1,
kadm5_hook_plugin_deps,
kadm5_get_instance
};
void
_kadm5_s_set_hook_error_message(kadm5_server_context *context,
krb5_error_code ret,
const char *op,
const struct kadm5_hook *hook,
const struct kadm5_hook_ftable *hook,
enum kadm5_hook_stage stage)
{
assert(ret != 0);
@@ -63,137 +60,38 @@ _kadm5_s_set_hook_error_message(kadm5_server_context *context,
stage == KADM5_HOOK_STAGE_PRECOMMIT ? "pre" : "post");
}
/*
* Load kadmin server hooks.
*/
kadm5_ret_t
_kadm5_s_init_hooks(kadm5_server_context *ctx)
{
krb5_context context = ctx->context;
char **hooks;
void *handle = NULL;
struct kadm5_hook_context *hook_context = NULL;
#ifdef HAVE_DLOPEN
struct kadm5_hook_context **tmp;
size_t i;
#endif
kadm5_ret_t ret = KADM5_BAD_SERVER_HOOK;
char **dirs;
hooks = krb5_config_get_strings(context, NULL,
"kadmin", "hooks", NULL);
if (hooks == NULL)
dirs = krb5_config_get_strings(context, NULL, "kadmin",
"plugin_dir", NULL);
if (dirs == NULL)
return 0;
#ifdef HAVE_DLOPEN
for (i = 0; hooks[i] != NULL; i++) {
const char *hookpath = hooks[i];
kadm5_hook_init_t hook_init;
const struct kadm5_hook *hook = NULL;
void *data = NULL;
_krb5_load_plugins(context, "kadm5", (const char **)dirs);
krb5_config_free_strings(dirs);
handle = dlopen(hookpath, RTLD_NOW | RTLD_LOCAL | RTLD_GROUP);
if (handle == NULL) {
krb5_warnx(context, "failed to open `%s': %s", hookpath, dlerror());
ret = KADM5_SERVER_HOOK_NOT_FOUND;
goto fail;
}
hook_init = dlsym(handle, "kadm5_hook_init");
if (hook_init == NULL) {
krb5_warnx(context, "didn't find kadm5_hook_init symbol in `%s': %s",
hookpath, dlerror());
ret = KADM5_BAD_SERVER_HOOK;
goto fail;
}
ret = hook_init(context, KADM5_HOOK_VERSION_V1, &hook, &data);
if (ret == 0 && hook == NULL)
ret = KADM5_BAD_SERVER_HOOK;
if (ret) {
krb5_warn(context, ret, "initialization of hook `%s' failed", hookpath);
goto fail;
}
if (hook->version < KADM5_HOOK_VERSION_V1)
ret = KADM5_OLD_SERVER_HOOK_VERSION;
else if (hook->version > KADM5_HOOK_VERSION_V1)
ret = KADM5_NEW_SERVER_HOOK_VERSION;
if (ret) {
krb5_warnx(context, "%s: version of loaded hook `%s' by vendor `%s' is %u"
" (supported versions are %u to %u)",
hookpath, hook->name, hook->vendor, hook->version,
KADM5_HOOK_VERSION_V1, KADM5_HOOK_VERSION_V1);
hook->fini(context, data);
goto fail;
}
if (hook->init_context != krb5_init_context) {
krb5_warnx(context, "%s: loaded hook `%s' by vendor `%s' (API version %u)"
"is not linked against this version of Heimdal",
hookpath, hook->name, hook->vendor, hook->version);
hook->fini(context, data);
goto fail;
}
hook_context = calloc(1, sizeof(*hook_context));
if (hook_context == NULL) {
ret = krb5_enomem(context);
hook->fini(context, data);
goto fail;
}
hook_context->handle = handle;
hook_context->hook = hook;
hook_context->data = data;
tmp = realloc(ctx->hooks, (ctx->num_hooks + 1) * sizeof(*tmp));
if (tmp == NULL) {
ret = krb5_enomem(context);
hook->fini(context, data);
goto fail;
}
ctx->hooks = tmp;
ctx->hooks[ctx->num_hooks] = hook_context;
hook_context = NULL;
ctx->num_hooks++;
krb5_warnx(context, "Loaded kadm5 hook `%s' by vendor `%s' (API version %u)",
hook->name, hook->vendor, hook->version);
}
krb5_config_free_strings(hooks);
return 0;
#else
krb5_warnx(context, "kadm5 hooks configured, but platform "
"does not support dynamic loading");
ret = KADM5_BAD_SERVER_HOOK;
goto fail;
#endif /* HAVE_DLOPEN */
fail:
_kadm5_s_free_hooks(ctx);
if (hook_context != NULL)
free(hook_context);
if (handle != NULL)
dlclose(handle);
krb5_config_free_strings(hooks);
return ret;
}
void
_kadm5_s_free_hooks(kadm5_server_context *ctx)
{
#ifdef HAVE_DLOPEN
size_t i;
for (i = 0; i < ctx->num_hooks; i++) {
if (ctx->hooks[i]->hook->fini != NULL)
ctx->hooks[i]->hook->fini(ctx->context, ctx->hooks[i]->data);
dlclose(ctx->hooks[i]->handle);
free(ctx->hooks[i]);
}
free(ctx->hooks);
ctx->hooks = NULL;
ctx->num_hooks = 0;
#endif /* HAVE_DLOPEN */
_krb5_unload_plugins(ctx->context, "kadm5");
}
uintptr_t
kadm5_get_instance(const char *libname)
{
static const char *instance = "libkadm5";
if (strcmp(libname, "kadm5") == 0)
return (uintptr_t)instance;
else if (strcmp(libname, "krb5") == 0)
return krb5_get_instance(libname);
return 0;
}

View File

@@ -33,6 +33,44 @@
#include "kadm5_locl.h"
struct setkey_principal_hook_ctx {
kadm5_server_context *context;
enum kadm5_hook_stage stage;
krb5_error_code code;
krb5_const_principal princ;
uint32_t flags;
size_t n_ks_tuple;
krb5_key_salt_tuple *ks_tuple;
size_t n_keys;
krb5_keyblock *keys;
};
static krb5_error_code
setkey_principal_hook_cb(krb5_context context,
const void *hook,
void *hookctx,
void *userctx)
{
krb5_error_code ret;
const struct kadm5_hook_ftable *ftable = hook;
struct setkey_principal_hook_ctx *ctx = userctx;
ret = ftable->set_keys(context, hookctx,
ctx->stage, ctx->code,
ctx->princ, ctx->flags,
ctx->n_ks_tuple, ctx->ks_tuple,
ctx->n_keys, ctx->keys);
if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
_kadm5_s_set_hook_error_message(ctx->context, ret, "setkey",
hook, ctx->stage);
/* only pre-commit plugins can abort */
if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
ret = KRB5_PLUGIN_NO_HANDLE;
return ret;
}
static kadm5_ret_t
setkey_principal_hook(kadm5_server_context *context,
enum kadm5_hook_stage stage,
@@ -44,25 +82,23 @@ setkey_principal_hook(kadm5_server_context *context,
size_t n_keys,
krb5_keyblock *keyblocks)
{
krb5_error_code ret = 0;
size_t i;
krb5_error_code ret;
struct setkey_principal_hook_ctx ctx;
for (i = 0; i < context->num_hooks; i++) {
kadm5_hook_context *hook = context->hooks[i];
ctx.context = context;
ctx.stage = stage;
ctx.code = code;
ctx.princ = princ;
ctx.flags = flags;
ctx.n_ks_tuple = n_ks_tuple;
ctx.ks_tuple = ks_tuple;
ctx.n_keys = n_keys;
ctx.keys = keyblocks;
if (hook->hook->set_keys != NULL) {
ret = hook->hook->set_keys(context->context, hook->data,
stage, code, princ,
flags, n_ks_tuple, ks_tuple,
n_keys, keyblocks);
if (ret != 0) {
_kadm5_s_set_hook_error_message(context, ret, "setkey",
hook->hook, stage);
if (stage == KADM5_HOOK_STAGE_PRECOMMIT)
break;
}
}
}
ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
0, &ctx, setkey_principal_hook_cb);
if (ret == KRB5_PLUGIN_NO_HANDLE)
ret = 0;
return ret;
}

View File

@@ -31,10 +31,14 @@
* SUCH DAMAGE.
*/
#include "kadm5_locl.h"
#include <string.h>
#include <stdlib.h>
#include <getarg.h>
RCSID("$Id$");
#include <roken.h>
#include <krb5.h>
#include "admin.h"
static int version_flag;
static int help_flag;

View File

@@ -23,6 +23,7 @@ HEIMDAL_KAMD5_SERVER_1.0 {
kadm5_free_name_list;
kadm5_free_policy_ent;
kadm5_free_principal_ent;
kadm5_get_instance;
kadm5_get_policy;
kadm5_get_policies;
kadm5_get_principal;

View File

@@ -80,6 +80,17 @@ plcallback(krb5_context context,
return locate->an2ln(plugctx, context, plctx->rule, plctx->aname, set_res, plctx);
}
static const char *an2ln_plugin_deps[] = { "krb5", NULL };
static struct krb5_plugin_data
an2ln_plugin_data = {
"krb5",
KRB5_PLUGIN_AN2LN,
KRB5_PLUGIN_AN2LN_VERSION_0,
an2ln_plugin_deps,
krb5_get_instance
};
static krb5_error_code
an2ln_plugin(krb5_context context, const char *rule, krb5_const_principal aname,
size_t lnsize, char *lname)
@@ -96,8 +107,8 @@ an2ln_plugin(krb5_context context, const char *rule, krb5_const_principal aname,
* really be no more than one plugin that can handle any given kind
* rule, so the effect should be deterministic anyways.
*/
ret = _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_AN2LN,
KRB5_PLUGIN_AN2LN_VERSION_0, 0, &ctx, plcallback);
ret = _krb5_plugin_run_f(context, &an2ln_plugin_data,
0, &ctx, plcallback);
if (ret != 0) {
heim_release(ctx.luser);
return ret;

View File

@@ -1589,3 +1589,4 @@ krb5_set_home_dir_access(krb5_context context, krb5_boolean allow)
return old;
}

View File

@@ -14,12 +14,22 @@ db_plugins_plcallback(krb5_context context, const void *plug, void *plugctx,
return 0;
}
static const char *db_plugin_deps[] = { "krb5", NULL };
static struct krb5_plugin_data
db_plugin_data = {
"krb5",
KRB5_PLUGIN_DB,
KRB5_PLUGIN_DB_VERSION_0,
db_plugin_deps,
krb5_get_instance
};
static void
db_plugins_init(void *arg)
{
krb5_context context = arg;
(void)_krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_DB,
KRB5_PLUGIN_DB_VERSION_0, 0, NULL,
(void)_krb5_plugin_run_f(context, &db_plugin_data, 0, NULL,
db_plugins_plcallback);
}

View File

@@ -57,11 +57,26 @@ associated header file, such as, for example,
.Va krb5plugin_kuserok_ftable
and a pointer to which is either registered via
.Xr krb5_plugin_register 3
or found in a shared object via a symbol lookup for the symbol name
defined in the associated header file (e.g., "kuserok" for the
or via a plugin load function exported by a shared object. Plugin load
functions should be named by concatenating the name defined in the associated
header file with the string "plugin_load" (e.g. "kuserok_plugin_load" for the
plugin for
.Xr krb5_kuserok 3
).
The plugin load function has the following signature:
.Bd -literal -offset indent
krb5_error_code
kuserok_plugin_load(krb5_context context,
krb5_get_instance_func_t *get_instance,
size_t *n_plugins,
const common_plugin_ftable *const **plugins);
.Ed
.Pp
The plugin should set the get_instance output parameter to the a function
that will return the instances of its library dependencies. The output
parameters n_plugins and plugins specify the number of plugins, and an array of
pointers to function tables, respectively.
.Ed
.Pp
The plugin structs for all plugin types always begin with the same three
common fields:
@@ -200,6 +215,31 @@ krb5plugin_an2ln_ftable an2ln = {
nouser_plug_fini,
nouser_plug_an2ln,
};
static const krb5plugin_an2ln_ftable *const plugins[] = {
&an2ln
};
static uintptr_t
an2ln_get_instance(const char *libname)
{
if (strcmp(libname, "krb5") == 0)
return krb5_get_instance(libname);
return 0;
}
krb5_error_code
an2ln_plugin_load(krb5_context context,
krb5_get_instance_func_t *get_instance,
size_t *num_plugins,
const krb5plugin_an2ln_ftable * const **pplugins)
{
*get_instance = an2ln_get_instance;
*num_plugins = sizeof(plugins) / sizeof(plugins[0]);
*pplugins = plugins;
return 0;
}
.Ed
.Pp
An example kuserok plugin that rejects all requests follows. (Note that
@@ -232,12 +272,38 @@ reject_plug_kuserok(void *plug_ctx, krb5_context context, const char *rule,
return 0;
}
krb5plugin_kuserok_ftable kuserok = {
static krb5plugin_kuserok_ftable kuserok = {
KRB5_PLUGIN_KUSEROK_VERSION_0,
reject_plug_init,
reject_plug_fini,
reject_plug_kuserok,
};
static const krb5plugin_kuserok_ftable *const plugins[] = {
&kuserok
};
static uintptr_t
kuserok_get_instance(const char *libname)
{
if (strcmp(libname, "krb5") == 0)
return krb5_get_instance(libname);
return 0;
}
krb5_error_code
kuserok_plugin_load(krb5_context context,
krb5_get_instance_func_t *get_instance,
size_t *num_plugins,
const krb5plugin_kuserok_ftable * const **pplugins)
{
*krb5_instance = kuserok_get_instance;
*num_plugins = sizeof(plugins) / sizeof(plugins[0]);
*pplugins = plugins;
return 0;
}
.Ed
.Sh SEE ALSO
.Xr krb5_plugin_register 3

View File

@@ -873,14 +873,16 @@ typedef krb5_error_code
(KRB5_CALLCONV * krb5_sendto_ctx_func)(krb5_context, krb5_sendto_ctx, void *,
const krb5_data *, int *);
struct krb5_plugin;
enum krb5_plugin_type {
PLUGIN_TYPE_DATA = 1,
PLUGIN_TYPE_FUNC
PLUGIN_TYPE_FUNC /* no longer supported */
};
#define KRB5_PLUGIN_INVOKE_ALL 1
typedef uintptr_t
(KRB5_CALLCONV *krb5_get_instance_func_t)(const char *);
struct credentials; /* this is to keep the compiler happy */
struct getargs;
struct sockaddr;

View File

@@ -134,6 +134,7 @@ struct ContentInfo;
struct AlgorithmIdentifier;
typedef struct krb5_pk_init_ctx_data *krb5_pk_init_ctx;
struct krb5_dh_moduli;
struct krb5_plugin_data;
/* v4 glue */
struct _krb5_krb_auth_data;
@@ -393,4 +394,12 @@ struct krb5_pk_init_ctx_data {
# define ISPATHSEP(x) (x == '/')
#endif
struct krb5_plugin_data {
const char *module;
const char *name;
int min_version;
const char **deps;
krb5_get_instance_func_t get_instance;
};
#endif /* __KRB5_LOCL_H__ */

View File

@@ -674,6 +674,17 @@ plcallback(krb5_context context,
return KRB5_PLUGIN_NO_HANDLE;
}
static const char *locate_plugin_deps[] = { "krb5", NULL };
static struct krb5_plugin_data
locate_plugin_data = {
"krb5",
KRB5_PLUGIN_LOCATE,
KRB5_PLUGIN_LOCATE_VERSION_0,
locate_plugin_deps,
krb5_get_instance
};
static void
plugin_get_hosts(krb5_context context,
struct krb5_krbhst_data *kd,
@@ -684,8 +695,7 @@ plugin_get_hosts(krb5_context context,
if (_krb5_homedir_access(context))
ctx.flags |= KRB5_PLF_ALLOW_HOMEDIR;
_krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_LOCATE,
KRB5_PLUGIN_LOCATE_VERSION_0,
_krb5_plugin_run_f(context, &locate_plugin_data,
0, &ctx, plcallback);
}

View File

@@ -455,6 +455,17 @@ krb5_kuserok(krb5_context context,
}
static const char *kuserok_plugin_deps[] = { "krb5", NULL };
static struct krb5_plugin_data
kuserok_plugin_data = {
"krb5",
KRB5_PLUGIN_KUSEROK,
KRB5_PLUGIN_KUSEROK_VERSION_0,
kuserok_plugin_deps,
krb5_get_instance
};
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
_krb5_kuserok(krb5_context context,
krb5_principal principal,
@@ -515,9 +526,8 @@ _krb5_kuserok(krb5_context context,
for (n = 0; rules[n]; n++) {
ctx.rule = rules[n];
ret = _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_KUSEROK,
KRB5_PLUGIN_KUSEROK_VERSION_0, 0,
&ctx, plcallback);
ret = _krb5_plugin_run_f(context, &kuserok_plugin_data,
0, &ctx, plcallback);
if (ret != KRB5_PLUGIN_NO_HANDLE)
goto out;
}

View File

@@ -366,6 +366,7 @@ EXPORTS
krb5_get_init_creds_opt_set_tkt_life
krb5_get_init_creds_opt_set_win2k
krb5_get_init_creds_password
krb5_get_instance
krb5_get_kdc_cred
krb5_get_kdc_sec_offset
krb5_get_krb524hst
@@ -756,6 +757,10 @@ EXPORTS
_krb5_build_authenticator
_krb5_kt_client_default_name
; Shared with libkadm5
_krb5_load_plugins
_krb5_unload_plugins
; Shared with libkdc
_krb5_AES_SHA1_string_to_default_iterator
_krb5_AES_SHA2_string_to_default_iterator
@@ -806,8 +811,6 @@ EXPORTS
; Recent additions
krb5_cc_type_dcc;
krb5_dcc_ops;
_krb5_plugin_find;
_krb5_plugin_free;
_krb5_expand_path_tokensv;
_krb5_find_capath;
_krb5_free_capath;

View File

@@ -58,13 +58,24 @@ cc_plugin_register_to_context(krb5_context context, const void *plug, void *plug
return KRB5_PLUGIN_NO_HANDLE;
}
static const char *ccache_plugin_deps[] = { "krb5", NULL };
static struct krb5_plugin_data
ccache_plugin_data = {
"krb5",
KRB5_PLUGIN_CCACHE,
0,
ccache_plugin_deps,
krb5_get_instance
};
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_load_ccache_plugins(krb5_context context)
{
krb5_error_code userctx = 0;
(void)_krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_CCACHE,
0, 0, &userctx, cc_plugin_register_to_context);
(void)_krb5_plugin_run_f(context, &ccache_plugin_data, 0,
&userctx, cc_plugin_register_to_context);
return userctx;
}

View File

@@ -3,6 +3,8 @@
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Portions Copyright (c) 2018 AuriStor, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -38,29 +40,159 @@
#endif
#include <dirent.h>
/*
* Definitions:
*
* module - a category of plugin module, identified by subsystem
* (typically "krb5")
* dso - a library for a module containing a map of plugin
* types to plugins (e.g. "service_locator")
* plugin - a set of callbacks and state that follows the
* common plugin module definition (version, init, fini)
*
* Obviously it would have been clearer to use the term "module" rather than
* "DSO" given there is an internal "DSO", but "module" was already taken...
*
* modules := { module: dsos }
* dsos := { path, dsohandle, plugins-by-name }
* plugins-by-name := { plugin-name: [plug] }
* plug := { ftable, ctx }
*
* Some existing plugin consumers outside libkrb5 use the "krb5" module
* namespace, but going forward the module should match the consumer library
* name (e.g. libhdb should use the "hdb" module rather than "krb5").
*/
/* global module use, use copy_modules() accessor to access */
static heim_dict_t __modules;
static HEIMDAL_MUTEX modules_mutex = HEIMDAL_MUTEX_INITIALIZER;
static void
copy_modules_once(void *context)
{
heim_dict_t *modules = (heim_dict_t *)context;
*modules = heim_dict_create(11);
heim_assert(*modules, "plugin modules array allocation failure");
}
/* returns global modules list, refcount +1 */
static heim_dict_t
copy_modules(void)
{
static heim_base_once_t modules_once = HEIM_BASE_ONCE_INIT;
heim_base_once_f(&modules_once, &__modules, copy_modules_once);
return heim_retain(__modules);
}
/* returns named module, refcount +1 */
static heim_dict_t
copy_module(const char *name)
{
heim_string_t module_name = heim_string_create(name);
heim_dict_t modules = copy_modules();
heim_dict_t module;
module = heim_dict_copy_value(modules, module_name);
if (module == NULL) {
module = heim_dict_create(11);
heim_dict_set_value(modules, module_name, module);
}
heim_release(modules);
heim_release(module_name);
return module;
}
/* DSO helpers */
struct krb5_dso {
heim_string_t path;
heim_dict_t plugins_by_name;
void *dsohandle;
};
static void
dso_dealloc(void *ptr)
{
struct krb5_dso *p = ptr;
heim_release(p->path);
heim_release(p->plugins_by_name);
#ifdef HAVE_DLOPEN
if (p->dsohandle)
dlclose(p->dsohandle);
#endif
}
/* returns internal "DSO" for name, refcount +1 */
static struct krb5_dso *
copy_internal_dso(const char *name)
{
heim_string_t dso_name = HSTR("__HEIMDAL_INTERNAL_DSO__");
heim_dict_t module = copy_module(name);
struct krb5_dso *dso;
if (module == NULL)
return NULL;
dso = heim_dict_copy_value(module, dso_name);
if (dso == NULL) {
dso = heim_alloc(sizeof(*dso), "krb5-dso", dso_dealloc);
dso->path = dso_name;
dso->plugins_by_name = heim_dict_create(11);
heim_dict_set_value(module, dso_name, dso);
}
heim_release(module);
return dso;
}
/*
* All plugin function tables extend the following structure.
*/
typedef struct common_plugin_ftable_desc {
int version;
krb5_error_code (*init)(krb5_context, void **);
void (*fini)(void *);
} common_plugin_ftable;
struct krb5_plugin {
common_plugin_ftable *ftable;
void *ctx;
};
static void
plugin_free(void *ptr)
{
struct krb5_plugin *pl = ptr;
if (pl->ftable && pl->ftable->fini)
pl->ftable->fini(pl->ctx);
}
struct krb5_plugin_register_ctx {
void *symbol;
struct krb5_plugin *next;
int is_dup;
};
struct plugin {
enum { DSO, SYMBOL } type;
union {
struct {
char *path;
void *dsohandle;
} dso;
struct {
enum krb5_plugin_type type;
char *name;
char *symbol;
} symbol;
} u;
struct plugin *next;
};
static void
plugin_register_check_dup(heim_object_t value, void *ctx, int *stop)
{
struct krb5_plugin_register_ctx *pc = ctx;
struct krb5_plugin *pl = value;
static HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER;
static struct plugin *registered = NULL;
if (pl->ftable == pc->symbol) {
pc->is_dup = 1;
*stop = 1;
}
}
/**
* Register a plugin symbol name of specific type.
@@ -80,147 +212,67 @@ krb5_plugin_register(krb5_context context,
const char *name,
void *symbol)
{
struct plugin *e;
HEIMDAL_MUTEX_lock(&plugin_mutex);
/* check for duplicates */
for (e = registered; e != NULL; e = e->next) {
if (e->type == SYMBOL &&
strcmp(e->u.symbol.name, name) == 0 &&
e->u.symbol.type == type && e->u.symbol.symbol == symbol) {
HEIMDAL_MUTEX_unlock(&plugin_mutex);
return 0;
}
}
e = calloc(1, sizeof(*e));
if (e == NULL) {
HEIMDAL_MUTEX_unlock(&plugin_mutex);
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
e->type = SYMBOL;
e->u.symbol.type = type;
e->u.symbol.name = strdup(name);
if (e->u.symbol.name == NULL) {
HEIMDAL_MUTEX_unlock(&plugin_mutex);
free(e);
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
e->u.symbol.symbol = symbol;
e->next = registered;
registered = e;
HEIMDAL_MUTEX_unlock(&plugin_mutex);
return 0;
}
static krb5_error_code
add_symbol(krb5_context context, struct krb5_plugin **list, void *symbol)
{
struct krb5_plugin *e;
e = calloc(1, sizeof(*e));
if (e == NULL) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
e->symbol = symbol;
e->next = *list;
*list = e;
return 0;
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_plugin_find(krb5_context context,
enum krb5_plugin_type type,
const char *name,
struct krb5_plugin **list)
{
struct plugin *e;
krb5_error_code ret;
heim_array_t plugins;
heim_string_t hname;
struct krb5_dso *dso;
struct krb5_plugin_register_ctx ctx;
*list = NULL;
ctx.symbol = symbol;
ctx.is_dup = 0;
HEIMDAL_MUTEX_lock(&plugin_mutex);
for (ret = 0, e = registered; e != NULL; e = e->next) {
switch(e->type) {
case DSO: {
void *sym;
if (e->u.dso.dsohandle == NULL)
continue;
sym = dlsym(e->u.dso.dsohandle, name);
if (sym)
ret = add_symbol(context, list, sym);
break;
}
case SYMBOL:
if (strcmp(e->u.symbol.name, name) == 0 && e->u.symbol.type == type)
ret = add_symbol(context, list, e->u.symbol.symbol);
break;
}
if (ret) {
_krb5_plugin_free(*list);
*list = NULL;
}
/*
* It's not clear that PLUGIN_TYPE_FUNC was ever used or supported. It likely
* would have caused _krb5_plugin_run_f() to crash as the previous implementation
* assumed PLUGIN_TYPE_DATA.
*/
if (type != PLUGIN_TYPE_DATA) {
krb5_warnx(context, "krb5_plugin_register: PLUGIN_TYPE_DATA no longer supported");
return EINVAL;
}
HEIMDAL_MUTEX_unlock(&plugin_mutex);
if (ret)
return ret;
HEIMDAL_MUTEX_lock(&modules_mutex);
if (*list == NULL) {
krb5_set_error_message(context, ENOENT, "Did not find a plugin for %s", name);
return ENOENT;
dso = copy_internal_dso("krb5");
hname = heim_string_create(name);
plugins = heim_dict_copy_value(dso->plugins_by_name, hname);
if (plugins != NULL)
heim_array_iterate_f(plugins, &ctx, plugin_register_check_dup);
else {
plugins = heim_array_create();
heim_dict_set_value(dso->plugins_by_name, hname, plugins);
}
return 0;
if (!ctx.is_dup) {
/* Note: refactored plugin API only supports common plugin layout */
struct krb5_plugin *pl;
pl = heim_alloc(sizeof(*pl), "krb5-plugin", plugin_free);
if (pl == NULL) {
ret = krb5_enomem(context);
} else {
pl->ftable = symbol;
ret = pl->ftable->init(context, &pl->ctx);
if (ret == 0) {
heim_array_append_value(plugins, pl);
_krb5_debug(context, 5, "Registered %s plugin", name);
}
heim_release(pl);
}
} else
ret = 0; /* ignore duplicates to match previous behavior */
HEIMDAL_MUTEX_unlock(&modules_mutex);
heim_release(dso);
heim_release(hname);
heim_release(plugins);
return ret;
}
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_plugin_free(struct krb5_plugin *list)
{
struct krb5_plugin *next;
while (list) {
next = list->next;
free(list);
list = next;
}
}
/*
* module - dict of {
* ModuleName = [
* plugin = object{
* array = { ptr, ctx }
* }
* ]
* }
*/
static heim_dict_t modules;
struct plugin2 {
heim_string_t path;
void *dsohandle;
heim_dict_t names;
};
#ifdef HAVE_DLOPEN
static void
plug_dealloc(void *ptr)
{
struct plugin2 *p = ptr;
heim_release(p->path);
heim_release(p->names);
if (p->dsohandle)
dlclose(p->dsohandle);
}
static char *
resolve_origin(const char *di)
{
@@ -277,7 +329,7 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
{
#ifdef HAVE_DLOPEN
heim_string_t s = heim_string_create(name);
heim_dict_t module;
heim_dict_t module, modules;
struct dirent *entry;
krb5_error_code ret;
const char **di;
@@ -292,27 +344,23 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
plugin_prefix_len = (plugin_prefix ? strlen(plugin_prefix) : 0);
#endif
HEIMDAL_MUTEX_lock(&plugin_mutex);
HEIMDAL_MUTEX_lock(&modules_mutex);
if (modules == NULL) {
modules = heim_dict_create(11);
if (modules == NULL) {
HEIMDAL_MUTEX_unlock(&plugin_mutex);
return;
}
}
modules = copy_modules();
module = heim_dict_copy_value(modules, s);
if (module == NULL) {
module = heim_dict_create(11);
if (module == NULL) {
HEIMDAL_MUTEX_unlock(&plugin_mutex);
HEIMDAL_MUTEX_unlock(&modules_mutex);
heim_release(s);
heim_release(modules);
return;
}
heim_dict_set_value(modules, s, module);
}
heim_release(s);
heim_release(modules);
for (di = paths; *di != NULL; di++) {
free(dirname);
@@ -328,7 +376,7 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
char *n = entry->d_name;
char *path = NULL;
heim_string_t spath;
struct plugin2 *p;
struct krb5_dso *p;
/* skip . and .. */
if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
@@ -377,13 +425,12 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
/* check if already cached */
p = heim_dict_copy_value(module, spath);
if (p == NULL) {
p = heim_alloc(sizeof(*p), "krb5-plugin", plug_dealloc);
p = heim_alloc(sizeof(*p), "krb5-dso", dso_dealloc);
if (p)
p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY);
p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY|RTLD_GROUP);
if (p && p->dsohandle) {
p->path = heim_retain(spath);
p->names = heim_dict_create(11);
p->plugins_by_name = heim_dict_create(11);
heim_dict_set_value(module, spath, p);
}
}
@@ -394,7 +441,7 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
closedir(d);
}
free(dirname);
HEIMDAL_MUTEX_unlock(&plugin_mutex);
HEIMDAL_MUTEX_unlock(&modules_mutex);
heim_release(module);
#ifdef _WIN32
if (plugin_prefix)
@@ -409,42 +456,24 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_unload_plugins(krb5_context context, const char *name)
{
HEIMDAL_MUTEX_lock(&plugin_mutex);
heim_string_t sname = heim_string_create(name);
heim_dict_t modules;
HEIMDAL_MUTEX_lock(&modules_mutex);
modules = copy_modules();
heim_dict_delete_key(modules, sname);
HEIMDAL_MUTEX_unlock(&modules_mutex);
heim_release(modules);
modules = NULL;
HEIMDAL_MUTEX_unlock(&plugin_mutex);
}
/*
*
*/
struct common_plugin_method {
int version;
krb5_error_code (*init)(krb5_context, void **);
void (*fini)(void *);
};
struct plug {
void *dataptr;
void *ctx;
};
static void
plug_free(void *ptr)
{
struct plug *pl = ptr;
if (pl->dataptr) {
struct common_plugin_method *cpm = pl->dataptr;
cpm->fini(pl->ctx);
}
heim_release(sname);
}
struct iter_ctx {
krb5_context context;
heim_string_t n;
const char *name;
int min_version;
struct krb5_plugin_data *caller;
int flags;
heim_array_t result;
krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *);
@@ -452,48 +481,213 @@ struct iter_ctx {
krb5_error_code ret;
};
#ifdef HAVE_DLOPEN
/*
* Add plugin from a DSO that exports the plugin structure directly. This is
* provided for backwards compatibility with prior versions of Heimdal, but it
* does not allow a module to export multiple plugins, nor does it allow
* instance validation.
*/
static heim_array_t
add_dso_plugin_struct(krb5_context context,
const char *dsopath,
void *dsohandle,
const char *name)
{
krb5_error_code ret;
common_plugin_ftable *cpm;
struct krb5_plugin *pl;
heim_array_t plugins;
if (dsohandle == NULL)
return NULL;
/* suppress error here because we may be looking for a different plugin type */
cpm = dlsym(dsohandle, name);
if (cpm == NULL)
return NULL;
krb5_warnx(context, "plugin %s uses deprecated loading mechanism", dsopath);
pl = heim_alloc(sizeof(*pl), "krb5-plugin", plugin_free);
ret = cpm->init(context, &pl->ctx);
if (ret) {
krb5_warn(context, ret, "plugin %s failed to initialize", dsopath);
heim_release(pl);
return NULL;
}
pl->ftable = cpm;
plugins = heim_array_create();
heim_array_append_value(plugins, pl);
heim_release(pl);
return plugins;
}
typedef krb5_error_code
(KRB5_CALLCONV *krb5_plugin_load_t)(krb5_context context,
krb5_get_instance_func_t *func,
size_t *n_ftables,
const common_plugin_ftable *const **ftables);
static krb5_boolean
validate_plugin_deps(krb5_context context,
struct krb5_plugin_data *caller,
const char *dsopath,
krb5_get_instance_func_t get_instance)
{
size_t i;
if (get_instance == NULL) {
krb5_warnx(context, "plugin %s omitted instance callback",
dsopath);
return FALSE;
}
for (i = 0; caller->deps[i] != NULL; i++) {
uintptr_t heim_instance, plugin_instance;
heim_instance = caller->get_instance(caller->deps[i]);
plugin_instance = get_instance(caller->deps[i]);
if (heim_instance == 0 || plugin_instance == 0)
continue;
if (heim_instance != plugin_instance) {
krb5_warnx(context, "plugin %s library %s linked against different "
"version of Heimdal (got %zu, us %zu)",
dsopath, caller->deps[i],
plugin_instance, heim_instance);
return FALSE;
}
_krb5_debug(context, 10, "Validated plugin library dependency %s for %s",
caller->deps[i], dsopath);
}
return TRUE;
}
/*
* New interface from Heimdal 8 where a DSO can export a load function
* that can return both a libkrb5 instance identifier along with an array
* of plugins.
*/
static heim_array_t
add_dso_plugins_load_fn(krb5_context context,
struct krb5_plugin_data *caller,
const char *dsopath,
void *dsohandle)
{
krb5_error_code ret;
heim_array_t plugins;
krb5_plugin_load_t load_fn;
char *sym;
size_t i;
krb5_get_instance_func_t get_instance;
size_t n_ftables;
const common_plugin_ftable *const *ftables;
if (asprintf(&sym, "%s_plugin_load", caller->name) == -1)
return NULL;
/* suppress error here because we may be looking for a different plugin type */
load_fn = dlsym(dsohandle, sym);
free(sym);
if (load_fn == NULL)
return NULL;
ret = load_fn(context, &get_instance, &n_ftables, &ftables);
if (ret) {
krb5_warn(context, ret, "plugin %s failed to load", dsopath);
return NULL;
}
if (!validate_plugin_deps(context, caller, dsopath, get_instance))
return NULL;
plugins = heim_array_create();
for (i = 0; i < n_ftables; i++) {
const common_plugin_ftable *const cpm = ftables[i];
struct krb5_plugin *pl;
pl = heim_alloc(sizeof(*pl), "krb5-plugin", plugin_free);
ret = cpm->init(context, &pl->ctx);
if (ret) {
krb5_warn(context, ret, "plugin %s[%zu] failed to initialize",
dsopath, i);
} else {
pl->ftable = rk_UNCONST(cpm);
heim_array_append_value(plugins, pl);
}
heim_release(pl);
}
return plugins;
}
#endif /* HAVE_DLOPEN */
static void
reduce_by_version(heim_object_t value, void *ctx, int *stop)
{
struct iter_ctx *s = ctx;
struct krb5_plugin *pl = value;
if (pl->ftable && pl->ftable->version >= s->caller->min_version)
heim_array_append_value(s->result, pl);
}
static void
search_modules(heim_object_t key, heim_object_t value, void *ctx)
{
struct iter_ctx *s = ctx;
struct plugin2 *p = value;
struct plug *pl = heim_dict_copy_value(p->names, s->n);
struct common_plugin_method *cpm;
struct krb5_dso *p = value;
heim_array_t plugins = heim_dict_copy_value(p->plugins_by_name, s->n);
if (pl == NULL) {
if (p->dsohandle == NULL)
return;
#ifdef HAVE_DLOPEN
if (plugins == NULL && p->dsohandle) {
const char *path = heim_string_get_utf8(p->path);
pl = heim_alloc(sizeof(*pl), "struct-plug", plug_free);
cpm = pl->dataptr = dlsym(p->dsohandle, s->name);
if (cpm) {
int ret;
ret = cpm->init(s->context, &pl->ctx);
if (ret)
cpm = pl->dataptr = NULL;
plugins = add_dso_plugins_load_fn(s->context,
s->caller,
path,
p->dsohandle);
if (plugins == NULL) {
/* fallback to loading structure directly */
plugins = add_dso_plugin_struct(s->context, path,
p->dsohandle, s->caller->name);
}
if (plugins) {
heim_dict_set_value(p->plugins_by_name, s->n, plugins);
_krb5_debug(s->context, 5, "Loaded %zu %s %s plugin%s from %s",
heim_array_get_length(plugins),
s->caller->module, s->caller->name,
heim_array_get_length(plugins) > 1 ? "s" : "",
path);
}
heim_dict_set_value(p->names, s->n, pl);
} else {
cpm = pl->dataptr;
}
#endif /* HAVE_DLOPEN */
if (cpm && cpm->version >= s->min_version)
heim_array_append_value(s->result, pl);
heim_release(pl);
if (plugins) {
heim_array_iterate_f(plugins, s, reduce_by_version);
heim_release(plugins);
}
}
static void
eval_results(heim_object_t value, void *ctx, int *stop)
{
struct plug *pl = value;
struct krb5_plugin *pl = value;
struct iter_ctx *s = ctx;
if (s->ret != KRB5_PLUGIN_NO_HANDLE)
return;
s->ret = s->func(s->context, pl->dataptr, pl->ctx, s->userctx);
s->ret = s->func(s->context, pl->ftable, pl->ctx, s->userctx);
if (s->ret != KRB5_PLUGIN_NO_HANDLE
&& !(s->flags & KRB5_PLUGIN_INVOKE_ALL))
*stop = 1;
@@ -503,7 +697,7 @@ eval_results(heim_object_t value, void *ctx, int *stop)
* Run plugins for the given @module (e.g., "krb5") and @name (e.g.,
* "kuserok"). Specifically, the @func is invoked once per-plugin with
* four arguments: the @context, the plugin symbol value (a pointer to a
* struct whose first three fields are the same as struct common_plugin_method),
* struct whose first three fields are the same as common_plugin_ftable),
* a context value produced by the plugin's init method, and @userctx.
*
* @func should unpack arguments for a plugin function and invoke it
@@ -530,83 +724,69 @@ eval_results(heim_object_t value, void *ctx, int *stop)
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_plugin_run_f(krb5_context context,
const char *module,
const char *name,
int min_version,
struct krb5_plugin_data *caller,
int flags,
void *userctx,
krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *))
{
heim_string_t m = heim_string_create(module);
heim_dict_t dict = NULL;
void *plug_ctx;
struct common_plugin_method *cpm;
heim_string_t m = heim_string_create(caller->module);
heim_dict_t modules, dict = NULL;
struct iter_ctx s;
struct krb5_plugin *registered_plugins = NULL;
struct krb5_plugin *p;
/* Get registered plugins */
(void) _krb5_plugin_find(context, SYMBOL, name, &registered_plugins);
HEIMDAL_MUTEX_lock(&plugin_mutex);
s.context = context;
s.name = name;
s.n = heim_string_create(name);
s.caller = caller;
s.n = heim_string_create(caller->name);
s.flags = flags;
s.min_version = min_version;
s.result = heim_array_create();
s.func = func;
s.userctx = userctx;
s.ret = KRB5_PLUGIN_NO_HANDLE;
/* Get loaded plugins */
if (modules)
dict = heim_dict_copy_value(modules, m);
HEIMDAL_MUTEX_lock(&modules_mutex);
heim_release(m);
/* Get loaded plugins */
modules = copy_modules();
dict = heim_dict_copy_value(modules, m);
/* Add loaded plugins to s.result array */
if (dict)
heim_dict_iterate_f(dict, &s, search_modules);
/* We don't need to hold plugin_mutex during plugin invocation */
HEIMDAL_MUTEX_unlock(&plugin_mutex);
/* We don't need to hold modules_mutex during plugin invocation */
HEIMDAL_MUTEX_unlock(&modules_mutex);
/* Invoke registered plugins (old system) */
for (p = registered_plugins; p; p = p->next) {
/*
* XXX This is the wrong way to handle registered plugins, as we
* call init/fini on each invocation! We do this because we
* have nowhere in the struct plugin registered list to store
* the context allocated by the plugin's init function. (But at
* least we do call init/fini!)
*
* What we should do is adapt the old plugin system to the new
* one and change how we register plugins so that we use the new
* struct plug to keep track of their context structures, that
* way we can init once, invoke many times, then fini.
*/
cpm = (struct common_plugin_method *)p->symbol;
s.ret = cpm->init(context, &plug_ctx);
if (s.ret)
continue;
s.ret = s.func(s.context, p->symbol, plug_ctx, s.userctx);
cpm->fini(plug_ctx);
if (s.ret != KRB5_PLUGIN_NO_HANDLE &&
!(flags & KRB5_PLUGIN_INVOKE_ALL))
break;
}
_krb5_plugin_free(registered_plugins);
/* Invoke loaded plugins (new system) */
if (s.ret == KRB5_PLUGIN_NO_HANDLE)
heim_array_iterate_f(s.result, &s, eval_results);
/* Invoke loaded plugins */
heim_array_iterate_f(s.result, &s, eval_results);
heim_release(s.result);
heim_release(s.n);
if (dict)
heim_release(dict);
heim_release(dict);
heim_release(m);
heim_release(modules);
return s.ret;
}
/**
* Return a cookie identifying this instance of a library.
*
* Inputs:
*
* @context A krb5_context
* @module Our library name or a library we depend on
*
* Outputs: The instance cookie
*
* @ingroup krb5_support
*/
KRB5_LIB_FUNCTION uintptr_t KRB5_LIB_CALL
krb5_get_instance(const char *libname)
{
static const char *instance = "libkrb5";
if (strcmp(libname, "krb5") == 0)
return (uintptr_t)instance;
return 0;
}

View File

@@ -96,6 +96,17 @@ realmcallback(krb5_context context, const void *plug, void *plugctx, void *userc
ctx->send_data, ctx->receive);
}
static const char *send_to_kdc_plugin_deps[] = { "krb5", NULL };
static struct krb5_plugin_data
send_to_kdc_plugin_data = {
"krb5",
KRB5_PLUGIN_SEND_TO_KDC,
KRB5_PLUGIN_SEND_TO_KDC_VERSION_0,
send_to_kdc_plugin_deps,
krb5_get_instance
};
static krb5_error_code
kdc_via_plugin(krb5_context context,
krb5_krbhst_info *hi,
@@ -111,8 +122,7 @@ kdc_via_plugin(krb5_context context,
userctx.send_data = send_data;
userctx.receive = receive;
return _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_SEND_TO_KDC,
KRB5_PLUGIN_SEND_TO_KDC_VERSION_0, 0,
return _krb5_plugin_run_f(context, &send_to_kdc_plugin_data, 0,
&userctx, kdccallback);
}
@@ -131,8 +141,7 @@ realm_via_plugin(krb5_context context,
userctx.send_data = send_data;
userctx.receive = receive;
return _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_SEND_TO_KDC,
KRB5_PLUGIN_SEND_TO_KDC_VERSION_2, 0,
return _krb5_plugin_run_f(context, &send_to_kdc_plugin_data, 0,
&userctx, realmcallback);
}

View File

@@ -66,10 +66,12 @@ resolve_lookup(void *ctx,
s.sin_port = htons(88);
s.sin_addr.s_addr = htonl(0x7f000002);
if (strcmp(realm, "NOTHERE.H5L.SE") == 0)
if (strcmp(realm, "NOTHERE.H5L.SE") == 0) {
(*add)(addctx, type, (struct sockaddr *)&s);
return 0;
}
return 0;
return KRB5_PLUGIN_NO_HANDLE;
}

View File

@@ -362,6 +362,7 @@ HEIMDAL_KRB5_2.0 {
krb5_get_init_creds_opt_set_tkt_life;
krb5_get_init_creds_opt_set_win2k;
krb5_get_init_creds_password;
krb5_get_instance;
krb5_get_kdc_cred;
krb5_get_kdc_sec_offset;
krb5_get_krb524hst;
@@ -748,6 +749,10 @@ HEIMDAL_KRB5_2.0 {
_krb5_build_authenticator;
_krb5_kt_client_default_name;
# Shared with libkadm5
_krb5_load_plugins;
_krb5_unload_plugins;
# Shared with libkdc
_krb5_AES_SHA1_string_to_default_iterator;
_krb5_AES_SHA2_string_to_default_iterator;
@@ -761,8 +766,6 @@ HEIMDAL_KRB5_2.0 {
_krb5_pk_load_id;
_krb5_pk_mk_ContentInfo;
_krb5_pk_octetstring2key;
_krb5_plugin_find;
_krb5_plugin_free;
_krb5_plugin_run_f;
_krb5_principal2principalname;
_krb5_principalname2krb5_principal;