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 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 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 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 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 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 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 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 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 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 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 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 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 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