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 struct sockaddr * from,krb5_audit_state ** state_out)178 kau_init_kdc_req(krb5_context context,
179 krb5_kdc_req *request, const struct sockaddr *from,
180 krb5_audit_state **state_out)
181 {
182 krb5_error_code ret = 0;
183 krb5_audit_state *state = NULL;
184 krb5_address addr;
185 const krb5_address unknown_addr = { KV5M_ADDRESS, 0, 0, NULL };
186
187 state = k5calloc(1, sizeof(*state), &ret);
188 if (state == NULL)
189 return ret;
190
191 ret = k5_sockaddr_to_address(from, TRUE, &addr);
192 if (ret)
193 addr = unknown_addr;
194 ret = krb5_copy_addr(context, &addr, &state->cl_addr);
195 if (ret)
196 goto cleanup;
197 state->request = request;
198 state->cl_port = sa_getport(from);
199 state->stage = AUTHN_REQ_CL;
200 ret = krb5int_random_string(context, state->req_id,
201 sizeof(state->req_id));
202 if (ret)
203 goto cleanup;
204 *state_out = state;
205 state = NULL;
206
207 cleanup:
208 kau_free_kdc_req(state);
209 return ret;
210 }
211
212 /* Free resources allocated by kau_init_kdc_req() and kau_make_tkt_id()
213 * routines. */
214 void
kau_free_kdc_req(krb5_audit_state * state)215 kau_free_kdc_req(krb5_audit_state *state)
216 {
217 if (state == NULL)
218 return;
219 free(state->tkt_in_id);
220 free(state->tkt_out_id);
221 free(state->evid_tkt_id);
222 krb5_free_address(NULL, state->cl_addr);
223 free(state);
224 }
225
226 /* Call the KDC start/stop audit plugin entry points. */
227
228 void
kau_kdc_stop(krb5_context context,const krb5_boolean ev_success)229 kau_kdc_stop(krb5_context context, const krb5_boolean ev_success)
230 {
231 audit_module_handle *hp, hdl;
232
233 if (handles == NULL)
234 return;
235
236 for (hp = handles; *hp != NULL; hp++) {
237 hdl = *hp;
238 if (hdl->vt.kdc_stop != NULL)
239 hdl->vt.kdc_stop(hdl->auctx, ev_success);
240 }
241 }
242
243 void
kau_kdc_start(krb5_context context,const krb5_boolean ev_success)244 kau_kdc_start(krb5_context context, const krb5_boolean ev_success)
245 {
246 audit_module_handle *hp, hdl;
247
248 if (handles == NULL)
249 return;
250
251 for (hp = handles; *hp != NULL; hp++) {
252 hdl = *hp;
253 if (hdl->vt.kdc_start != NULL)
254 hdl->vt.kdc_start(hdl->auctx, ev_success);
255 }
256 }
257
258 /* Call the AS-REQ audit plugin entry point. */
259 void
kau_as_req(krb5_context context,const krb5_boolean ev_success,krb5_audit_state * state)260 kau_as_req(krb5_context context, const krb5_boolean ev_success,
261 krb5_audit_state *state)
262 {
263 audit_module_handle *hp, hdl;
264
265 if (handles == NULL)
266 return;
267
268 for (hp = handles; *hp != NULL; hp++) {
269 hdl = *hp;
270 if (hdl->vt.as_req != NULL)
271 hdl->vt.as_req(hdl->auctx, ev_success, state);
272 }
273 }
274
275 /* Call the TGS-REQ audit plugin entry point. */
276 void
kau_tgs_req(krb5_context context,const krb5_boolean ev_success,krb5_audit_state * state)277 kau_tgs_req(krb5_context context, const krb5_boolean ev_success,
278 krb5_audit_state *state)
279 {
280 audit_module_handle *hp, hdl;
281
282 if (handles == NULL)
283 return;
284
285 for (hp = handles; *hp != NULL; hp++) {
286 hdl = *hp;
287 if (hdl->vt.tgs_req != NULL)
288 hdl->vt.tgs_req(hdl->auctx, ev_success, state);
289 }
290 }
291
292 /* Call the S4U2Self audit plugin entry point. */
293 void
kau_s4u2self(krb5_context context,const krb5_boolean ev_success,krb5_audit_state * state)294 kau_s4u2self(krb5_context context, const krb5_boolean ev_success,
295 krb5_audit_state *state)
296 {
297 audit_module_handle *hp, hdl;
298
299 if (handles == NULL)
300 return;
301
302 for (hp = handles; *hp != NULL; hp++) {
303 hdl = *hp;
304 if (hdl->vt.tgs_s4u2self != NULL)
305 hdl->vt.tgs_s4u2self(hdl->auctx, ev_success, state);
306 }
307 }
308
309 /* Call the S4U2Proxy audit plugin entry point. */
310 void
kau_s4u2proxy(krb5_context context,const krb5_boolean ev_success,krb5_audit_state * state)311 kau_s4u2proxy(krb5_context context,const krb5_boolean ev_success,
312 krb5_audit_state *state)
313 {
314 audit_module_handle *hp, hdl;
315
316 if (handles == NULL)
317 return;
318
319 for (hp = handles; *hp != NULL; hp++) {
320 hdl = *hp;
321 if (hdl->vt.tgs_s4u2proxy != NULL)
322 hdl->vt.tgs_s4u2proxy(hdl->auctx, ev_success, state);
323 }
324 }
325
326 /* Call the U2U audit plugin entry point. */
327 void
kau_u2u(krb5_context context,const krb5_boolean ev_success,krb5_audit_state * state)328 kau_u2u(krb5_context context, const krb5_boolean ev_success,
329 krb5_audit_state *state)
330 {
331 audit_module_handle *hp, hdl;
332
333 if (handles == NULL)
334 return;
335
336 for (hp = handles; *hp != NULL; hp++) {
337 hdl = *hp;
338 if (hdl->vt.tgs_u2u != NULL)
339 hdl->vt.tgs_u2u(hdl->auctx, ev_success, state);
340 }
341 }
342