1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc_audit.c - Interface for KDC audit plugins. */
3 /*
4 * Copyright (C) 2013 by the Massachusetts Institute of Technology.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "k5-int.h"
34 #include "kdc_util.h"
35 #include "kdc_audit.h"
36 /* for krb5_klog_syslog */
37 #include <syslog.h>
38 #include "adm_proto.h"
39
40 struct audit_module_handle_st {
41 struct krb5_audit_vtable_st vt;
42 krb5_audit_moddata auctx;
43 };
44 typedef struct audit_module_handle_st *audit_module_handle;
45
46 static audit_module_handle *handles = NULL;
47
48 static void
free_handles(audit_module_handle * list)49 free_handles(audit_module_handle *list)
50 {
51 audit_module_handle *hp, hdl;
52
53 if (list == NULL)
54 return;
55
56 for (hp = list; *hp != NULL; hp++) {
57 hdl = *hp;
58 if (hdl->vt.close != NULL)
59 hdl->vt.close(hdl->auctx);
60 free(hdl);
61 }
62 free(list);
63 }
64
65 /*
66 * Load all available audit plugin modules and prepare for logging. The list of
67 * modules is stored as an array in handles. Use unload_audit_modules() to free
68 * resources allocated by this function.
69 */
70 krb5_error_code
load_audit_modules(krb5_context context)71 load_audit_modules(krb5_context context)
72 {
73 krb5_error_code ret = 0;
74 krb5_plugin_initvt_fn *modules = NULL, *mod;
75 struct krb5_audit_vtable_st vtable;
76 audit_module_handle *list = NULL, hdl = NULL;
77 krb5_audit_moddata auctx;
78 int count = 0;
79
80 if (context == NULL || handles != NULL)
81 return EINVAL;
82
83 /* Get audit plugin vtable. */
84 ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_AUDIT, &modules);
85 if (ret)
86 return ret;
87
88 /* Allocate handle, initialize vtable. */
89 for (count = 0; modules[count] != NULL; count++);
90 list = k5calloc(count + 1, sizeof(*list), &ret);
91 if (list == NULL)
92 goto cleanup;
93 count = 0;
94 for (mod = modules; *mod != NULL; mod++) {
95 hdl = k5alloc(sizeof(*hdl), &ret);
96 if (hdl == NULL)
97 goto cleanup;
98 ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&hdl->vt);
99 if (ret) {
100 free(hdl);
101 hdl = NULL;
102 continue;
103 }
104
105 vtable = hdl->vt;
106 if (vtable.open != NULL) {
107 ret = vtable.open(&auctx);
108 if (ret) {
109 krb5_klog_syslog(LOG_ERR,
110 _("audit plugin %s failed to open. error=%i"),
111 vtable.name, ret);
112 goto cleanup;
113 }
114 hdl->auctx = auctx;
115 }
116 list[count++] = hdl;
117 list[count] = NULL;
118 hdl = NULL;
119 }
120 list[count] = NULL;
121 handles = list;
122 list = NULL;
123 ret = 0;
124
125 cleanup:
126 free(hdl);
127 k5_plugin_free_modules(context, modules);
128 free_handles(list);
129 return ret;
130 }
131
132 /* Free resources allocated by load_audit_modules() function. */
133 void
unload_audit_modules(krb5_context context)134 unload_audit_modules(krb5_context context)
135 {
136 free_handles(handles);
137 }
138
139 /*
140 * Write the output ticket ID into newly-allocated buffer.
141 * Returns 0 on success.
142 */
143 krb5_error_code
kau_make_tkt_id(krb5_context context,const krb5_ticket * ticket,char ** out)144 kau_make_tkt_id(krb5_context context,
145 const krb5_ticket *ticket, char **out)
146 {
147 krb5_error_code ret = 0;
148 char *hash = NULL, *ptr;
149 uint8_t hashbytes[K5_SHA256_HASHLEN];
150 unsigned int i;
151
152 *out = NULL;
153
154 if (ticket == NULL)
155 return EINVAL;
156
157 ret = k5_sha256(&ticket->enc_part.ciphertext, 1, hashbytes);
158 if (ret)
159 return ret;
160
161 hash = k5alloc(sizeof(hashbytes) * 2 + 1, &ret);
162 if (hash == NULL)
163 return ret;
164
165 for (i = 0, ptr = hash; i < sizeof(hashbytes); i++, ptr += 2)
166 snprintf(ptr, 3, "%02X", hashbytes[i]);
167 *ptr = '\0';
168 *out = hash;
169
170 return 0;
171 }
172
173 /*
174 * Create and initialize krb5_audit_state structure.
175 * Returns 0 on success.
176 */
177 krb5_error_code
kau_init_kdc_req(krb5_context context,krb5_kdc_req * request,const krb5_fulladdr * from,krb5_audit_state ** state_out)178 kau_init_kdc_req(krb5_context context,
179 krb5_kdc_req *request, const krb5_fulladdr *from,
180 krb5_audit_state **state_out)
181 {
182 krb5_error_code ret = 0;
183 krb5_audit_state *state = NULL;
184
185 state = k5calloc(1, sizeof(*state), &ret);
186 if (state == NULL)
187 return ret;
188
189 state->request = request;
190 state->cl_addr = from->address;
191 state->cl_port = from->port;
192 state->stage = AUTHN_REQ_CL;
193 ret = krb5int_random_string(context, state->req_id,
194 sizeof(state->req_id));
195 if (ret) {
196 free(state);
197 return ret;
198 }
199 *state_out = state;
200
201 return 0;
202 }
203
204 /* Free resources allocated by kau_init_kdc_req() and kau_make_tkt_id()
205 * routines. */
206 void
kau_free_kdc_req(krb5_audit_state * state)207 kau_free_kdc_req(krb5_audit_state *state)
208 {
209 free(state->tkt_in_id);
210 free(state->tkt_out_id);
211 free(state->evid_tkt_id);
212 free(state);
213 }
214
215 /* Call the KDC start/stop audit plugin entry points. */
216
217 void
kau_kdc_stop(krb5_context context,const krb5_boolean ev_success)218 kau_kdc_stop(krb5_context context, const krb5_boolean ev_success)
219 {
220 audit_module_handle *hp, hdl;
221
222 if (handles == NULL)
223 return;
224
225 for (hp = handles; *hp != NULL; hp++) {
226 hdl = *hp;
227 if (hdl->vt.kdc_stop != NULL)
228 hdl->vt.kdc_stop(hdl->auctx, ev_success);
229 }
230 }
231
232 void
kau_kdc_start(krb5_context context,const krb5_boolean ev_success)233 kau_kdc_start(krb5_context context, const krb5_boolean ev_success)
234 {
235 audit_module_handle *hp, hdl;
236
237 if (handles == NULL)
238 return;
239
240 for (hp = handles; *hp != NULL; hp++) {
241 hdl = *hp;
242 if (hdl->vt.kdc_start != NULL)
243 hdl->vt.kdc_start(hdl->auctx, ev_success);
244 }
245 }
246
247 /* Call the AS-REQ audit plugin entry point. */
248 void
kau_as_req(krb5_context context,const krb5_boolean ev_success,krb5_audit_state * state)249 kau_as_req(krb5_context context, const krb5_boolean ev_success,
250 krb5_audit_state *state)
251 {
252 audit_module_handle *hp, hdl;
253
254 if (handles == NULL)
255 return;
256
257 for (hp = handles; *hp != NULL; hp++) {
258 hdl = *hp;
259 if (hdl->vt.as_req != NULL)
260 hdl->vt.as_req(hdl->auctx, ev_success, state);
261 }
262 }
263
264 /* Call the TGS-REQ audit plugin entry point. */
265 void
kau_tgs_req(krb5_context context,const krb5_boolean ev_success,krb5_audit_state * state)266 kau_tgs_req(krb5_context context, const krb5_boolean ev_success,
267 krb5_audit_state *state)
268 {
269 audit_module_handle *hp, hdl;
270
271 if (handles == NULL)
272 return;
273
274 for (hp = handles; *hp != NULL; hp++) {
275 hdl = *hp;
276 if (hdl->vt.tgs_req != NULL)
277 hdl->vt.tgs_req(hdl->auctx, ev_success, state);
278 }
279 }
280
281 /* Call the S4U2Self audit plugin entry point. */
282 void
kau_s4u2self(krb5_context context,const krb5_boolean ev_success,krb5_audit_state * state)283 kau_s4u2self(krb5_context context, const krb5_boolean ev_success,
284 krb5_audit_state *state)
285 {
286 audit_module_handle *hp, hdl;
287
288 if (handles == NULL)
289 return;
290
291 for (hp = handles; *hp != NULL; hp++) {
292 hdl = *hp;
293 if (hdl->vt.tgs_s4u2self != NULL)
294 hdl->vt.tgs_s4u2self(hdl->auctx, ev_success, state);
295 }
296 }
297
298 /* Call the S4U2Proxy audit plugin entry point. */
299 void
kau_s4u2proxy(krb5_context context,const krb5_boolean ev_success,krb5_audit_state * state)300 kau_s4u2proxy(krb5_context context,const krb5_boolean ev_success,
301 krb5_audit_state *state)
302 {
303 audit_module_handle *hp, hdl;
304
305 if (handles == NULL)
306 return;
307
308 for (hp = handles; *hp != NULL; hp++) {
309 hdl = *hp;
310 if (hdl->vt.tgs_s4u2proxy != NULL)
311 hdl->vt.tgs_s4u2proxy(hdl->auctx, ev_success, state);
312 }
313 }
314
315 /* Call the U2U audit plugin entry point. */
316 void
kau_u2u(krb5_context context,const krb5_boolean ev_success,krb5_audit_state * state)317 kau_u2u(krb5_context context, const krb5_boolean ev_success,
318 krb5_audit_state *state)
319 {
320 audit_module_handle *hp, hdl;
321
322 if (handles == NULL)
323 return;
324
325 for (hp = handles; *hp != NULL; hp++) {
326 hdl = *hp;
327 if (hdl->vt.tgs_u2u != NULL)
328 hdl->vt.tgs_u2u(hdl->auctx, ev_success, state);
329 }
330 }
331