summaryrefslogtreecommitdiffstats
path: root/Add-KDC-pre-send-and-post-receive-KDC-hooks.patch
blob: 17ecec623d2ff57942509881fdfa4a1dd9c8f5e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
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