/* A list of plugin interface IDs. Make sure to increment
* PLUGIN_NUM_INTERFACES when a new interface is added. */
#define PLUGIN_INTERFACE_PWQUAL 0
-#define PLUGIN_NUM_INTERFACES 1
+#define PLUGIN_INTERFACE_KADM5_HOOK 1
+#define PLUGIN_NUM_INTERFACES 2
/* Retrieve the plugin module of type interface_id and name modname,
* storing the result into module. */
--- /dev/null
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * include/krb5/kadm5_hook_plugin.h
+ */
+/*
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#ifndef H_KRB5_KADM5_HOOK_PLUGIN
+#define H_KRB5_KADM5_HOOK_PLUGIN
+
+/**
+ * @file krb5/krb5_kadm5_hook_plugin.h
+ * Provide a plugin interface for kadm5 operations. This interface
+ * permits a plugin to intercept principal modification, creation and
+ * change password operations. Operations run at two stages: a
+ * precommit stage that runs before the operation is committed to the
+ * database and a postcommit operation that runs after the database
+ * is updated; see #kadm5_hook_stage for details on semantics.
+ *
+ * This interface is based on a proposed extension to Heimdal by Russ
+ * Allbery; it is likely that Heimdal will adopt an approach based on
+ * stacked kdb modules rather than this interface. For MIT, writing a
+ * plugin to this interface is significantly easier than stacking kdb
+ * modules. Also, the kadm5 interface is significantly more stable
+ * than the kdb interface, so this approach is more desirable than
+ * stacked kdb modules.
+ *
+ * This interface depends on kadm5/admin.h. As such, the interface
+ * does not provide strong guarantees of ABI stability.
+ */
+
+#include <krb5/krb5.h>
+#include <krb5/plugin.h>
+#include <kadm5/admin.h>
+
+/**
+ * Whether the operation is being run before or after the database
+ * update
+ */
+enum kadm5_hook_stage {
+ /** In this stage, any plugin failure prevents following plugins from
+ * running and aborts the operation.*/
+ KADM5_HOOK_STAGE_PRECOMMIT,
+ /** In this stage, plugin failures are logged but otherwise ignored.*/
+ KADM5_HOOK_STAGE_POSTCOMMIT
+};
+
+/** Opaque module data pointer*/
+typedef struct kadm5_hook_modinfo_st kadm5_hook_modinfo;
+
+/**
+ * Interface for the v1 virtual table for the kadm5_hook plugin
+ * All entry points are optional. The name field must be provided.
+ */
+typedef struct kadm5_hook_vtable_1_st {
+
+ /** A text string identifying the plugin for logging messages*/
+ char *name;
+
+ /** Initialize a plugin module
+ * @param modinfo returns newly allocated module info for future
+ * calls. Cleaned up by the fini() function.
+ */
+ kadm5_ret_t (*init)(krb5_context, kadm5_hook_modinfo **modinfo);
+
+ /** Clean up a module and free @a modinfo*/
+ void (*fini)(krb5_context, kadm5_hook_modinfo *modinfo);
+
+ /** Indicates that the password is being changed.
+ * @param stage is an integer from #kadm5_hook_stage enumeration
+ * @param keepold is true if existing keys are being kept.
+ * */
+ kadm5_ret_t (*chpass)(krb5_context,
+ kadm5_hook_modinfo *modinfo,
+ int stage,
+ krb5_principal, krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *newpass);
+
+ /** Indicate a principal is created*/
+ kadm5_ret_t (*create)(krb5_context,
+ kadm5_hook_modinfo *,
+ int stage,
+ kadm5_principal_ent_t, long mask,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password);
+ /** Modify a principal*/
+ kadm5_ret_t (*modify)(krb5_context,
+ kadm5_hook_modinfo *,
+ int stage,
+ kadm5_principal_ent_t, long mask);
+
+
+ /** Indicate a principal is deleted*/
+ kadm5_ret_t (* remove) (krb5_context,
+ kadm5_hook_modinfo *modinfo,
+ int stage, krb5_principal
+ );
+
+ /*End of minor version 1*/
+}kadm5_hook_vftable_1;
+
+#endif /*H_KRB5_KADM5_HOOK_PLUGIN*/
/* A pwqual_handle represents a password quality plugin module. */
typedef struct pwqual_handle_st *pwqual_handle;
+typedef struct kadm5_hook_handle_st *kadm5_hook_handle;
+
typedef struct _kadm5_server_handle_t {
krb5_ui_4 magic_number;
krb5_ui_4 struct_version;
struct _kadm5_server_handle_t *lhandle;
char **db_args;
pwqual_handle *qual_handles;
+ kadm5_hook_handle *hook_handles;
} kadm5_server_handle_rec, *kadm5_server_handle_t;
#define OSA_ADB_PRINC_VERSION_1 0x12345C01
pwqual_princ_initvt(krb5_context context, int maj_ver, int min_ver,
krb5_plugin_vtable vtable);
+/** @{
+ * @name kadm5_hook plugin support
+ */
+
+/**Load all kadm5_hook plugins*/
+krb5_error_code
+k5_kadm5_hook_load(krb5_context context,
+ kadm5_hook_handle **handles_out);
+
+/** Free handles allocated by k5_kadm5_hook_load()*/
+void
+k5_kadm5_hook_free_handles(krb5_context context, kadm5_hook_handle *handles);
+
+/**Call the chpass entry point on every kadm5_hook in @a handles*/
+kadm5_ret_t
+k5_kadm5_hook_chpass (krb5_context context,
+ kadm5_hook_handle *handles,
+ int stage, krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *newpass);
+
+/** Call the create entry point for kadm5_hook_plugins*/
+kadm5_ret_t
+k5_kadm5_hook_create (krb5_context context,
+ kadm5_hook_handle *handles,
+ int stage,
+ kadm5_principal_ent_t princ, long mask,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *newpass);
+
+/** Call modify kadm5_hook entry point*/
+kadm5_ret_t
+k5_kadm5_hook_modify (krb5_context context,
+ kadm5_hook_handle *handles,
+ int stage,
+ kadm5_principal_ent_t princ, long mask);
+
+/** call remove kadm5_hook entry point*/
+kadm5_ret_t
+k5_kadm5_hook_remove (krb5_context context,
+ kadm5_hook_handle *handles,
+ int stage,
+ krb5_principal princ);
+
+/** @}*/
+
+
+
+
#endif /* __KADM5_SERVER_INTERNAL_H__ */
RELDIR=kadm5/srv
SRCS = $(srcdir)/pwqual.c \
+ $(srcdir)/kadm5_hook.c \
$(srcdir)/pwqual_dict.c \
$(srcdir)/pwqual_empty.c \
$(srcdir)/pwqual_hesiod.c \
pwqual_empty.$(OBJEXT) \
pwqual_hesiod.$(OBJEXT) \
pwqual_princ.$(OBJEXT) \
+ kadm5_hook.$(OBJEXT) \
svr_policy.$(OBJEXT) \
svr_principal.$(OBJEXT) \
server_acl.$(OBJEXT) \
pwqual_empty.o \
pwqual_hesiod.o \
pwqual_princ.o \
+ kadm5_hook.o \
svr_policy.o \
svr_principal.o \
server_acl.o \
--- /dev/null
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * lib/kadm5/srv/kadm5_hook.c
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+/* Consumer interface for kadm5_hook plugins*/
+
+
+#include "k5-int.h"
+#include "server_internal.h"
+#include <krb5/kadm5_hook_plugin.h>
+#include <adm_proto.h>
+#include <syslog.h>
+
+struct kadm5_hook_handle_st {
+ kadm5_hook_vftable_1 vt;
+ kadm5_hook_modinfo *data;
+};
+
+krb5_error_code
+k5_kadm5_hook_load(krb5_context context,
+ kadm5_hook_handle **handles_out)
+{
+ krb5_error_code ret;
+ krb5_plugin_initvt_fn *modules = NULL, *mod;
+ size_t count;
+ kadm5_hook_handle *list = NULL, handle = NULL;
+
+ *handles_out = NULL;
+
+ ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_KADM5_HOOK, &modules);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Allocate a large enough list of handles. */
+ for (count = 0; modules[count] != NULL; count++);
+ list = k5alloc((count + 1) * sizeof(*list), &ret);
+ if (list == NULL)
+ goto cleanup;
+
+ /* For each module, allocate a handle, initialize its vtable, and initialize the module*/
+ count = 0;
+ for (mod = modules; *mod != NULL; mod++) {
+ handle = k5alloc(sizeof(*handle), &ret);
+ if (handle == NULL)
+ goto cleanup;
+ ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&handle->vt);
+ if (ret != 0) { /* Failed vtable init is non-fatal. */
+ free(handle);
+ handle = NULL;
+ continue;
+ }
+ handle->data = NULL;
+ if (handle->vt.init != NULL) {
+ ret = handle->vt.init(context, &handle->data);
+ if (ret != 0) /* Failed dictionary binding is fatal. */
+ goto cleanup;
+ }
+ list[count++] = handle;
+ list[count] = NULL;
+ handle = NULL;
+ }
+ list[count] = NULL;
+
+ ret = 0;
+ *handles_out = list;
+ list = NULL;
+
+cleanup:
+ free(handle);
+ k5_plugin_free_modules(context, modules);
+ k5_kadm5_hook_free_handles(context, list);
+ return ret;
+}
+
+void
+k5_kadm5_hook_free_handles(krb5_context context, kadm5_hook_handle *handles)
+{
+ kadm5_hook_handle *hp, handle;
+
+ if (handles == NULL)
+ return;
+ for (hp = handles; *hp != NULL; hp++) {
+ handle = *hp;
+ if (handle->vt.fini != NULL)
+ handle->vt.fini(context, handle->data);
+ }
+ free(handles);
+}
+
+static void
+log_failure(krb5_context context,
+ const char *name,
+ const char *function,
+ krb5_error_code ret)
+{
+ const char *e = krb5_get_error_message(context, ret);
+ if (e)
+ krb5_klog_syslog(LOG_ERR, "kadm5_hook %s failed postcommit %s: %s",
+ name, function, e);
+ krb5_free_error_message(context, e);
+}
+
+#define ITERATE(operation, params) \
+ for (; *handles; handles++) { \
+ kadm5_hook_handle h = *handles; \
+ krb5_error_code ret = 0; \
+ if (h->vt.operation) { \
+ ret = h->vt.operation params; \
+ } \
+ if (ret) { \
+ if (stage == KADM5_HOOK_STAGE_PRECOMMIT) \
+ return ret; \
+ else log_failure(context, h->vt.name, #operation, ret); \
+ } \
+ }
+
+
+kadm5_ret_t
+k5_kadm5_hook_chpass (krb5_context context,
+ kadm5_hook_handle *handles,
+ int stage, krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *newpass)
+{
+ ITERATE(chpass, (context, h->data,
+ stage, princ, keepold,
+ n_ks_tuple, ks_tuple, newpass));
+ return 0;
+}
+
+kadm5_ret_t
+k5_kadm5_hook_create (krb5_context context,
+ kadm5_hook_handle *handles,
+ int stage,
+ kadm5_principal_ent_t princ, long mask,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *newpass)
+{
+ ITERATE(create, (context, h->data,
+ stage, princ, mask, n_ks_tuple, ks_tuple, newpass));
+ return 0;
+}
+
+kadm5_ret_t
+k5_kadm5_hook_modify (krb5_context context,
+ kadm5_hook_handle *handles,
+ int stage,
+ kadm5_principal_ent_t princ, long mask)
+{
+ ITERATE(modify, (context, h->data,
+ stage, princ, mask));
+ return 0;
+}
+
+kadm5_ret_t
+k5_kadm5_hook_remove (krb5_context context,
+ kadm5_hook_handle *handles,
+ int stage,
+ krb5_principal princ)
+{
+ ITERATE(remove, (context, h->data,
+ stage, princ));
+ return 0;
+}
return ret;
}
+ ret = k5_kadm5_hook_load(context,&handle->hook_handles);
+ if (ret) {
+ krb5_db_fini(handle->context);
+ krb5_free_principal(handle->context, handle->current_caller);
+ free_db_args(handle);
+ free(handle);
+ return ret;
+ }
+
ret = init_pwqual(handle);
if (ret) {
+ k5_kadm5_hook_free_handles(context, handle->hook_handles);
krb5_db_fini(handle->context);
krb5_free_principal(handle->context, handle->current_caller);
free_db_args(handle);
destroy_pwqual(handle);
+ k5_kadm5_hook_free_handles(handle->context, handle->hook_handles);
adb_policy_close(handle);
krb5_db_fini(handle->context);
krb5_free_principal(handle->context, handle->current_caller);
*
* $Header$
*/
-
-#if !defined(lint) && !defined(__CODECENTER__)
-static char *rcsid = "$Header$";
-#endif
-
+#include <assert.h>
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>
#endif
+#include <krb5/kadm5_hook_plugin.h>
+
#ifdef USE_VALGRIND
#include <valgrind/memcheck.h>
#else
if (ret)
goto cleanup;
+ ret = k5_kadm5_hook_create(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_PRECOMMIT, entry, mask,
+ n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
+ n_ks_tuple?ks_tuple:handle->params.keysalts, password);
+ if (ret)
+ goto cleanup;
+
/* populate the admin-server-specific fields. In the OV server,
this used to be in a separate database. Since there's already
marshalling code for the admin fields, to keep things simple,
}
}
+ (void) k5_kadm5_hook_create(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask,
+ n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
+ n_ks_tuple?ks_tuple:handle->params.keysalts, password);
+
cleanup:
krb5_db_free_principal(handle->context, kdb);
if (have_polent)
if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
return(ret);
+ ret = k5_kadm5_hook_remove(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_PRECOMMIT, principal);
+ if (ret) {
+ kdb_free_entry(handle, kdb, &adb);
+ return ret;
+ }
if ((adb.aux_attributes & KADM5_POLICY)) {
if ((ret = kadm5_get_policy(handle->lhandle,
kdb_free_entry(handle, kdb, &adb);
+ if (ret == 0)
+ (void) k5_kadm5_hook_remove(handle->context,
+ handle->hook_handles,
+ KADM5_HOOK_STAGE_POSTCOMMIT, principal);
+
return ret;
}
/* let the mask propagate to the database provider */
kdb->mask = mask;
+ ret = k5_kadm5_hook_modify(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_PRECOMMIT, entry, mask);
+ if (ret)
+ goto done;
+
ret = kdb_put_entry(handle, kdb, &adb);
if (ret) goto done;
+ (void) k5_kadm5_hook_modify(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask);
ret = KADM5_OK;
done:
if (mask & KADM5_KVNO)
for (entry->kvno = 0, i=0; i<kdb->n_key_data; i++)
- if (kdb->key_data[i].key_data_kvno > entry->kvno)
+ if ((krb5_kvno) kdb->key_data[i].key_data_kvno > entry->kvno)
entry->kvno = kdb->key_data[i].key_data_kvno;
if (mask & KADM5_MKVNO) {
krb5_keyblock newkey, histkey;
krb5_error_code ret;
- for (x = 0; x < n_new_key_data; x++) {
+ assert (n_new_key_data >= 0);
+ for (x = 0; x < (unsigned) n_new_key_data; x++) {
ret = krb5_dbe_decrypt_key_data(context, NULL, &(new_key_data[x]),
&newkey, NULL);
if (ret)
return(ret);
for (y = 0; y < n_pw_hist_data; y++) {
- for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
+ for (z = 0; z < (unsigned int) pw_hist_data[y].n_key_data; z++) {
ret = krb5_dbe_decrypt_key_data(context, hist_keyblock,
&pw_hist_data[y].key_data[z],
&histkey, NULL);
knext = adb->old_key_next = 0;
/* free the old pw history entry if it contains data */
histp = &adb->old_keys[knext];
- for (i = 0; i < histp->n_key_data; i++)
+ for (i = 0; i < (unsigned int) histp->n_key_data; i++)
krb5_free_key_data_contents(context, &histp->key_data[i]);
free(histp->key_data);
KADM5_FAIL_AUTH_COUNT;
/* | KADM5_CPW_FUNCTION */
+ ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
+ n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
+ n_ks_tuple?ks_tuple:handle->params.keysalts,
+ password);
+ if (ret)
+ goto done;
+
if ((ret = kdb_put_entry(handle, kdb, &adb)))
goto done;
+ (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_POSTCOMMIT, principal, keepold,
+ n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
+ n_ks_tuple?ks_tuple:handle->params.keysalts,
+ password);
ret = KADM5_OK;
done:
if (!hist_added && hist.key_data)
kdb->mask = KADM5_KEY_DATA | KADM5_FAIL_AUTH_COUNT;
/* | KADM5_RANDKEY_USED */;
+ ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
+ n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
+ n_ks_tuple?ks_tuple:handle->params.keysalts,
+ NULL);
+ if (ret)
+ goto done;
if ((ret = kdb_put_entry(handle, kdb, &adb)))
goto done;
+ (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_POSTCOMMIT, principal, keepold,
+ n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
+ n_ks_tuple?ks_tuple:handle->params.keysalts,
+ NULL);
ret = KADM5_OK;
done:
kdb_free_entry(handle, kdb, &adb);
#include "k5-int.h"
const char *interface_names[PLUGIN_NUM_INTERFACES] = {
- "pwqual"
+ "pwqual",
+ "kadm5_hook"
};
/* Return the context's interface structure for id, or NULL if invalid. */