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
					Luke Howard
				
			
				
					committed by
					
						 Nico Williams
						Nico Williams
					
				
			
			
				
	
			
			
			 Nico Williams
						Nico Williams
					
				
			
						parent
						
							e9b3b2326d
						
					
				
				
					commit
					803efebca5
				
			| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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 */ | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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__ */ | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user