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
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'.
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
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) {
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) {
}
cleanup:
+ krb5_free_data(context, hook_message);
+ krb5_free_data_contents(context, &reply);
k5_free_serverlist(&servers);
return retval;
}