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

@@ -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;