krb5/Add-KDC-pre-send-and-post-receive-KDC-hooks.patch

315 lines
11 KiB
Diff
Raw Normal View History

From 21330cb3db69fc5a004844a1e4dec8998eb50068 Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn@samba.org>
Date: Thu, 3 Mar 2016 18:53:31 +0100
Subject: [PATCH] Add KDC pre-send and post-receive KDC hooks
Add two new APIs, krb5_set_kdc_send_hook() and
krb5_set_kdc_recv_hook(), which can be used to inspect and override
messages sent to KDCs.
[ghudson@mit.edu: style and documentation changes]
ticket: 8386 (new)
---
doc/appdev/refs/api/index.rst | 2 +
doc/appdev/refs/types/index.rst | 2 +
src/include/k5-int.h | 6 +++
src/include/krb5/krb5.hin | 104 ++++++++++++++++++++++++++++++++++++++++
src/lib/krb5/libkrb5.exports | 2 +
src/lib/krb5/os/sendto_kdc.c | 56 +++++++++++++++++++++-
src/lib/krb5_32.def | 4 ++
7 files changed, 174 insertions(+), 2 deletions(-)
diff --git a/doc/appdev/refs/api/index.rst b/doc/appdev/refs/api/index.rst
index 8df351d..e97cbca 100644
--- a/doc/appdev/refs/api/index.rst
+++ b/doc/appdev/refs/api/index.rst
@@ -268,6 +268,8 @@ Rarely used public interfaces
krb5_server_decrypt_ticket_keytab.rst
krb5_set_default_tgs_enctypes.rst
krb5_set_error_message.rst
+ krb5_set_kdc_recv_hook.rst
+ krb5_set_kdc_send_hook.rst
krb5_set_real_time.rst
krb5_string_to_cksumtype.rst
krb5_string_to_deltat.rst
diff --git a/doc/appdev/refs/types/index.rst b/doc/appdev/refs/types/index.rst
index 51c4093..dc414cf 100644
--- a/doc/appdev/refs/types/index.rst
+++ b/doc/appdev/refs/types/index.rst
@@ -57,6 +57,8 @@ Public
krb5_pa_svr_referral_data.rst
krb5_pa_data.rst
krb5_pointer.rst
+ krb5_post_recv_fn.rst
+ krb5_pre_send_fn.rst
krb5_preauthtype.rst
krb5_principal.rst
krb5_principal_data.rst
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 6b7b2e3..045abfc 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -1238,6 +1238,12 @@ struct _krb5_context {
krb5_trace_callback trace_callback;
void *trace_callback_data;
+ krb5_pre_send_fn kdc_send_hook;
+ void *kdc_send_hook_data;
+
+ krb5_post_recv_fn kdc_recv_hook;
+ void *kdc_recv_hook_data;
+
struct plugin_interface plugins[PLUGIN_NUM_INTERFACES];
char *plugin_base_dir;
};
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index c93a0f2..2b0d59e 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -8300,6 +8300,110 @@ krb5_set_trace_callback(krb5_context context, krb5_trace_callback fn,
krb5_error_code KRB5_CALLCONV
krb5_set_trace_filename(krb5_context context, const char *filename);
+
+/**
+ * Hook function for inspecting or modifying messages sent to KDCs.
+ *
+ * If the hook function returns an error code, the KDC communication will be
+ * aborted and the error code will be returned to the library operation which
+ * initiated the communication.
+ *
+ * If the hook function sets @a reply_out, @a message will not be sent to the
+ * KDC, and the given reply will used instead.
+ *
+ * If the hook function sets @a new_message_out, the given message will be sent
+ * to the KDC in place of @a message.
+ *
+ * If the hook function returns successfully without setting either output,
+ * @a message will be sent to the KDC normally.
+ *
+ * The hook function should use krb5_copy_data() to construct the value for
+ * @a new_message_out or @a reply_out, to ensure that it can be freed correctly
+ * by the library.
+ *
+ * @param [in] context Library context
+ * @param [in] data Callback data
+ * @param [in] realm The realm the message will be sent to
+ * @param [in] message The original message to be sent to the KDC
+ * @param [out] new_message_out Optional replacement message to be sent
+ * @param [out] reply_out Optional synthetic reply
+ *
+ * @retval 0 Success
+ * @return A Kerberos error code
+ */
+typedef krb5_error_code
+(KRB5_CALLCONV *krb5_pre_send_fn)(krb5_context context, void *data,
+ const krb5_data *realm,
+ const krb5_data *message,
+ krb5_data **new_message_out,
+ krb5_data **new_reply_out);
+
+/**
+ * Hook function for inspecting or overriding KDC replies.
+ *
+ * If @a code is zero, @a reply contains the reply received from the KDC. The
+ * hook function may return an error code to simulate an error, may synthesize
+ * a different reply by setting @a new_reply_out, or may simply return
+ * successfully to do nothing.
+ *
+ * If @a code is non-zero, KDC communication failed and @a reply should be
+ * ignored. The hook function may return @a code or a different error code, or
+ * may synthesize a reply by setting @a new_reply_out and return successfully.
+ *
+ * The hook function should use krb5_copy_data() to construct the value for
+ * @a new_reply_out, to ensure that it can be freed correctly by the library.
+ *
+ * @param [in] context Library context
+ * @param [in] data Callback data
+ * @param [in] code Status of KDC communication
+ * @param [in] realm The realm the reply was received from
+ * @param [in] message The message sent to the realm's KDC
+ * @param [in] reply The reply received from the KDC
+ * @param [out] new_reply_out Optional replacement reply
+ *
+ * @retval 0 Success
+ * @return A Kerberos error code
+ */
+typedef krb5_error_code
+(KRB5_CALLCONV *krb5_post_recv_fn)(krb5_context context, void *data,
+ krb5_error_code code,
+ const krb5_data *realm,
+ const krb5_data *message,
+ const krb5_data *reply,
+ krb5_data **new_reply_out);
+
+/**
+ * Set a KDC pre-send hook function.
+ *
+ * @a send_hook will be called before messages are sent to KDCs by library
+ * functions such as krb5_get_credentials(). The hook function may inspect,
+ * override, or synthesize its own reply to the message.
+ *
+ * @param [in] context Library context
+ * @param [in] send_hook Hook function (or NULL to disable the hook)
+ * @param [in] data Callback data to be passed to @a send_hook
+ */
+void KRB5_CALLCONV
+krb5_set_kdc_send_hook(krb5_context context, krb5_pre_send_fn send_hook,
+ void *data);
+
+/**
+ * Set a KDC post-receive hook function.
+ *
+ * @a recv_hook will be called after a reply is received from a KDC during a
+ * call to a library function such as krb5_get_credentials(). The hook
+ * function may inspect or override the reply. This hook will not be executed
+ * if the pre-send hook returns a synthetic reply.
+ *
+ * @param [in] context The library context.
+ * @param [in] recv_hook Hook function (or NULL to disable the hook)
+ * @param [in] data Callback data to be passed to @a recv_hook
+ */
+void KRB5_CALLCONV
+krb5_set_kdc_recv_hook(krb5_context context, krb5_post_recv_fn recv_hook,
+ void *data);
+
+
#if TARGET_OS_MAC
# pragma pack(pop)
#endif
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index c623409..ea6982d 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -581,6 +581,8 @@ krb5_set_password
krb5_set_password_using_ccache
krb5_set_principal_realm
krb5_set_real_time
+krb5_set_kdc_send_hook
+krb5_set_kdc_recv_hook
krb5_set_time_offsets
krb5_set_trace_callback
krb5_set_trace_filename
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index 6231de2..be00b8f 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -403,6 +403,22 @@ check_for_svc_unavailable (krb5_context context,
return 1;
}
+void
+krb5_set_kdc_send_hook(krb5_context context, krb5_pre_send_fn send_hook,
+ void *data)
+{
+ context->kdc_send_hook = send_hook;
+ context->kdc_send_hook_data = data;
+}
+
+void
+krb5_set_kdc_recv_hook(krb5_context context, krb5_post_recv_fn recv_hook,
+ void *data)
+{
+ context->kdc_recv_hook = recv_hook;
+ context->kdc_recv_hook_data = data;
+}
+
/*
* send the formatted request 'message' to a KDC for realm 'realm' and
* return the response (if any) in 'reply'.
@@ -416,13 +432,16 @@ check_for_svc_unavailable (krb5_context context,
krb5_error_code
krb5_sendto_kdc(krb5_context context, const krb5_data *message,
- const krb5_data *realm, krb5_data *reply, int *use_master,
+ const krb5_data *realm, krb5_data *reply_out, int *use_master,
int no_udp)
{
krb5_error_code retval, err;
struct serverlist servers;
int server_used;
k5_transport_strategy strategy;
+ krb5_data reply = empty_data(), *hook_message = NULL, *hook_reply = NULL;
+
+ *reply_out = empty_data();
/*
* find KDC location(s) for realm
@@ -467,9 +486,26 @@ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
if (retval)
return retval;
+ if (context->kdc_send_hook != NULL) {
+ retval = context->kdc_send_hook(context, context->kdc_send_hook_data,
+ realm, message, &hook_message,
+ &hook_reply);
+ if (retval)
+ goto cleanup;
+
+ if (hook_reply != NULL) {
+ *reply_out = *hook_reply;
+ free(hook_reply);
+ goto cleanup;
+ }
+
+ if (hook_message != NULL)
+ message = hook_message;
+ }
+
err = 0;
retval = k5_sendto(context, message, realm, &servers, strategy, NULL,
- reply, NULL, NULL, &server_used,
+ &reply, NULL, NULL, &server_used,
check_for_svc_unavailable, &err);
if (retval == KRB5_KDC_UNREACH) {
if (err == KDC_ERR_SVC_UNAVAILABLE) {
@@ -480,9 +516,23 @@ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
realm->length, realm->data);
}
}
+
+ if (context->kdc_recv_hook != NULL) {
+ retval = context->kdc_recv_hook(context, context->kdc_recv_hook_data,
+ retval, realm, message, &reply,
+ &hook_reply);
+ }
if (retval)
goto cleanup;
+ if (hook_reply != NULL) {
+ *reply_out = *hook_reply;
+ free(hook_reply);
+ } else {
+ *reply_out = reply;
+ reply = empty_data();
+ }
+
/* Set use_master to 1 if we ended up talking to a master when we didn't
* explicitly request to. */
if (*use_master == 0) {
@@ -492,6 +542,8 @@ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
}
cleanup:
+ krb5_free_data(context, hook_message);
+ krb5_free_data_contents(context, &reply);
k5_free_serverlist(&servers);
return retval;
}
diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def
index 3734e9b..8d58ea1 100644
--- a/src/lib/krb5_32.def
+++ b/src/lib/krb5_32.def
@@ -463,3 +463,7 @@ EXPORTS
krb5_vwrap_error_message @430
krb5_c_prfplus @431
krb5_c_derive_prfplus @432
+
+; new in 1.15
+ krb5_set_kdc_send_hook @433
+ krb5_set_kdc_recv_hook @434
--
2.9.3