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