From 1d2a3e24cb5e680a4fe9fa918bd8e20b66ad37d8 Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Mon, 24 Dec 2018 12:52:22 +1100 Subject: [PATCH] update for v1 hook API --- README | 2 +- patches/README | 77 ------ patches/heimdal-1.3.1 | 581 --------------------------------------- patches/heimdal-7.4.0 | 503 --------------------------------- plugin/heimdal.c | 194 ++++++++++--- tests/plugin/heimdal-t.c | 135 ++++++--- 6 files changed, 262 insertions(+), 1230 deletions(-) delete mode 100644 patches/README delete mode 100644 patches/heimdal-1.3.1 delete mode 100644 patches/heimdal-7.4.0 diff --git a/README b/README index e0720fc..ee01e72 100644 --- a/README +++ b/README @@ -361,7 +361,7 @@ CONFIGURATION to use the plugin, add configuration like: [kadmin] - hook_libraries = /usr/local/lib/krb5/plugins/kadm5_hook/sync.so + hooks = /usr/local/lib/krb5/plugins/kadm5_hook/sync.so to the configuration file used by kadmind and kpasswdd. Update the path for wherever the krb5-sync plugin is located. diff --git a/patches/README b/patches/README deleted file mode 100644 index 38bd901..0000000 --- a/patches/README +++ /dev/null @@ -1,77 +0,0 @@ -The patches in this directory apply to Heimdal and add plugin support to -the kadmin libraries for performing actions before and after a password -change is committed to the KDC database and after a change is made to the -attributes of a principal (specifically, a change to DISALLOW_ALL_TIX). - -No patch is required for MIT Kerberos 1.9 or later. MIT Kerberos prior to -1.9 is not supported. - -Currently, there are two patches available: - - heimdal-1.3.1 Built against stock Heimdal 1.3.1 - heimdal-7.4.0 Built against stock Heimdal 7.4.0 - -More patches against other source trees may be provided in the future. -Please let me know if there is a specific version you wish to see a patch -for (and even better, let me know if you have a tested patch for a -different version). - -This patch adds a hook_libraries configuration option to the [kadmin] -section of krb5.conf (or kdc.conf if you use that file) that must be set -to load the module. That configuration option is in the form: - - [kadmin] - hook_libraries = /usr/local/lib/krb5/plugins/kadm5_hook/krb5_sync.so - -where the value is the full path to the plugin that you want to load. If -this option is not present, kadmind will not load a plugin and the changes -from the patch will be inactive. If this option is given and the plugin -cannot be loaded, kadmind startup will abort with a (hopefully useful) -error message in syslog. - - -Any plugin used with this patch must expose a public struct named -kadm5_hook. That struct must contain the following: - - typedef struct kadm5_hook { - const char *name; - int version; - const char *vendor; - - krb5_error_code (*init)(krb5_context, void **); - void (*fini)(krb5_context, void *); - - krb5_error_code (*chpass)(krb5_context, void *, enum kadm5_hook_stage, - krb5_principal, const char *); - krb5_error_code (*create)(krb5_context, void *, enum kadm5_hook_stage, - kadm5_principal_ent_t, uint32_t mask, - const char *password); - krb5_error_code (*modify)(krb5_context, void *, enum kadm5_hook_stage, - kadm5_principal_ent_t, uint32_t mask); - } kadm5_hook; - -where enum kadm5_hook_stage is: - - enum kadm5_hook_stage { - KADM5_HOOK_STAGE_PRECOMMIT, - KADM5_HOOK_STAGE_POSTCOMMIT - }; - -init creates a hook context that is passed into all subsequent calls. -chpass is called for password changes, create is called for principal -creation (with the newly-created principal in the kadm5_principal_ent_t -argument), and modify is called when a principal is modified. - -These functions should follow the normal Kerberos calling convention of -returning 0 on success and a Kerberos error code on failure, setting the -Kerberos error message in the provided context. - ------ - -Copyright 2012, 2013 - The Board of Trustees of the Leland Stanford Junior University - -Copying and distribution of this file, with or without modification, are -permitted in any medium without royalty provided the copyright notice and -this notice are preserved. This file is offered as-is, without any -warranty. diff --git a/patches/heimdal-1.3.1 b/patches/heimdal-1.3.1 deleted file mode 100644 index 8b68b6c..0000000 --- a/patches/heimdal-1.3.1 +++ /dev/null @@ -1,581 +0,0 @@ -krb5-sync patch for Heimdal 1.3.1. - -This patch may apply to earlier or later versions, but may not and will -require verification. It was actually written against Heimdal trunk as of -2010-02-11 so will apply with some fuzz to 1.3.1. - -Note that this patch modifies Makefile.am and hence requires autogen.sh -before running configure and rebuilding. - -Once this patch is applied and the libkadm5srv library is updated, you can -configure Heimdal to load hook modules by adding a hook_libraries setting -to the [kadmin] section in the configuration file used by kadmind, -kpasswdd, and kadmin (for kadmin -l). An example setting would look like: - - [kadmin] - hook_libraries = /usr/local/lib/krb5/plugins/krb5_sync.so - -This is a preliminary, and ugly, patch that adds the necessary features -for krb5-sync in a somewhat brute-force way. It will change in the future -to better fit with the rest of Heimdal so that it can be integrated into -Heimdal. Expect the details of how it works to change in later versions. - -Written by Russ Allbery -Copyright 2010 - The Board of Trustees of the Leland Stanford Junior University - -See LICENSE for licensing terms. - -From e023789e10f04d8985238cf529258c50c8e99395 Mon Sep 17 00:00:00 2001 -From: Russ Allbery -Date: Thu, 11 Feb 2010 16:01:03 -0800 -Subject: [PATCH] Add libkadm5srv hooks for creation, modification, and password change - -Add hooks to libkadm5srv to dynamically load modules at runtime and add -pre-commit and post-commit callback hooks for password change, principal -creation with a password, and principal modification. Only those three -hooks are supported. Failure of the pre-commit hook will abort the -operation, whereas failure of the post-commit hook will not. - -The dynamic modules are loaded based on [kadmin]hook_libraries. - -Only creation with a password and key change with a password have hooks, -not changes with keys. ---- - lib/kadm5/Makefile.am | 3 +- - lib/kadm5/chpass_s.c | 31 +++++++++ - lib/kadm5/context_s.c | 9 +++ - lib/kadm5/create_s.c | 32 ++++++++++ - lib/kadm5/destroy_s.c | 6 +- - lib/kadm5/kadm5-hook.h | 81 ++++++++++++++++++++++++ - lib/kadm5/kadm5_err.et | 1 + - lib/kadm5/modify_s.c | 32 ++++++++++ - lib/kadm5/private.h | 10 +++ - lib/kadm5/server_hooks.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++ - 10 files changed, 354 insertions(+), 3 deletions(-) - create mode 100644 lib/kadm5/kadm5-hook.h - create mode 100644 lib/kadm5/server_hooks.c - -diff --git a/lib/kadm5/Makefile.am b/lib/kadm5/Makefile.am -index dd311fd..3afb597 100644 ---- a/lib/kadm5/Makefile.am -+++ b/lib/kadm5/Makefile.am -@@ -32,7 +32,7 @@ default_keys_SOURCES = default_keys.c - kadm5includedir = $(includedir)/kadm5 - buildkadm5include = $(buildinclude)/kadm5 - --dist_kadm5include_HEADERS = admin.h private.h kadm5-pwcheck.h -+dist_kadm5include_HEADERS = admin.h private.h kadm5-hook.h kadm5-pwcheck.h - dist_kadm5include_HEADERS += kadm5-protos.h kadm5-private.h - - nodist_kadm5include_HEADERS = kadm5_err.h -@@ -105,6 +105,7 @@ dist_libkadm5srv_la_SOURCES = \ - randkey_s.c \ - rename_s.c \ - server_glue.c \ -+ server_hooks.c \ - set_keys.c \ - set_modifier.c \ - admin.h -diff --git a/lib/kadm5/chpass_s.c b/lib/kadm5/chpass_s.c -index 0131bba..d6cc30f 100644 ---- a/lib/kadm5/chpass_s.c -+++ b/lib/kadm5/chpass_s.c -@@ -47,6 +47,8 @@ change(void *server_handle, - Key *keys; - size_t num_keys; - int existsp = 0; -+ int i; -+ krb5_error_code code; - - memset(&ent, 0, sizeof(ent)); - ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0); -@@ -58,6 +60,22 @@ change(void *server_handle, - if(ret) - goto out; - -+ for (i = 0; i < context->num_hooks; i++) { -+ kadm5_hook_context *hook = context->hooks[i]; -+ -+ if (hook->hook->chpass != NULL) { -+ ret = hook->hook->chpass(context->context, hook->data, -+ KADM5_HOOK_STAGE_PRECOMMIT, -+ princ, password); -+ if (ret != 0) { -+ krb5_set_error_message(context->context, ret, -+ "password change hook `%s' failed" -+ " precommit", hook->hook->name); -+ goto out2; -+ } -+ } -+ } -+ - if (context->db->hdb_capability_flags & HDB_CAP_F_HANDLE_PASSWORDS) { - ret = context->db->hdb_password(context->context, context->db, - &ent, password, cond); -@@ -115,6 +133,19 @@ change(void *server_handle, - KADM5_KEY_DATA | KADM5_KVNO | KADM5_PW_EXPIRATION | - KADM5_TL_DATA); - -+ for (i = 0; i < context->num_hooks; i++) { -+ kadm5_hook_context *hook = context->hooks[i]; -+ -+ if (hook->hook->chpass != NULL) { -+ code = hook->hook->chpass(context->context, hook->data, -+ KADM5_HOOK_STAGE_POSTCOMMIT, -+ princ, password); -+ if (code != 0) -+ krb5_warn(context->context, code, "password change hook `%s'" -+ " failed postcommit", hook->hook->name); -+ } -+ } -+ - out2: - hdb_free_entry(context->context, &ent); - out: -diff --git a/lib/kadm5/context_s.c b/lib/kadm5/context_s.c -index 811921d..cfee258 100644 ---- a/lib/kadm5/context_s.c -+++ b/lib/kadm5/context_s.c -@@ -141,6 +141,8 @@ _kadm5_s_init_context(kadm5_server_context **ctx, - kadm5_config_params *params, - krb5_context context) - { -+ kadm5_ret_t ret; -+ - *ctx = malloc(sizeof(**ctx)); - if(*ctx == NULL) - return ENOMEM; -@@ -162,6 +164,13 @@ _kadm5_s_init_context(kadm5_server_context **ctx, - - find_db_spec(*ctx); - -+ ret = _kadm5_s_init_hooks(*ctx); -+ if (ret != 0) { -+ kadm5_s_destroy(*ctx); -+ *ctx = NULL; -+ return ret; -+ } -+ - /* PROFILE can't be specified for now */ - /* KADMIND_PORT is supposed to be used on the server also, - but this doesn't make sense */ -diff --git a/lib/kadm5/create_s.c b/lib/kadm5/create_s.c -index d6f8f6a..0af73cc 100644 ---- a/lib/kadm5/create_s.c -+++ b/lib/kadm5/create_s.c -@@ -152,6 +152,24 @@ kadm5_s_create_principal(void *server_handle, - kadm5_ret_t ret; - hdb_entry_ex ent; - kadm5_server_context *context = server_handle; -+ int i; -+ krb5_error_code code; -+ -+ for (i = 0; i < context->num_hooks; i++) { -+ kadm5_hook_context *hook = context->hooks[i]; -+ -+ if (hook->hook->create != NULL) { -+ ret = hook->hook->create(context->context, hook->data, -+ KADM5_HOOK_STAGE_PRECOMMIT, princ, -+ mask, password); -+ if (ret != 0) { -+ krb5_set_error_message(context->context, ret, -+ "create hook `%s' failed precommit", -+ hook->hook->name); -+ return ret; -+ } -+ } -+ } - - ret = create_principal(context, princ, mask, &ent, - KADM5_PRINCIPAL, -@@ -187,6 +205,20 @@ kadm5_s_create_principal(void *server_handle, - - kadm5_log_create (context, &ent.entry); - -+ for (i = 0; i < context->num_hooks; i++) { -+ kadm5_hook_context *hook = context->hooks[i]; -+ -+ if (hook->hook->create != NULL) { -+ code = hook->hook->create(context->context, hook->data, -+ KADM5_HOOK_STAGE_POSTCOMMIT, princ, -+ mask, password); -+ if (code != 0) -+ krb5_warn(context->context, code, -+ "create hook `%s' failed postcommit", -+ hook->hook->name); -+ } -+ } -+ - out: - hdb_free_entry(context->context, &ent); - return _kadm5_error_code(ret); -diff --git a/lib/kadm5/destroy_s.c b/lib/kadm5/destroy_s.c -index 25fc48b..d24e26b 100644 ---- a/lib/kadm5/destroy_s.c -+++ b/lib/kadm5/destroy_s.c -@@ -72,11 +72,13 @@ destroy_kadm5_log_context (kadm5_log_context *c) - kadm5_ret_t - kadm5_s_destroy(void *server_handle) - { -- kadm5_ret_t ret; -+ kadm5_ret_t ret = 0; - kadm5_server_context *context = server_handle; - krb5_context kcontext = context->context; - -- ret = context->db->hdb_destroy(kcontext, context->db); -+ _kadm5_s_free_hooks(context); -+ if(context->db) -+ ret = context->db->hdb_destroy(kcontext, context->db); - destroy_kadm5_log_context (&context->log_context); - destroy_config (&context->config); - krb5_free_principal (kcontext, context->caller); -diff --git a/lib/kadm5/kadm5-hook.h b/lib/kadm5/kadm5-hook.h -new file mode 100644 -index 0000000..0651afc ---- /dev/null -+++ b/lib/kadm5/kadm5-hook.h -@@ -0,0 +1,81 @@ -+/* -+ * Copyright 2010 -+ * The Board of Trustees of the Leland Stanford Junior University -+ * -+ * 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. -+ * -+ * 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. -+ * -+ * 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 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. -+ */ -+ -+#ifndef KADM5_HOOK_H -+#define KADM5_HOOK_H 1 -+ -+#define KADM5_HOOK_VERSION_V0 0 -+ -+/* -+ * Each hook is called before the operation using KADM5_STAGE_PRECOMMIT and -+ * then after the operation using KADM5_STAGE_POSTCOMMIT. If the hook returns -+ * failure during precommit, the operation is aborted without changes to the -+ * database. -+ */ -+enum kadm5_hook_stage { -+ KADM5_HOOK_STAGE_PRECOMMIT, -+ KADM5_HOOK_STAGE_POSTCOMMIT -+}; -+ -+/* -+ * libkadm5srv expects a symbol named kadm5_hook_v0 exported by the dynamicaly -+ * loaded module and of type kadm5_hook. version must be -+ * KADM5_HOOK_VERSION_V0. Any or all of the function pointers may be NULL, in -+ * which case that hook will not be called. -+ */ -+typedef struct kadm5_hook { -+ const char *name; -+ int version; -+ const char *vendor; -+ -+ krb5_error_code (*init)(krb5_context, void **); -+ void (*fini)(krb5_context, void *); -+ -+ krb5_error_code (*chpass)(krb5_context, void *, enum kadm5_hook_stage, -+ krb5_principal, const char *); -+ krb5_error_code (*create)(krb5_context, void *, enum kadm5_hook_stage, -+ kadm5_principal_ent_t, uint32_t mask, -+ const char *password); -+ krb5_error_code (*modify)(krb5_context, void *, enum kadm5_hook_stage, -+ kadm5_principal_ent_t, uint32_t mask); -+ -+#if 0 -+ krb5_error_code (*delete)(krb5_context, void *, enum kadm5_hook_stage, -+ krb5_principal); -+ krb5_error_code (*randkey)(krb5_context, void *, enum kadm5_hook_stage, -+ krb5_principal); -+ krb5_error_code (*rename)(krb5_context, void *, enum kadm5_hook_stage, -+ krb5_principal source, krb5_principal target); -+#endif -+} kadm5_hook; -+ -+#endif /* !KADM5_HOOK_H */ -diff --git a/lib/kadm5/kadm5_err.et b/lib/kadm5/kadm5_err.et -index ae78472..0831472 100644 ---- a/lib/kadm5/kadm5_err.et -+++ b/lib/kadm5/kadm5_err.et -@@ -57,3 +57,4 @@ error_code AUTH_CHANGEPW, "Operation requires `change-password' privilege" - error_code BAD_TL_TYPE, "Invalid tagged data list element type" - error_code MISSING_CONF_PARAMS, "Required parameters in kdc.conf missing" - error_code BAD_SERVER_NAME, "Bad krb5 admin server hostname" -+error_code BAD_SERVER_HOOK, "Bad KADM5 server hook module" -diff --git a/lib/kadm5/modify_s.c b/lib/kadm5/modify_s.c -index bf16982..d71204f 100644 ---- a/lib/kadm5/modify_s.c -+++ b/lib/kadm5/modify_s.c -@@ -44,6 +44,9 @@ modify_principal(void *server_handle, - kadm5_server_context *context = server_handle; - hdb_entry_ex ent; - kadm5_ret_t ret; -+ int i; -+ krb5_error_code code; -+ - if((mask & forbidden_mask)) - return KADM5_BAD_MASK; - if((mask & KADM5_POLICY) && strcmp(princ->policy, "default")) -@@ -57,6 +60,21 @@ modify_principal(void *server_handle, - princ->principal, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, &ent); - if(ret) - goto out; -+ for (i = 0; i < context->num_hooks; i++) { -+ kadm5_hook_context *hook = context->hooks[i]; -+ -+ if (hook->hook->modify != NULL) { -+ ret = hook->hook->modify(context->context, hook->data, -+ KADM5_HOOK_STAGE_PRECOMMIT, princ, -+ mask); -+ if (ret != 0) { -+ krb5_set_error_message(context->context, code, -+ "modify hook `%s' failed precommit", -+ hook->hook->name); -+ goto out2; -+ } -+ } -+ } - ret = _kadm5_setup_entry(context, &ent, mask, princ, mask, NULL, 0); - if(ret) - goto out2; -@@ -77,6 +95,20 @@ modify_principal(void *server_handle, - &ent.entry, - mask | KADM5_MOD_NAME | KADM5_MOD_TIME); - -+ for (i = 0; i < context->num_hooks; i++) { -+ kadm5_hook_context *hook = context->hooks[i]; -+ -+ if (hook->hook->modify != NULL) { -+ code = hook->hook->modify(context->context, hook->data, -+ KADM5_HOOK_STAGE_POSTCOMMIT, princ, -+ mask); -+ if (code != 0) -+ krb5_warn(context->context, code, -+ "modify hook `%s' failed postcommit", -+ hook->hook->name); -+ } -+ } -+ - out2: - hdb_free_entry(context->context, &ent); - out: -diff --git a/lib/kadm5/private.h b/lib/kadm5/private.h -index 7c5b27f..f9bf923 100644 ---- a/lib/kadm5/private.h -+++ b/lib/kadm5/private.h -@@ -36,6 +36,8 @@ - #ifndef __kadm5_privatex_h__ - #define __kadm5_privatex_h__ - -+#include "kadm5-hook.h" -+ - struct kadm_func { - kadm5_ret_t (*chpass_principal) (void *, krb5_principal, const char*); - kadm5_ret_t (*create_principal) (void*, kadm5_principal_ent_t, -@@ -82,6 +84,12 @@ typedef struct kadm5_log_context { - krb5_socket_t socket_fd; - } kadm5_log_context; - -+typedef struct kadm5_hook_context { -+ void *handle; -+ kadm5_hook *hook; -+ void *data; -+} kadm5_hook_context; -+ - typedef struct kadm5_server_context { - krb5_context context; - krb5_boolean my_context; -@@ -92,6 +100,8 @@ typedef struct kadm5_server_context { - krb5_principal caller; - unsigned acl_flags; - kadm5_log_context log_context; -+ int num_hooks; -+ kadm5_hook_context **hooks; - } kadm5_server_context; - - typedef struct kadm5_client_context { -diff --git a/lib/kadm5/server_hooks.c b/lib/kadm5/server_hooks.c -new file mode 100644 -index 0000000..8bf069f ---- /dev/null -+++ b/lib/kadm5/server_hooks.c -@@ -0,1 +1,152 @@ -+/* -+ * Copyright 2010 -+ * The Board of Trustees of the Leland Stanford Junior University -+ * -+ * 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. -+ * -+ * 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. -+ * -+ * 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 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" -+#include -+ -+#ifndef RTLD_NOW -+# define RTLD_NOW 0 -+#endif -+ -+/* -+ * Load kadmin server hooks. -+ */ -+#ifdef HAVE_DLOPEN -+ -+kadm5_ret_t -+_kadm5_s_init_hooks(kadm5_server_context *ctx) -+{ -+ krb5_context context = ctx->context; -+ char **libraries; -+ const char *library; -+ int i; -+ void *handle = NULL; -+ struct kadm5_hook *hook; -+ struct kadm5_hook_context *hook_context = NULL; -+ struct kadm5_hook_context **tmp; -+ kadm5_ret_t ret = KADM5_BAD_SERVER_NAME; -+ -+ libraries = krb5_config_get_strings(context, NULL, -+ "kadmin", "hook_libraries", NULL); -+ if (libraries == NULL) -+ return 0; -+ for (i = 0; libraries[i] != NULL; i++) { -+ library = libraries[i]; -+ handle = dlopen(library, RTLD_NOW); -+ if (handle == NULL) { -+ krb5_warnx(context, "failed to open `%s': %s", library, dlerror()); -+ goto fail; -+ } -+ hook = dlsym(handle, "kadm5_hook_v0"); -+ if (hook == NULL) { -+ krb5_warnx(context, "didn't find `kadm5_hook_v0' symbol in `%s':" -+ " %s", library, dlerror()); -+ goto fail; -+ } -+ if (hook->version != KADM5_HOOK_VERSION_V0) { -+ krb5_warnx(context, "version of loaded library `%s' is %d" -+ " (expected %d)", library, hook->version, -+ KADM5_HOOK_VERSION_V0); -+ goto fail; -+ } -+ hook_context = malloc(sizeof(*hook_context)); -+ if (hook_context == NULL) { -+ krb5_warnx(context, "out of memory"); -+ ret = errno; -+ goto fail; -+ } -+ hook_context->handle = handle; -+ hook_context->hook = hook; -+ if (hook->init == NULL) { -+ hook_context->data = NULL; -+ } else { -+ ret = hook->init(context, &hook_context->data); -+ if (ret != 0) { -+ krb5_warn(context, ret, "initialization of `%s' failed", -+ library); -+ goto fail; -+ } -+ } -+ tmp = realloc(ctx->hooks, (ctx->num_hooks + 1) * sizeof(*tmp)); -+ if (tmp == NULL) { -+ krb5_warnx(context, "out of memory"); -+ ret = errno; -+ goto fail; -+ } -+ ctx->hooks = tmp; -+ ctx->hooks[ctx->num_hooks] = hook_context; -+ hook_context = NULL; -+ ctx->num_hooks++; -+ } -+ return 0; -+ -+fail: -+ _kadm5_s_free_hooks(ctx); -+ if (hook_context != NULL) -+ free(hook_context); -+ if (handle != NULL) -+ dlclose(handle); -+ return ret; -+} -+ -+void -+_kadm5_s_free_hooks(kadm5_server_context *ctx) -+{ -+ int i; -+ struct kadm5_hook *hook; -+ -+ 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; -+} -+ -+# else /* !HAVE_DLOPEN */ -+ -+kadm5_ret_t -+_kadm5_s_init_hooks(kadm5_server_context *ctx) -+{ -+ return 0; -+} -+ -+void -+_kadm5_s_free_hooks(kadm5_server_context *ctx) -+{ -+ return 0; -+} -+ -+#endif /* !HAVE_DLOPEN */ --- -1.6.6.1 - diff --git a/patches/heimdal-7.4.0 b/patches/heimdal-7.4.0 deleted file mode 100644 index 820a276..0000000 --- a/patches/heimdal-7.4.0 +++ /dev/null @@ -1,503 +0,0 @@ -diff --git a/lib/kadm5/Makefile.am b/lib/kadm5/Makefile.am -index de9a208..626b756 100644 ---- a/lib/kadm5/Makefile.am -+++ b/lib/kadm5/Makefile.am -@@ -35,7 +35,7 @@ default_keys_SOURCES = default_keys.c - kadm5includedir = $(includedir)/kadm5 - buildkadm5include = $(buildinclude)/kadm5 - --dist_kadm5include_HEADERS = admin.h private.h kadm5-pwcheck.h -+dist_kadm5include_HEADERS = admin.h private.h kadm5-hook.h kadm5-pwcheck.h - dist_kadm5include_HEADERS += $(srcdir)/kadm5-protos.h $(srcdir)/kadm5-private.h - - nodist_kadm5include_HEADERS = kadm5_err.h -@@ -108,6 +108,7 @@ dist_libkadm5srv_la_SOURCES = \ - randkey_s.c \ - rename_s.c \ - server_glue.c \ -+ server_hooks.c \ - setkey3_s.c \ - set_keys.c \ - set_modifier.c \ -diff --git a/lib/kadm5/chpass_s.c b/lib/kadm5/chpass_s.c -index d1ed732..5db70ae 100644 ---- a/lib/kadm5/chpass_s.c -+++ b/lib/kadm5/chpass_s.c -@@ -50,6 +50,8 @@ change(void *server_handle, - Key *keys; - size_t num_keys; - int existsp = 0; -+ int i; -+ krb5_error_code code; - - memset(&ent, 0, sizeof(ent)); - if (!context->keep_open) { -@@ -67,6 +69,22 @@ change(void *server_handle, - if (ret) - goto out2; - -+ for (i = 0; i < context->num_hooks; i++) { -+ kadm5_hook_context *hook = context->hooks[i]; -+ -+ if (hook->hook->chpass != NULL) { -+ ret = hook->hook->chpass(context->context, hook->data, -+ KADM5_HOOK_STAGE_PRECOMMIT, -+ princ, password); -+ if (ret != 0) { -+ krb5_set_error_message(context->context, ret, -+ "password change hook `%s' failed" -+ " precommit", hook->hook->name); -+ goto out3; -+ } -+ } -+ } -+ - if (keepold || cond) { - /* - * We save these for now so we can handle password history checking; -@@ -149,6 +167,19 @@ change(void *server_handle, - KADM5_KEY_DATA | KADM5_KVNO | - KADM5_PW_EXPIRATION | KADM5_TL_DATA); - -+ for (i = 0; i < context->num_hooks; i++) { -+ kadm5_hook_context *hook = context->hooks[i]; -+ -+ if (hook->hook->chpass != NULL) { -+ code = hook->hook->chpass(context->context, hook->data, -+ KADM5_HOOK_STAGE_POSTCOMMIT, -+ princ, password); -+ if (code != 0) -+ krb5_warn(context->context, code, "password change hook `%s'" -+ " failed postcommit", hook->hook->name); -+ } -+ } -+ - out3: - hdb_free_entry(context->context, &ent); - out2: -diff --git a/lib/kadm5/context_s.c b/lib/kadm5/context_s.c -index 4aeee5d..cf90e5f 100644 ---- a/lib/kadm5/context_s.c -+++ b/lib/kadm5/context_s.c -@@ -268,6 +268,13 @@ _kadm5_s_init_context(kadm5_server_context **ctx, - - find_db_spec(*ctx); - -+ ret = _kadm5_s_init_hooks(*ctx); -+ if (ret != 0) { -+ kadm5_s_destroy(*ctx); -+ *ctx = NULL; -+ return ret; -+ } -+ - /* PROFILE can't be specified for now */ - /* KADMIND_PORT is supposed to be used on the server also, - but this doesn't make sense */ -diff --git a/lib/kadm5/create_s.c b/lib/kadm5/create_s.c -index 7d8f898..8e1b924 100644 ---- a/lib/kadm5/create_s.c -+++ b/lib/kadm5/create_s.c -@@ -171,6 +171,24 @@ kadm5_s_create_principal(void *server_handle, - kadm5_ret_t ret; - hdb_entry_ex ent; - kadm5_server_context *context = server_handle; -+ int i; -+ krb5_error_code code; -+ -+ for (i = 0; i < context->num_hooks; i++) { -+ kadm5_hook_context *hook = context->hooks[i]; -+ -+ if (hook->hook->create != NULL) { -+ ret = hook->hook->create(context->context, hook->data, -+ KADM5_HOOK_STAGE_PRECOMMIT, princ, -+ mask, password); -+ if (ret != 0) { -+ krb5_set_error_message(context->context, ret, -+ "create hook `%s' failed precommit", -+ hook->hook->name); -+ return ret; -+ } -+ } -+ } - - if ((mask & KADM5_KVNO) == 0) { - /* create_principal() through _kadm5_setup_entry(), will need this */ -@@ -214,6 +232,20 @@ kadm5_s_create_principal(void *server_handle, - /* This logs the change for iprop and writes to the HDB */ - ret = kadm5_log_create(context, &ent.entry); - -+ for (i = 0; i < context->num_hooks; i++) { -+ kadm5_hook_context *hook = context->hooks[i]; -+ -+ if (hook->hook->create != NULL) { -+ code = hook->hook->create(context->context, hook->data, -+ KADM5_HOOK_STAGE_POSTCOMMIT, princ, -+ mask, password); -+ if (code != 0) -+ krb5_warn(context->context, code, -+ "create hook `%s' failed postcommit", -+ hook->hook->name); -+ } -+ } -+ - out2: - (void) kadm5_log_end(context); - out: -diff --git a/lib/kadm5/destroy_s.c b/lib/kadm5/destroy_s.c -index 2424366..fc86e59 100644 ---- a/lib/kadm5/destroy_s.c -+++ b/lib/kadm5/destroy_s.c -@@ -77,6 +77,7 @@ kadm5_s_destroy(void *server_handle) - kadm5_server_context *context = server_handle; - krb5_context kcontext = context->context; - -+ _kadm5_s_free_hooks(context); - if (context->db != NULL) - ret = context->db->hdb_destroy(kcontext, context->db); - destroy_kadm5_log_context(&context->log_context); -diff --git a/lib/kadm5/kadm5-hook.h b/lib/kadm5/kadm5-hook.h -new file mode 100644 -index 0000000..faae19a ---- /dev/null -+++ b/lib/kadm5/kadm5-hook.h -@@ -0,0 +1,81 @@ -+/* -+ * Copyright 2010 -+ * The Board of Trustees of the Leland Stanford Junior University -+ * -+ * 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. -+ * -+ * 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. -+ * -+ * 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 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. -+ */ -+ -+#ifndef KADM5_HOOK_H -+#define KADM5_HOOK_H 1 -+ -+#define KADM5_HOOK_VERSION_V0 0 -+ -+/* -+ * Each hook is called before the operation using KADM5_STAGE_PRECOMMIT and -+ * then after the operation using KADM5_STAGE_POSTCOMMIT. If the hook returns -+ * failure during precommit, the operation is aborted without changes to the -+ * database. -+ */ -+enum kadm5_hook_stage { -+ KADM5_HOOK_STAGE_PRECOMMIT, -+ KADM5_HOOK_STAGE_POSTCOMMIT -+}; -+ -+/* -+ * libkadm5srv expects a symbol named kadm5_hook_v0 exported by the dynamicaly -+ * loaded module and of type kadm5_hook. version must be -+ * KADM5_HOOK_VERSION_V0. Any or all of the function pointers may be NULL, in -+ * which case that hook will not be called. -+ */ -+typedef struct kadm5_hook { -+ const char *name; -+ int version; -+ const char *vendor; -+ -+ krb5_error_code (*init)(krb5_context, void **); -+ void (*fini)(krb5_context, void *); -+ -+ krb5_error_code (*chpass)(krb5_context, void *, enum kadm5_hook_stage, -+ krb5_principal, const char *); -+ krb5_error_code (*create)(krb5_context, void *, enum kadm5_hook_stage, -+ kadm5_principal_ent_t, uint32_t mask, -+ const char *password); -+ krb5_error_code (*modify)(krb5_context, void *, enum kadm5_hook_stage, -+ kadm5_principal_ent_t, uint32_t mask); -+ -+#if 0 -+ krb5_error_code (*delete)(krb5_context, void *, enum kadm5_hook_stage, -+ krb5_principal); -+ krb5_error_code (*randkey)(krb5_context, void *, enum kadm5_hook_stage, -+ krb5_principal); -+ krb5_error_code (*rename)(krb5_context, void *, enum kadm5_hook_stage, -+ krb5_principal source, krb5_principal target); -+#endif -+} kadm5_hook; -+ -+#endif /* !KADM5_HOOK_H */ -diff --git a/lib/kadm5/kadm5_err.et b/lib/kadm5/kadm5_err.et -index bcbaea8..1442919 100644 ---- a/lib/kadm5/kadm5_err.et -+++ b/lib/kadm5/kadm5_err.et -@@ -67,3 +67,4 @@ error_code ALREADY_LOCKED, "Database already locked" - error_code NOT_LOCKED, "Database not locked" - error_code LOG_CORRUPT, "Incremental propagation log got corrupted" - error_code LOG_NEEDS_UPGRADE, "Incremental propagation log must be upgraded" -+error_code BAD_SERVER_HOOK, "Bad KADM5 server hook module" -diff --git a/lib/kadm5/modify_s.c b/lib/kadm5/modify_s.c -index 78f2673..b388693 100644 ---- a/lib/kadm5/modify_s.c -+++ b/lib/kadm5/modify_s.c -@@ -44,6 +44,8 @@ modify_principal(void *server_handle, - kadm5_server_context *context = server_handle; - hdb_entry_ex ent; - kadm5_ret_t ret; -+ int i; -+ krb5_error_code code; - - memset(&ent, 0, sizeof(ent)); - -@@ -66,6 +68,22 @@ modify_principal(void *server_handle, - princ->principal, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); - if (ret) - goto out2; -+ for (i = 0; i < context->num_hooks; i++) { -+ kadm5_hook_context *hook = context->hooks[i]; -+ -+ if (hook->hook->modify != NULL) { -+ ret = hook->hook->modify(context->context, hook->data, -+ KADM5_HOOK_STAGE_PRECOMMIT, princ, -+ mask); -+ if (ret != 0) { -+ krb5_set_error_message(context->context, code, -+ "modify hook `%s' failed precommit", -+ hook->hook->name); -+ goto out3; -+ } -+ } -+ } -+ - ret = _kadm5_setup_entry(context, &ent, mask, princ, mask, NULL, 0); - if (ret) - goto out3; -@@ -114,6 +132,20 @@ modify_principal(void *server_handle, - ret = kadm5_log_modify(context, &ent.entry, - mask | KADM5_MOD_NAME | KADM5_MOD_TIME); - -+ for (i = 0; i < context->num_hooks; i++) { -+ kadm5_hook_context *hook = context->hooks[i]; -+ -+ if (hook->hook->modify != NULL) { -+ code = hook->hook->modify(context->context, hook->data, -+ KADM5_HOOK_STAGE_POSTCOMMIT, princ, -+ mask); -+ if (code != 0) -+ krb5_warn(context->context, code, -+ "modify hook `%s' failed postcommit", -+ hook->hook->name); -+ } -+ } -+ - out3: - hdb_free_entry(context->context, &ent); - out2: -diff --git a/lib/kadm5/private.h b/lib/kadm5/private.h -index 0b14ebd..57ac19f 100644 ---- a/lib/kadm5/private.h -+++ b/lib/kadm5/private.h -@@ -36,6 +36,8 @@ - #ifndef __kadm5_privatex_h__ - #define __kadm5_privatex_h__ - -+#include "kadm5-hook.h" -+ - #ifdef HAVE_SYS_UN_H - #include - #endif -@@ -67,6 +69,12 @@ struct kadm_func { - krb5_keyblock *, int); - }; - -+typedef struct kadm5_hook_context { -+ void *handle; -+ kadm5_hook *hook; -+ void *data; -+} kadm5_hook_context; -+ - /* XXX should be integrated */ - typedef struct kadm5_common_context { - krb5_context context; -@@ -108,6 +116,8 @@ typedef struct kadm5_server_context { - krb5_principal caller; - unsigned acl_flags; - kadm5_log_context log_context; -+ int num_hooks; -+ kadm5_hook_context **hooks; - } kadm5_server_context; - - typedef struct kadm5_client_context { -diff --git a/lib/kadm5/server_hooks.c b/lib/kadm5/server_hooks.c -new file mode 100644 -index 0000000..bf5ddfa ---- /dev/null -+++ b/lib/kadm5/server_hooks.c -@@ -0,0 +1,152 @@ -+/* -+ * Copyright 2010 -+ * The Board of Trustees of the Leland Stanford Junior University -+ * -+ * 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. -+ * -+ * 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. -+ * -+ * 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 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" -+#include -+ -+#ifndef RTLD_NOW -+# define RTLD_NOW 0 -+#endif -+ -+/* -+ * Load kadmin server hooks. -+ */ -+#ifdef HAVE_DLOPEN -+ -+kadm5_ret_t -+_kadm5_s_init_hooks(kadm5_server_context *ctx) -+{ -+ krb5_context context = ctx->context; -+ char **libraries; -+ const char *library; -+ int i; -+ void *handle = NULL; -+ struct kadm5_hook *hook; -+ struct kadm5_hook_context *hook_context = NULL; -+ struct kadm5_hook_context **tmp; -+ kadm5_ret_t ret = KADM5_BAD_SERVER_NAME; -+ -+ libraries = krb5_config_get_strings(context, NULL, -+ "kadmin", "hook_libraries", NULL); -+ if (libraries == NULL) -+ return 0; -+ for (i = 0; libraries[i] != NULL; i++) { -+ library = libraries[i]; -+ handle = dlopen(library, RTLD_NOW); -+ if (handle == NULL) { -+ krb5_warnx(context, "failed to open `%s': %s", library, dlerror()); -+ goto fail; -+ } -+ hook = dlsym(handle, "kadm5_hook_v0"); -+ if (hook == NULL) { -+ krb5_warnx(context, "didn't find `kadm5_hook_v0' symbol in `%s':" -+ " %s", library, dlerror()); -+ goto fail; -+ } -+ if (hook->version != KADM5_HOOK_VERSION_V0) { -+ krb5_warnx(context, "version of loaded library `%s' is %d" -+ " (expected %d)", library, hook->version, -+ KADM5_HOOK_VERSION_V0); -+ goto fail; -+ } -+ hook_context = malloc(sizeof(*hook_context)); -+ if (hook_context == NULL) { -+ krb5_warnx(context, "out of memory"); -+ ret = errno; -+ goto fail; -+ } -+ hook_context->handle = handle; -+ hook_context->hook = hook; -+ if (hook->init == NULL) { -+ hook_context->data = NULL; -+ } else { -+ ret = hook->init(context, &hook_context->data); -+ if (ret != 0) { -+ krb5_warn(context, ret, "initialization of `%s' failed", -+ library); -+ goto fail; -+ } -+ } -+ tmp = realloc(ctx->hooks, (ctx->num_hooks + 1) * sizeof(*tmp)); -+ if (tmp == NULL) { -+ krb5_warnx(context, "out of memory"); -+ ret = errno; -+ goto fail; -+ } -+ ctx->hooks = tmp; -+ ctx->hooks[ctx->num_hooks] = hook_context; -+ hook_context = NULL; -+ ctx->num_hooks++; -+ } -+ return 0; -+ -+fail: -+ _kadm5_s_free_hooks(ctx); -+ if (hook_context != NULL) -+ free(hook_context); -+ if (handle != NULL) -+ dlclose(handle); -+ return ret; -+} -+ -+void -+_kadm5_s_free_hooks(kadm5_server_context *ctx) -+{ -+ int i; -+ struct kadm5_hook *hook; -+ -+ 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; -+} -+ -+# else /* !HAVE_DLOPEN */ -+ -+kadm5_ret_t -+_kadm5_s_init_hooks(kadm5_server_context *ctx) -+{ -+ return 0; -+} -+ -+void -+_kadm5_s_free_hooks(kadm5_server_context *ctx) -+{ -+ return 0; -+} -+ -+#endif /* !HAVE_DLOPEN */ diff --git a/plugin/heimdal.c b/plugin/heimdal.c index 4eb6a5d..04daa87 100644 --- a/plugin/heimdal.c +++ b/plugin/heimdal.c @@ -23,58 +23,189 @@ #include #include -#define KADM5_HOOK_VERSION_V0 0 +#define KADM5_HOOK_VERSION_V1 1 enum kadm5_hook_stage { KADM5_HOOK_STAGE_PRECOMMIT, KADM5_HOOK_STAGE_POSTCOMMIT }; -typedef struct kadm5_hook { - const char *name; +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; const char *vendor; - krb5_error_code (*init)(krb5_context, void **); - void (*fini)(krb5_context, void *); + /* + * Hook functions; NULL functions are ignored. code is only valid on + * post-commit hooks and represents the result of the commit. Post- + * commit hooks are not called if a pre-commit hook aborted the call. + */ + krb5_error_code (KRB5_CALLCONV *chpass)(krb5_context context, + void *data, + 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); + + krb5_error_code (KRB5_CALLCONV *create)(krb5_context context, + void *data, + enum kadm5_hook_stage stage, + krb5_error_code code, + kadm5_principal_ent_t ent, + uint32_t mask, + const char *password); + + krb5_error_code (KRB5_CALLCONV *modify)(krb5_context context, + void *data, + enum kadm5_hook_stage stage, + krb5_error_code code, + kadm5_principal_ent_t ent, + uint32_t mask); + + krb5_error_code (KRB5_CALLCONV *delete)(krb5_context context, + void *data, + enum kadm5_hook_stage stage, + krb5_error_code code, + krb5_const_principal princ); - krb5_error_code (*chpass)(krb5_context, void *, enum kadm5_hook_stage, - krb5_principal, const char *); - krb5_error_code (*create)(krb5_context, void *, enum kadm5_hook_stage, - kadm5_principal_ent_t, uint32_t mask, - const char *password); - krb5_error_code (*modify)(krb5_context, void *, enum kadm5_hook_stage, - kadm5_principal_ent_t, uint32_t mask); -} kadm5_hook; + krb5_error_code (KRB5_CALLCONV *randkey)(krb5_context context, + void *data, + enum kadm5_hook_stage stage, + krb5_error_code code, + krb5_const_principal princ); + krb5_error_code (KRB5_CALLCONV *rename)(krb5_context context, + void *data, + enum kadm5_hook_stage stage, + krb5_error_code code, + krb5_const_principal source, + krb5_const_principal target); + + krb5_error_code (KRB5_CALLCONV *set_keys)(krb5_context context, + void *data, + 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 *keyblocks); + + 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; + +static krb5_error_code +init(krb5_context ctx, void **data); + +static void +fini(void *data); + +static krb5_error_code +chpass(krb5_context ctx, void *data, 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 +create(krb5_context ctx, void *data, enum kadm5_hook_stage stage, + krb5_error_code code, kadm5_principal_ent_t entry, + uint32_t mask, const char *password); + +static krb5_error_code +modify(krb5_context ctx, void *data, enum kadm5_hook_stage stage, + krb5_error_code code, kadm5_principal_ent_t entry, uint32_t mask); + +static const struct kadm5_hook_ftable kadm5_hook_v1 = { + KADM5_HOOK_VERSION_V1, + init, + fini, + "krb5-sync", + "Russ Allbery", + chpass, + create, + modify +}; + +static uintptr_t +get_instance(const char *libname) +{ + 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 ctx, + krb5_get_instance_func_t *func, + size_t *n_hooks, + const kadm5_hook_ftable *const **hooks); /* * Initialize the plugin. Calls the pwupdate_init() function and returns the * resulting data object. */ +krb5_error_code +kadm5_hook_plugin_load(krb5_context ctx, + krb5_get_instance_func_t *func, + size_t *n_hooks, + const kadm5_hook_ftable *const **hooks_p) +{ + static const kadm5_hook_ftable *const hooks[] = { + &kadm5_hook_v1 + }; + + *func = get_instance; + *n_hooks = sizeof(hooks) / sizeof(hooks[0]); + *hooks_p = hooks; + + return 0; +} + +static krb5_context hook_krb5_ctx; + static krb5_error_code init(krb5_context ctx, void **data) { + hook_krb5_ctx = ctx; + return sync_init(ctx, (kadm5_hook_modinfo **) data); } - /* * Shut down the object, freeing any internal resources. */ static void -fini(krb5_context ctx, void *data) +fini(void *data) { - sync_close(ctx, data); + sync_close(hook_krb5_ctx, data); + hook_krb5_ctx = NULL; } - /* * Handle a password change. */ static krb5_error_code chpass(krb5_context ctx, void *data, enum kadm5_hook_stage stage, - krb5_principal princ, const char *password) + krb5_error_code code UNUSED, krb5_const_principal princ, + uint32_t flags UNUSED, + size_t n_ks_tuple UNUSED, krb5_key_salt_tuple *ks_tuple UNUSED, + const char *password) { /* * If password is NULL, we have a new key set but no password (meaning @@ -86,7 +217,7 @@ chpass(krb5_context ctx, void *data, enum kadm5_hook_stage stage, /* Dispatch to the appropriate function. */ if (stage == KADM5_HOOK_STAGE_PRECOMMIT) - return sync_chpass(data, ctx, princ, password); + return sync_chpass(data, ctx, (krb5_principal)princ, password); else return 0; } @@ -100,10 +231,11 @@ chpass(krb5_context ctx, void *data, enum kadm5_hook_stage stage, */ static krb5_error_code create(krb5_context ctx, void *data, enum kadm5_hook_stage stage, - kadm5_principal_ent_t entry, uint32_t mask UNUSED, - const char *password) + krb5_error_code code, kadm5_principal_ent_t entry, + uint32_t mask UNUSED, const char *password) { - return chpass(ctx, data, stage, entry->principal, password); + return chpass(ctx, data, stage, code, entry->principal, + 0, 0, NULL, password); } @@ -116,26 +248,14 @@ create(krb5_context ctx, void *data, enum kadm5_hook_stage stage, */ static krb5_error_code modify(krb5_context ctx, void *data, enum kadm5_hook_stage stage, - kadm5_principal_ent_t entry, uint32_t mask) + krb5_error_code code, kadm5_principal_ent_t entry, uint32_t mask) { bool enabled; - if (mask & KADM5_ATTRIBUTES && stage == KADM5_HOOK_STAGE_POSTCOMMIT) { + if (mask & KADM5_ATTRIBUTES && + code == 0 && stage == KADM5_HOOK_STAGE_POSTCOMMIT) { enabled = !(entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX); return sync_status(data, ctx, entry->principal, enabled); } return 0; } - - -/* The public symbol that Heimdal looks for. */ -struct kadm5_hook kadm5_hook_v0 = { - "krb5-sync", - KADM5_HOOK_VERSION_V0, - "Russ Allbery", - init, - fini, - chpass, - create, - modify -}; diff --git a/tests/plugin/heimdal-t.c b/tests/plugin/heimdal-t.c index 1dec427..a292bfa 100644 --- a/tests/plugin/heimdal-t.c +++ b/tests/plugin/heimdal-t.c @@ -28,33 +28,98 @@ * This is intentionally duplicated from the module so that the test will fail * if we change the interface in a way that isn't backward-compatible. */ -#define KADM5_HOOK_VERSION_V0 0 +#define KADM5_HOOK_VERSION_V1 1 enum kadm5_hook_stage { KADM5_HOOK_STAGE_PRECOMMIT = 0, KADM5_HOOK_STAGE_POSTCOMMIT = 1 }; -typedef struct kadm5_hook { - const char *name; +typedef struct kadm5_hook_ftable { int version; - const char *vendor; - - krb5_error_code (*init)(krb5_context, void **); - void (*fini)(krb5_context, void *); + krb5_error_code (KRB5_CALLCONV *init)(krb5_context, void **data); + void (KRB5_CALLCONV *fini)(void *data); - krb5_error_code (*chpass)(krb5_context, void *, enum kadm5_hook_stage, - krb5_principal, const char *); - krb5_error_code (*create)(krb5_context, void *, enum kadm5_hook_stage, - kadm5_principal_ent_t, uint32_t mask, - const char *password); - krb5_error_code (*modify)(krb5_context, void *, enum kadm5_hook_stage, - kadm5_principal_ent_t, uint32_t mask); -} kadm5_hook; + const char *name; + const char *vendor; + /* + * Hook functions; NULL functions are ignored. code is only valid on + * post-commit hooks and represents the result of the commit. Post- + * commit hooks are not called if a pre-commit hook aborted the call. + */ + krb5_error_code (KRB5_CALLCONV *chpass)(krb5_context context, + void *data, + 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); + + krb5_error_code (KRB5_CALLCONV *create)(krb5_context context, + void *data, + enum kadm5_hook_stage stage, + krb5_error_code code, + kadm5_principal_ent_t ent, + uint32_t mask, + const char *password); + + krb5_error_code (KRB5_CALLCONV *modify)(krb5_context context, + void *data, + enum kadm5_hook_stage stage, + krb5_error_code code, + kadm5_principal_ent_t ent, + uint32_t mask); + + krb5_error_code (KRB5_CALLCONV *delete)(krb5_context context, + void *data, + enum kadm5_hook_stage stage, + krb5_error_code code, + krb5_const_principal princ); + + krb5_error_code (KRB5_CALLCONV *randkey)(krb5_context context, + void *data, + enum kadm5_hook_stage stage, + krb5_error_code code, + krb5_const_principal princ); + + krb5_error_code (KRB5_CALLCONV *rename)(krb5_context context, + void *data, + enum kadm5_hook_stage stage, + krb5_error_code code, + krb5_const_principal source, + krb5_const_principal target); + + krb5_error_code (KRB5_CALLCONV *set_keys)(krb5_context context, + void *data, + 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 *keyblocks); + + 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; + +typedef krb5_error_code +(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); int -main(void) +main(int argc, char *argv[]) { char *path, *krb5_config, *plugin; krb5_error_code code; @@ -62,10 +127,14 @@ main(void) krb5_principal princ; void *handle = NULL; void *config = NULL; - struct kadm5_hook *hook = NULL; + size_t n_hooks = 0; + const struct kadm5_hook_ftable *const *hooks = NULL; + const struct kadm5_hook_ftable *hook; kadm5_principal_ent_rec entity; const char *message; + krb5_get_instance_func_t get_instance = NULL; char *wanted; + kadm5_hook_plugin_load_t hook_load; /* Set up the default krb5.conf file. */ path = test_file_path("data/krb5.conf"); @@ -100,11 +169,15 @@ main(void) test_file_path_free(plugin); /* Find the dispatch table and do a basic sanity check. */ - hook = dlsym(handle, "kadm5_hook_v0"); - if (hook == NULL) - bail("cannot get kadm5_hook_v0 symbol: %s", dlerror()); - if (hook->init == NULL) - bail("no init function found in module"); + hook_load = dlsym(handle, "kadm5_hook_plugin_load"); + if (hook_load == NULL) + bail("cannot get kadm5_hook_plugin_load symbol: %s", dlerror()); + is_int(0, hook_load(ctx, &get_instance, &n_hooks, &hooks), "load"); + is_int((long)get_instance("krb5"), (long)krb5_get_instance("krb5"), "Heimdal version"); + + is_int(1, n_hooks, "n_hooks"); + hook = hooks[0]; + is_int(0, hook->init(ctx, &config), "init"); /* No more skipping, so now we can report a plan. */ plan(13); @@ -112,7 +185,7 @@ main(void) /* Verify the metadata. */ is_string("krb5-sync", hook->name, "Module name"); is_string("Russ Allbery", hook->vendor, "Module vendor"); - is_int(KADM5_HOOK_VERSION_V0, hook->version, "Module version"); + is_int(KADM5_HOOK_VERSION_V1, hook->version, "Module version"); /* * Call init and chpass, which should fail with errors about queuing since @@ -120,17 +193,17 @@ main(void) */ basprintf(&wanted, "cannot open lock file queue/.lock: %s", strerror(ENOENT)); - is_int(0, hook->init(ctx, &config), "init"); ok(config != NULL, "...and config is not NULL"); - code = hook->chpass(ctx, config, KADM5_HOOK_STAGE_PRECOMMIT, princ, - "test"); + code = hook->chpass(ctx, config, KADM5_HOOK_STAGE_PRECOMMIT, 0, princ, + 0, 0, NULL, "test"); is_int(ENOENT, code, "chpass"); message = krb5_get_error_message(ctx, code); is_string(wanted, message, "...with correct error message"); krb5_free_error_message(ctx, message); /* Test chpass with a NULL password, which should do nothing. */ - code = hook->chpass(ctx, config, KADM5_HOOK_STAGE_PRECOMMIT, princ, NULL); + code = hook->chpass(ctx, config, KADM5_HOOK_STAGE_PRECOMMIT, 0, princ, + 0, 0, NULL, NULL); is_int(0, code, "chpass with NULL password"); /* @@ -143,7 +216,7 @@ main(void) entity.attributes = KRB5_KDB_DISALLOW_ALL_TIX; /* Test creation with no queue directory. */ - code = hook->create(ctx, config, KADM5_HOOK_STAGE_PRECOMMIT, &entity, 0, + code = hook->create(ctx, config, KADM5_HOOK_STAGE_PRECOMMIT, 0, &entity, 0, "test"); is_int(ENOENT, code, "create"); message = krb5_get_error_message(ctx, code); @@ -151,7 +224,7 @@ main(void) krb5_free_error_message(ctx, message); /* Test disabling with no queue directory. */ - code = hook->modify(ctx, config, KADM5_HOOK_STAGE_POSTCOMMIT, &entity, + code = hook->modify(ctx, config, KADM5_HOOK_STAGE_POSTCOMMIT, 0, &entity, KADM5_ATTRIBUTES); is_int(ENOENT, code, "modify"); message = krb5_get_error_message(ctx, code); @@ -159,12 +232,12 @@ main(void) krb5_free_error_message(ctx, message); /* Test creation with a NULL password, which should do nothing. */ - code = hook->create(ctx, config, KADM5_HOOK_STAGE_PRECOMMIT, &entity, 0, + code = hook->create(ctx, config, KADM5_HOOK_STAGE_PRECOMMIT, 0, &entity, 0, NULL); is_int(0, code, "create with NULL password"); /* Close down the module. */ - hook->fini(ctx, config); + hook->fini(config); if (dlclose(handle) != 0) bail("cannot close plugin: %s", dlerror()); free(wanted);