# plugins/authdata/greet
SUBDIRS=util include lib \
@sam2_plugin@ \
+ plugins/audit \
+ plugins/audit/test \
+ @audit_plugin@ \
plugins/kadm5_hook/test \
plugins/hostrealm/test \
plugins/localauth/test \
AC_DEFINE(KRBCONF_VAGUE_ERRORS,1,[Define if the KDC should return only vague error codes to clients])
fi
+# Check which (if any) audit plugin to build
+audit_plugin=""
+AC_ARG_ENABLE([audit-plugin],
+AC_HELP_STRING([--enable-audit-plugin=IMPL],
+ [use audit plugin @<:@ do not use audit @:>@]), , enableval=no)
+if test "$enableval" != no; then
+ case "$enableval" in
+ simple)
+ # if audit_log_user_message is found, we assume
+ # that audit_open and audit_close are also defined.
+ AC_CHECK_LIB(audit, audit_log_user_message,
+ [AUDIT_IMPL_LIBS=-laudit
+ K5_GEN_MAKEFILE(plugins/audit/simple)
+ audit_plugin=plugins/audit/simple ],
+ AC_MSG_ERROR([libaudit not found or undefined symbol audit_log_user_message]))
+ ;;
+ *)
+ AC_MSG_ERROR([Unknown audit plugin implementation $enableval.])
+ ;;
+ esac
+fi
+AC_SUBST(AUDIT_IMPL_LIBS)
+AC_SUBST(audit_plugin)
+
# WITH_CRYPTO_IMPL
CRYPTO_IMPL="builtin"
plugins/localauth/test
plugins/kadm5_hook/test
plugins/pwqual/test
+ plugins/audit
+ plugins/audit/test
plugins/kdb/db2
plugins/kdb/db2/libdb2
plugins/kdb/db2/libdb2/hash
#define PLUGIN_INTERFACE_CCSELECT 4
#define PLUGIN_INTERFACE_LOCALAUTH 5
#define PLUGIN_INTERFACE_HOSTREALM 6
-#define PLUGIN_NUM_INTERFACES 7
+#define PLUGIN_INTERFACE_AUDIT 7
+#define PLUGIN_NUM_INTERFACES 8
/* Retrieve the plugin module of type interface_id and name modname,
* storing the result into module. */
--- /dev/null
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/krb5/audit_plugin.h - Audit plugin interface */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * NOTE: This is a private interface and may change incompatibly
+ * between versions.
+ */
+/*
+ * Declarations for KDC audit plugin module implementers. Audit modules allow
+ * the KDC to produce log output or audit records in any desired form.
+ *
+ * The audit interface has a single supported major version, which is 1. Major
+ * version 1 has a current minor version of 1. Audit modules should define a
+ * function named audit_<modulename>_initvt, matching the signature:
+ *
+ * krb5_error_code
+ * audit_modname_initvt(krb5_context context, int maj_ver, int min_ver,
+ * krb5_plugin_vtable vtable);
+ *
+ * The initvt function should:
+ *
+ * - Check that the supplied maj_ver number is supported by the module, or
+ * return KRB5_PLUGIN_VER_NOTSUPP if it is not.
+ *
+ * - Cast the vtable pointer as appropriate for the interface and maj_ver:
+ * maj_ver == 1: Cast to krb5_audit_vtable
+ *
+ * - Initialize the methods of the vtable, stopping as appropriate for the
+ * supplied min_ver. Optional methods may be left uninitialized.
+ *
+ * Memory for the vtable is allocated by the caller, not by the module.
+ */
+
+#ifndef KRB5_AU_PLUGIN_H_INCLUDED
+#define KRB5_AU_PLUGIN_H_INCLUDED
+#include <krb5/krb5.h>
+
+/** KDC processing steps */
+#define AUTHN_REQ_CL 1 /**< Authenticate request and client */
+#define SRVC_PRINC 2 /**< Determine service principal */
+#define VALIDATE_POL 3 /**< Validate local and protocol policies */
+#define ISSUE_TKT 4 /**< Issue ticket */
+#define ENCR_REP 5 /**< Encrypt reply */
+
+/** Types of violations */
+#define PROT_CONSTRAINT 1 /**< Protocol constraint */
+#define LOCAL_POLICY 2 /**< Local policy violation */
+
+#define REQID_LEN 32 /* Size of the alpha-numeric request ID */
+
+/** KDC audit state structure and declarations */
+typedef struct _krb5_audit_state {
+ krb5_kdc_req *request;
+ krb5_kdc_rep *reply;
+ krb5_address *cl_addr; /**< client address */
+ krb5_ui_4 cl_port; /**< client port */
+ int stage; /**< step in KDC processing */
+ const char *status; /**< KDC status message */
+ char *tkt_in_id; /**< primary (TGT) ticket ID */
+ char *tkt_out_id; /**< derived (service or referral TGT) ticket ID */
+ /** for s4u2proxy - evidence ticket ID; for u2u - second ticket ID */
+ char *evid_tkt_id;
+ char req_id[REQID_LEN]; /**< request ID */
+ krb5_data *cl_realm; /**< referrals: remote client's realm */
+ krb5_principal s4u2self_user; /**< impersonated user */
+ int violation; /**< local or protocol policy problem */
+} krb5_audit_state;
+
+/** An abstract type for audit module data. */
+typedef struct krb5_audit_moddata_st *krb5_audit_moddata;
+
+/*
+ * Mandatory:
+ * - krb5_audit_open_fn,
+ * Open connection to the audit system and initialize audit module data. If
+ * the underlying (OS or third party) audit facility fails to open, no
+ * auditable KDC events should be recorded.
+ */
+typedef krb5_error_code
+(*krb5_audit_open_fn)(krb5_audit_moddata *auctx);
+
+/*
+ * Mandatory:
+ * - krb5_audit_close_fn.
+ * Close connection to the underlying audit system.
+ */
+typedef krb5_error_code
+(*krb5_audit_close_fn)(krb5_audit_moddata auctx);
+
+/**
+ * Log KDC-start event.
+ *
+ * @param [in] auctx Audit context
+ * @param [in] ev_success Success/failure of the event being audited
+ *
+ * @note Optional.
+ *
+ * @retval 0 Success; otherwise - Kerberos error codes
+ */
+typedef krb5_error_code
+(*krb5_audit_kdc_start_fn)(krb5_audit_moddata auctx, krb5_boolean ev_success);
+
+/**
+ * Log KDC-stop event.
+ *
+ * @param [in] auctx Audit context
+ * @param [in] ev_success Success/failure of the event being audited
+ *
+ * @note Optional.
+ *
+ * @retval 0 Success; otherwise - Kerberos error codes
+ */
+typedef krb5_error_code
+(*krb5_audit_kdc_stop_fn)(krb5_audit_moddata auctx, krb5_boolean ev_success);
+
+/**
+ * Log AS exchange event.
+ *
+ * @param [in] auctx Audit context
+ * @param [in] ev_success Success/failure of the event being audited
+ * @param [in] state AS-request related auditable information
+ *
+ * The @a state provides the following data:
+ * - Full information about KDC request, assigned request ID, client address
+ * and port, and stage of the AS exchange
+ * - If available, the information about the encryption types of the short- and
+ * long-term keys, non-local client's referral realm, KDC status, the TGT
+ * and its ticket ID
+ *
+ * @note Optional.
+ *
+ * @retval 0 Success; otherwise - Kerberos error codes
+ */
+typedef krb5_error_code
+(*krb5_audit_as_req_fn)(krb5_audit_moddata auctx,
+ krb5_boolean ev_success, krb5_audit_state *state);
+
+/**
+ * Log TGS exchange event.
+ *
+ * @param [in] auctx Audit context
+ * @param [in] ev_success Success/failure of the event being audited
+ * @param [in] state TGS-request related auditable information
+ *
+ * The @a state provides the following data:
+ * - Full information about KDC request, assigned request ID, primary ticket
+ * ID, client address and port, and stage of the TGS exchange
+ * - If available, the information about the encryption types of the short- and
+ * long-term keys, KDC status, KDC reply, and the output ticket ID
+ *
+ * @note Optional.
+ *
+ * @retval 0 Success; otherwise - Kerberos error codes
+ */
+typedef krb5_error_code
+(*krb5_audit_tgs_req_fn)(krb5_audit_moddata auctx,
+ krb5_boolean ev_success, krb5_audit_state *state);
+
+/**
+ * Log S4U2SELF event.
+ *
+ * @param [in] auctx Audit context
+ * @param [in] ev_success Report on success or failure
+ * @param [in] state s4u2self related auditable information
+ *
+ * The @a state provides the following data:
+ * - Full information about KDC request, assigned request ID, client address
+ * and port, and stage of the TGS exchange
+ * - Requesting server's TGT ID, impersonated user principal name, and service
+ * "to self" ticket or referral TGT ID
+ * - If available, KDC status, local policy violation or S4U protocol
+ * constraints
+ *
+ * @note Optional.
+ *
+ * @retval 0 Success; otherwise - Kerberos error codes
+ */
+typedef krb5_error_code
+(*krb5_audit_s4u2self_fn)(krb5_audit_moddata auctx,
+ krb5_boolean ev_success, krb5_audit_state *state);
+
+/**
+ * Log S4U2PROXY event.
+ *
+ * @param [in] auctx Audit context
+ * @param [in] ev_success Report on success or failure
+ * @param [in] state s4u2proxy related auditable information
+ *
+ * The @a state provides the following data:
+ * - Full information about request, assigned request ID, client address and
+ * port, and stage of the TGS exchange
+ * - Requesting server's TGT ID, delegated user principal name, and evidence
+ * ticket ID
+ * - If available, KDC status, local policy violation or S4U protocol
+ * constraints
+ *
+ * @note Optional.
+ *
+ * @retval 0 Success; otherwise - Kerberos error codes
+ */
+typedef krb5_error_code
+(*krb5_audit_s4u2proxy_fn)(krb5_audit_moddata auctx,
+ krb5_boolean ev_success, krb5_audit_state *state);
+
+/**
+ * Log U2U event.
+ *
+ * @param [in] auctx Audit context
+ * @param [in] ev_success Report on success or failure
+ * @param [in] state user-to-user related auditable information
+ *
+ * The @a state provides the following data:
+ * - Full information about request, assigned request ID, client address and
+ * port, and stage of the TGS exchange,
+ * - Requestor's TGT ID, service ticket ID, and client's principal name in the
+ * second ticket
+ * - If available, KDC status
+ *
+ * @note Optional.
+ *
+ * @retval 0 Success; otherwise - Kerberos error codes
+ */
+typedef krb5_error_code
+(*krb5_audit_u2u_fn)(krb5_audit_moddata auctx,
+ krb5_boolean ev_success, krb5_audit_state *state);
+
+/* vtable declaration */
+typedef struct krb5_audit_vtable_st {
+ /* Mandatory: name of module. */
+ char *name;
+ krb5_audit_open_fn open;
+ krb5_audit_close_fn close;
+ krb5_audit_kdc_start_fn kdc_start;
+ krb5_audit_kdc_stop_fn kdc_stop;
+ krb5_audit_as_req_fn as_req;
+ krb5_audit_tgs_req_fn tgs_req;
+ krb5_audit_s4u2self_fn tgs_s4u2self;
+ krb5_audit_s4u2proxy_fn tgs_s4u2proxy;
+ krb5_audit_u2u_fn tgs_u2u;
+} *krb5_audit_vtable;
+
+#endif /* KRB5_AU_PLUGIN_H_INCLUDED */
$(srcdir)/extern.c \
$(srcdir)/replay.c \
$(srcdir)/kdc_authdata.c \
+ $(srcdir)/kdc_audit.c \
$(srcdir)/kdc_transit.c \
$(srcdir)/tgs_policy.c
extern.o \
replay.o \
kdc_authdata.o \
+ kdc_audit.o \
kdc_transit.o \
tgs_policy.o
/* kdc/do_as_req.c */
/*
* Portions Copyright (C) 2007 Apple Inc.
- * Copyright 1990,1991,2007,2008,2009 by the Massachusetts Institute of Technology.
- * All Rights Reserved.
+ * Copyright 1990, 1991, 2007, 2008, 2009, 2013 by the Massachusetts Institute
+ * of Technology. All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
#endif /* HAVE_NETINET_IN_H */
#include "kdc_util.h"
+#include "kdc_audit.h"
#include "policy.h"
#include <kadm5/admin.h>
#include "adm_proto.h"
krb5_error_code preauth_err;
kdc_realm_t *active_realm;
+ krb5_audit_state *au_state;
};
static void
loop_respond_fn oldrespond;
void *oldarg;
kdc_realm_t *kdc_active_realm = state->active_realm;
+ krb5_audit_state *au_state = state->au_state;
assert(state);
oldrespond = state->respond;
if (errcode)
goto egress;
+ au_state->stage = ENCR_REP;
+
if ((errcode = validate_forwardable(state->request, *state->client,
*state->server, state->kdc_time,
&state->status))) {
state->status = "ENCRYPTING_TICKET";
goto egress;
}
+
+ errcode = kau_make_tkt_id(kdc_context, &state->ticket_reply,
+ &au_state->tkt_out_id);
+ if (errcode) {
+ state->status = "GENERATE_TICKET_ID";
+ goto egress;
+ }
+
state->ticket_reply.enc_part.kvno = server_key->key_data_kvno;
errcode = kdc_fast_response_handle_padata(state->rstate,
state->request,
egress:
if (errcode != 0)
assert (state->status != 0);
+
+ au_state->status = state->status;
+ au_state->reply = &state->reply;
+ kau_as_req(kdc_context,
+ (errcode || state->preauth_err) ? FALSE : TRUE, au_state);
+ kau_free_kdc_req(au_state);
+
free_padata_context(kdc_context, state->pa_context);
if (as_encrypting_key)
krb5_free_keyblock(kdc_context, as_encrypting_key);
krb5_data encoded_req_body;
krb5_enctype useenctype;
struct as_req_state *state;
+ krb5_audit_state *au_state = NULL;
state = k5alloc(sizeof(*state), &errcode);
if (state == NULL) {
errcode = kdc_make_rstate(kdc_active_realm, &state->rstate);
if (errcode != 0) {
(*respond)(arg, errcode, NULL);
+ free(state);
return;
}
+
+ /* Initialize audit state. */
+ errcode = kau_init_kdc_req(kdc_context, state->request, from, &au_state);
+ if (errcode) {
+ (*respond)(arg, errcode, NULL);
+ kdc_free_rstate(state->rstate);
+ free(state);
+ return;
+ }
+ state->au_state = au_state;
+
if (state->request->msg_type != KRB5_AS_REQ) {
state->status = "msg_type mismatch";
errcode = KRB5_BADMSGTYPE;
goto errout;
}
+
+ /* Seed the audit trail with the request ID and basic information. */
+ kau_as_req(kdc_context, TRUE, au_state);
+
if (fetch_asn1_field((unsigned char *) req_pkt->data,
1, 4, &encoded_req_body) != 0) {
errcode = ASN1_BAD_ID;
goto errout;
}
}
+ au_state->request = state->request;
state->rock.request = state->request;
state->rock.inner_body = state->inner_body;
state->rock.rstate = state->rstate;
if (!is_local_principal(kdc_active_realm, state->client->princ)) {
/* Entry is a referral to another realm */
state->status = "REFERRAL";
+ au_state->cl_realm = &state->client->princ->realm;
errcode = KRB5KDC_ERR_WRONG_REALM;
goto errout;
}
+ au_state->stage = SRVC_PRINC;
+
if (!state->request->server) {
state->status = "NULL_SERVER";
errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
goto errout;
}
+ au_state->stage = VALIDATE_POL;
+
if ((errcode = krb5_timeofday(kdc_context, &state->kdc_time))) {
state->status = "TIMEOFDAY";
goto errout;
goto errout;
}
+ au_state->stage = ISSUE_TKT;
+
/*
* Select the keytype for the ticket session key.
*/
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* kdc/do_tgs_req.c - KDC Routines to deal with TGS_REQ's */
/*
- * Copyright 1990,1991,2001,2007,2008,2009 by the Massachusetts Institute of Technology.
- * All Rights Reserved.
+ * Copyright 1990, 1991, 2001, 2007, 2008, 2009, 2013 by the Massachusetts
+ * Institute of Technology. All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
#endif
#include "kdc_util.h"
+#include "kdc_audit.h"
#include "policy.h"
#include "extern.h"
#include "adm_proto.h"
krb5_data scratch;
krb5_pa_data **e_data = NULL;
kdc_realm_t *kdc_active_realm = NULL;
+ krb5_audit_state *au_state = NULL;
memset(&reply, 0, sizeof(reply));
memset(&reply_encpart, 0, sizeof(reply_encpart));
krb5_free_kdc_req(handle->kdc_err_context, request);
return errcode;
}
+
+ /* Initialize audit state. */
+ errcode = kau_init_kdc_req(kdc_context, request, from, &au_state);
+ if (errcode) {
+ krb5_free_kdc_req(handle->kdc_err_context, request);
+ return errcode;
+ }
+ /* Seed the audit trail with the request ID and basic information. */
+ kau_tgs_req(kdc_context, TRUE, au_state);
+
errcode = kdc_process_tgs_req(kdc_active_realm,
request, from, pkt, &header_ticket,
&krbtgt, &tgskey, &subkey, &pa_tgs_req);
status="UNEXPECTED NULL in header_ticket";
goto cleanup;
}
+ errcode = kau_make_tkt_id(kdc_context, header_ticket,
+ &au_state->tkt_in_id);
+ if (errcode) {
+ status = "GENERATE_TICKET_ID";
+ goto cleanup;
+ }
+
scratch.length = pa_tgs_req->length;
scratch.data = (char *) pa_tgs_req->contents;
errcode = kdc_find_fast(&request, &scratch, subkey,
goto cleanup;
}
+ /* Ignore (for now) the request modification due to FAST processing. */
+ au_state->request = request;
+
/*
* Pointer to the encrypted part of the header ticket, which may be
* replaced to point to the encrypted part of the evidence ticket
* decrypted with the session key.
*/
+ au_state->stage = SRVC_PRINC;
+
/* XXX make sure server here has the proper realm...taken from AP_REQ
header? */
is_referral = is_cross_tgs_principal(server->princ) &&
!krb5_principal_compare(kdc_context, request->server, server->princ);
+ au_state->stage = VALIDATE_POL;
+
if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) {
status = "TIME_OF_DAY";
goto cleanup;
kdc_time, &status, &e_data))) {
if (!status)
status = "UNKNOWN_REASON";
+ if (retval == KDC_ERR_POLICY || retval == KDC_ERR_BADOPTION)
+ au_state->violation = PROT_CONSTRAINT;
errcode = retval + ERROR_TABLE_BASE_krb5;
goto cleanup;
}
&s4u_x509_user,
&client,
&status);
+ if (s4u_x509_user != NULL || errcode != 0) {
+ if (s4u_x509_user != NULL)
+ au_state->s4u2self_user = s4u_x509_user->user_id.user;
+ if (errcode == KDC_ERR_POLICY || errcode == KDC_ERR_BADOPTION)
+ au_state->violation = PROT_CONSTRAINT;
+ au_state->status = status;
+ kau_s4u2self(kdc_context, errcode ? FALSE : TRUE, au_state);
+ au_state->s4u2self_user = NULL;
+ }
+
if (errcode)
goto cleanup;
if (s4u_x509_user != NULL) {
}
}
+ /* Deal with user-to-user and constrained delegation */
errcode = decrypt_2ndtkt(kdc_active_realm, request, c_flags,
&stkt_server, &status);
if (errcode)
header_ticket->enc_part2->client,
request->server,
&status);
+ if (errcode == KDC_ERR_POLICY || errcode == KDC_ERR_BADOPTION)
+ au_state->violation = PROT_CONSTRAINT;
+ else if (errcode)
+ au_state->violation = LOCAL_POLICY;
+ au_state->status = status;
+ retval = kau_make_tkt_id(kdc_context, request->second_ticket[st_idx],
+ &au_state->evid_tkt_id);
+ if (retval) {
+ status = "GENERATE_TICKET_ID";
+ errcode = retval;
+ goto cleanup;
+ }
+ kau_s4u2proxy(kdc_context, errcode ? FALSE : TRUE, au_state);
if (errcode)
goto cleanup;
} else
assert(stkt_server == NULL);
+ au_state->stage = ISSUE_TKT;
+
errcode = gen_session_key(kdc_active_realm, request, server, &session_key,
&status);
if (errcode)
!isflagset(enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) {
errcode = KRB5KDC_ERR_POLICY;
status = "BAD_TRANSIT";
+ au_state->violation = LOCAL_POLICY;
goto cleanup;
}
altcprinc = client2;
errcode = KRB5KDC_ERR_SERVER_NOMATCH;
status = "2ND_TKT_MISMATCH";
+ au_state->status = status;
+ kau_u2u(kdc_context, FALSE, au_state);
goto cleanup;
}
ticket_kvno = 0;
ticket_reply.enc_part.enctype = t2enc->session->enctype;
+ kau_u2u(kdc_context, TRUE, au_state);
st_idx++;
} else {
ticket_kvno = server_key->key_data_kvno;
}
ticket_reply.enc_part.kvno = ticket_kvno;
/* Start assembling the response */
+ au_state->stage = ENCR_REP;
reply.msg_type = KRB5_TGS_REP;
if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
krb5int_find_pa_data(kdc_context, request->padata,
&reply_encpart);
if (errcode) {
status = "KDC_RETURN_S4U2SELF_PADATA";
- goto cleanup;
+ au_state->status = status;
}
+ kau_s4u2self(kdc_context, errcode ? FALSE : TRUE, au_state);
+ if (errcode)
+ goto cleanup;
}
reply.client = enc_tkt_reply.client;
goto cleanup;
}
+ errcode = kau_make_tkt_id(kdc_context, &ticket_reply, &au_state->tkt_out_id);
+ if (errcode) {
+ status = "GENERATE_TICKET_ID";
+ goto cleanup;
+ }
+
if (kdc_fast_hide_client(state))
reply.client = (krb5_principal)krb5_anonymous_principal();
errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart,
krb5_free_keyblock(kdc_context, reply_key);
if (errcode)
emsg = krb5_get_error_message (kdc_context, errcode);
+
+ au_state->status = status;
+ if (!errcode)
+ au_state->reply = &reply;
+ kau_tgs_req(kdc_context, errcode ? FALSE : TRUE, au_state);
+ kau_free_kdc_req(au_state);
+
log_tgs_req(kdc_context, from, request, &reply, cprinc,
sprinc, altcprinc, authtime,
c_flags, status, errcode, emsg);
--- /dev/null
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kdc_audit.c - Interface for KDC audit plugins. */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "k5-int.h"
+#include "kdc_util.h"
+#include "kdc_audit.h"
+/* for krb5_klog_syslog */
+#include <syslog.h>
+#include "adm_proto.h"
+
+struct audit_module_handle_st {
+ struct krb5_audit_vtable_st vt;
+ krb5_audit_moddata auctx;
+};
+typedef struct audit_module_handle_st *audit_module_handle;
+
+static audit_module_handle *handles = NULL;
+
+static void
+free_handles(audit_module_handle *list)
+{
+ audit_module_handle *hp, hdl;
+
+ if (list == NULL)
+ return;
+
+ for (hp = list; *hp != NULL; hp++) {
+ hdl = *hp;
+ if (hdl->vt.close != NULL)
+ hdl->vt.close(hdl->auctx);
+ free(hdl);
+ }
+ free(list);
+}
+
+/*
+ * Load all available audit plugin modules and prepare for logging. The list of
+ * modules is stored as an array in handles. Use unload_audit_modules() to free
+ * resources allocated by this function.
+ */
+krb5_error_code
+load_audit_modules(krb5_context context)
+{
+ krb5_error_code ret = 0;
+ krb5_plugin_initvt_fn *modules = NULL, *mod;
+ struct krb5_audit_vtable_st vtable;
+ audit_module_handle *list = NULL, hdl = NULL;
+ krb5_audit_moddata auctx;
+ int count = 0;
+
+ if (context == NULL || handles != NULL)
+ return EINVAL;
+
+ /* Get audit plugin vtable. */
+ ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_AUDIT, &modules);
+ if (ret)
+ return ret;
+
+ /* Allocate handle, initialize vtable. */
+ for (count = 0; modules[count] != NULL; count++);
+ list = k5calloc(count + 1, sizeof(*list), &ret);
+ if (list == NULL)
+ goto cleanup;
+ count = 0;
+ for (mod = modules; *mod != NULL; mod++) {
+ hdl = k5alloc(sizeof(*hdl), &ret);
+ if (hdl == NULL)
+ goto cleanup;
+ ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&hdl->vt);
+ if (ret) {
+ free(hdl);
+ hdl = NULL;
+ continue;
+ }
+
+ vtable = hdl->vt;
+ if (vtable.open != NULL) {
+ ret = vtable.open(&auctx);
+ if (ret) {
+ krb5_klog_syslog(LOG_ERR,
+ _("audit plugin %s failed to open. error=%i"),
+ vtable.name, ret);
+ goto cleanup;
+ }
+ hdl->auctx = auctx;
+ }
+ list[count++] = hdl;
+ list[count] = NULL;
+ hdl = NULL;
+ }
+ list[count] = NULL;
+ handles = list;
+ list = NULL;
+ ret = 0;
+
+cleanup:
+ free(hdl);
+ k5_plugin_free_modules(context, modules);
+ free_handles(list);
+ return ret;
+}
+
+/* Free resources allocated by load_audit_modules() function. */
+void
+unload_audit_modules(krb5_context context)
+{
+ free_handles(handles);
+}
+
+/*
+ * Write the output ticket ID into newly-allocated buffer.
+ * Returns 0 on success.
+ */
+krb5_error_code
+kau_make_tkt_id(krb5_context context,
+ const krb5_ticket *ticket, char **out)
+{
+ krb5_error_code ret = 0;
+ char *hash = NULL, *ptr;
+ krb5_checksum cksum;
+ unsigned int i;
+
+ *out = NULL;
+
+ if (ticket == NULL)
+ return EINVAL;
+
+ ret = krb5_c_make_checksum(context, CKSUMTYPE_RSA_MD5, NULL, 0,
+ &ticket->enc_part.ciphertext, &cksum);
+ if (ret)
+ return ret;
+
+ hash = k5alloc(cksum.length * 2 + 1, &ret);
+ if (hash != NULL) {
+ for (i = 0, ptr = hash; i < cksum.length; i++, ptr += 2)
+ snprintf(ptr, 3, "%02X", cksum.contents[i]);
+ *ptr = '\0';
+ *out = hash;
+ }
+ krb5_free_checksum_contents(context, &cksum);
+
+ return 0;
+}
+
+/*
+ * Create and initialize krb5_audit_state structure.
+ * Returns 0 on success.
+ */
+krb5_error_code
+kau_init_kdc_req(krb5_context context,
+ krb5_kdc_req *request, const krb5_fulladdr *from,
+ krb5_audit_state **state_out)
+{
+ krb5_error_code ret = 0;
+ krb5_audit_state *state = NULL;
+
+ state = k5calloc(1, sizeof(*state), &ret);
+ if (state == NULL)
+ return ret;
+
+ state->request = request;
+ state->cl_addr = from->address;
+ state->cl_port = from->port;
+ state->stage = AUTHN_REQ_CL;
+ ret = krb5int_random_string(context, state->req_id,
+ sizeof(state->req_id));
+ if (ret) {
+ free(state);
+ return ret;
+ }
+ *state_out = state;
+
+ return 0;
+}
+
+/* Free resources allocated by kau_init_kdc_req() and kau_make_tkt_id()
+ * routines. */
+void
+kau_free_kdc_req(krb5_audit_state *state)
+{
+ free(state->tkt_in_id);
+ free(state->tkt_out_id);
+ free(state->evid_tkt_id);
+ free(state);
+}
+
+/* Call the KDC start/stop audit plugin entry points. */
+
+void
+kau_kdc_stop(krb5_context context, const krb5_boolean ev_success)
+{
+ audit_module_handle *hp, hdl;
+
+ if (handles == NULL)
+ return;
+
+ for (hp = handles; *hp != NULL; hp++) {
+ hdl = *hp;
+ if (hdl->vt.kdc_stop != NULL)
+ hdl->vt.kdc_stop(hdl->auctx, ev_success);
+ }
+}
+
+void
+kau_kdc_start(krb5_context context, const krb5_boolean ev_success)
+{
+ audit_module_handle *hp, hdl;
+
+ if (handles == NULL)
+ return;
+
+ for (hp = handles; *hp != NULL; hp++) {
+ hdl = *hp;
+ if (hdl->vt.kdc_start != NULL)
+ hdl->vt.kdc_start(hdl->auctx, ev_success);
+ }
+}
+
+/* Call the AS-REQ audit plugin entry point. */
+void
+kau_as_req(krb5_context context, const krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ audit_module_handle *hp, hdl;
+
+ if (handles == NULL)
+ return;
+
+ for (hp = handles; *hp != NULL; hp++) {
+ hdl = *hp;
+ if (hdl->vt.as_req != NULL)
+ hdl->vt.as_req(hdl->auctx, ev_success, state);
+ }
+}
+
+/* Call the TGS-REQ audit plugin entry point. */
+void
+kau_tgs_req(krb5_context context, const krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ audit_module_handle *hp, hdl;
+
+ if (handles == NULL)
+ return;
+
+ for (hp = handles; *hp != NULL; hp++) {
+ hdl = *hp;
+ if (hdl->vt.tgs_req != NULL)
+ hdl->vt.tgs_req(hdl->auctx, ev_success, state);
+ }
+}
+
+/* Call the S4U2Self audit plugin entry point. */
+void
+kau_s4u2self(krb5_context context, const krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ audit_module_handle *hp, hdl;
+
+ if (handles == NULL)
+ return;
+
+ for (hp = handles; *hp != NULL; hp++) {
+ hdl = *hp;
+ if (hdl->vt.tgs_s4u2self != NULL)
+ hdl->vt.tgs_s4u2self(hdl->auctx, ev_success, state);
+ }
+}
+
+/* Call the S4U2Proxy audit plugin entry point. */
+void
+kau_s4u2proxy(krb5_context context,const krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ audit_module_handle *hp, hdl;
+
+ if (handles == NULL)
+ return;
+
+ for (hp = handles; *hp != NULL; hp++) {
+ hdl = *hp;
+ if (hdl->vt.tgs_s4u2proxy != NULL)
+ hdl->vt.tgs_s4u2proxy(hdl->auctx, ev_success, state);
+ }
+}
+
+/* Call the U2U audit plugin entry point. */
+void
+kau_u2u(krb5_context context, const krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ audit_module_handle *hp, hdl;
+
+ if (handles == NULL)
+ return;
+
+ for (hp = handles; *hp != NULL; hp++) {
+ hdl = *hp;
+ if (hdl->vt.tgs_u2u != NULL)
+ hdl->vt.tgs_u2u(hdl->auctx, ev_success, state);
+ }
+}
--- /dev/null
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/kdc_audit.h - KDC-facing API for audit */
+/*
+ * Copyright 2013 by the Massachusetts Institute of Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef KRB5_KDC_AUDIT__
+#define KRB5_KDC_AUDIT__
+
+#include <krb5/krb5.h>
+#include <net-server.h>
+#include <krb5/audit_plugin.h>
+
+krb5_error_code load_audit_modules(krb5_context context);
+void unload_audit_modules(krb5_context context);
+
+/* Utilities */
+
+krb5_error_code
+kau_make_tkt_id(krb5_context context,
+ const krb5_ticket *ticket, char **out);
+
+krb5_error_code
+kau_init_kdc_req(krb5_context context, krb5_kdc_req *request,
+ const krb5_fulladdr *from, krb5_audit_state **au_state);
+
+void kau_free_kdc_req(krb5_audit_state *state);
+
+/* KDC-facing audit API */
+
+void
+kau_kdc_start(krb5_context context, const krb5_boolean ev_success);
+
+void
+kau_kdc_stop(krb5_context context, const krb5_boolean ev_success);
+
+void
+kau_as_req(krb5_context context, const krb5_boolean ev_success,
+ krb5_audit_state *state);
+
+void
+kau_tgs_req(krb5_context context, const krb5_boolean ev_success,
+ krb5_audit_state *state);
+
+void
+kau_s4u2self(krb5_context context, const krb5_boolean ev_success,
+ krb5_audit_state *state);
+
+void
+kau_s4u2proxy(krb5_context context, const krb5_boolean ev_success,
+ krb5_audit_state *state);
+
+void
+kau_u2u(krb5_context context, const krb5_boolean ev_success,
+ krb5_audit_state *state);
+
+#endif /* KRB5_KDC_AUDIT__ */
#include <kadm5/admin.h>
#include "adm_proto.h"
#include "kdc_util.h"
+#include "kdc_audit.h"
#include "extern.h"
#include "kdc5_err.h"
#include "kdb_kt.h"
/* We get here only in a worker child process; re-initialize realms. */
initialize_realms(kcontext, argc, argv);
}
+
+ /* Initialize audit system and audit KDC startup. */
+ retval = load_audit_modules(kcontext);
+ if (retval) {
+ kdc_err(kcontext, retval, _("while loading audit plugin module(s)"));
+ finish_realms();
+ return 1;
+ }
krb5_klog_syslog(LOG_INFO, _("commencing operation"));
if (nofork)
fprintf(stderr, _("%s: starting...\n"), kdc_progname);
+ kau_kdc_start(kcontext, TRUE);
verto_run(ctx);
loop_free(ctx);
+ kau_kdc_stop(kcontext, TRUE);
krb5_klog_syslog(LOG_INFO, _("shutting down"));
unload_preauth_plugins(kcontext);
unload_authdata_plugins(kcontext);
+ unload_audit_modules(kcontext);
krb5_klog_close(kcontext);
finish_realms();
if (shandle.kdc_realmlist)
"kdcpreauth",
"ccselect",
"localauth",
- "hostrealm"
+ "hostrealm",
+ "audit"
};
/* Return the context's interface structure for id, or NULL if invalid. */
--- /dev/null
+mydir=plugins$(S)audit
+BUILDTOP=$(REL)..$(S)..
+
+STLIBOBJS=kdc_j_encode.o
+LIBOBJS=$(OUTPRE)kdc_j_encode.$(OBJEXT)
+SRCS=kdc_j_encode.c
+
+AUJENC_HDR=$(BUILDTOP)$(S)include$(S)kdc_j_encode.h
+
+all-unix:: all-libobjs includes
+
+clean-unix:: clean-libobjs
+ $(RM) $(AUJENC_HDR)
+
+includes:: $(AUJENC_HDR)
+depend:: $(AUJENC_HDR)
+
+$(AUJENC_HDR): $(srcdir)/kdc_j_encode.h
+ $(RM) $@
+ $(CP) $(srcdir)/kdc_j_encode.h $@
+
+@libobj_frag@
--- /dev/null
+#
+# Generated makefile dependencies follow.
+#
+kdc_j_encode.so kdc_j_encode.po $(OUTPRE)kdc_j_encode.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/audit_plugin.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ j_dict.h kdc_j_encode.c kdc_j_encode.h
--- /dev/null
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* j_dict.h - Dictionary file for json implementation of audit system */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef KRB5_J_DICT_H_INCLUDED
+#define KRB5_J_DICT_H_INCLUDED
+
+/* Dictionary for KDC events */
+#define AU_STAGE "stage"
+#define AU_EVENT_NAME "event_name"
+#define AU_EVENT_STATUS "event_success"
+#define AU_TKT_IN_ID "tkt_in_id"
+#define AU_TKT_OUT_ID "tkt_out_id"
+#define AU_REQ_ID "req_id"
+#define AU_KDC_STATUS "kdc_status"
+#define AU_FROMPORT "fromport"
+#define AU_FROMADDR "fromaddr"
+#define AU_TYPE "type" /* used by fromaddr */
+#define AU_IPV4 "ipv4" /* used by fromaddr */
+#define AU_SESS_ETYPE "sess_etype"
+#define AU_SRV_ETYPE "srv_etype"
+#define AU_REP_ETYPE "rep_etype"
+#define AU_REALM "realm"
+#define AU_LENGTH "length"
+#define AU_COMPONENTS "components"
+#define AU_TKT_RENEWED "tkt_renewed"
+#define AU_TKT_VALIDATED "tkt_validated"
+/* referrals */
+#define AU_CREF_REALM "clreferral_realm"
+/* request */
+#define AU_REQ_KDC_OPTIONS "req.kdc_options"
+#define AU_REQ_SERVER "req.server"
+#define AU_REQ_CLIENT "req.client"
+#define AU_REQ_AVAIL_ETYPES "req.avail_etypes"
+#define AU_EVIDENCE_TKT "evidence_tkt"
+#define AU_REQ_ADDRESSES "req.addresses"
+#define AU_REQ_TKT_START "req.tkt_start"
+#define AU_REQ_TKT_END "req.tkt_end"
+#define AU_REQ_TKT_RENEW_TILL "req.tkt_renew_till"
+#define AU_REQ_PA_TYPE "req.pa_type"
+/* reply */
+#define AU_REP_TICKET "rep.ticket"
+#define AU_REP_PA_TYPE "rep.pa_type"
+/* ticket */
+#define AU_SNAME "sname"
+#define AU_CNAME "cname"
+#define AU_FLAGS "flags"
+#define AU_START "start"
+#define AU_END "end"
+#define AU_RENEW_TILL "renew_till"
+#define AU_AUTHTIME "authtime"
+#define AU_TR_CONTENTS "tr_contents"
+#define AU_CADDRS "caddrs"
+/* S4U and U2U */
+#define AU_VIOLATION "violation" /* policy or protocol restrictions */
+#define AU_REQ_S4U2S_USER "s4u2self_user"
+#define AU_REQ_S4U2P_USER "s4u2proxy_user"
+#define AU_REQ_U2U_USER "u2u_user"
+#define AU_EVIDENCE_TKT_ID "evidence_tkt_id" /* 2nd ticket in s4u2proxy req */
+#endif /* KRB5_J_DICT_H_INCLUDED */
--- /dev/null
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* plugins/audit/kdc_j_encode.c - Utilities to json encode KDC audit stuff */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <syslog.h>
+#include <k5-int.h>
+#include <k5-json.h>
+#include "kdc_j_encode.h"
+#include "j_dict.h"
+#include <krb5/audit_plugin.h>
+
+static krb5_error_code
+string_to_value(const char *in, k5_json_object obj, const char *key);
+static krb5_error_code
+princ_to_value(krb5_principal princ, k5_json_object obj, const char *key);
+static krb5_error_code
+data_to_value(krb5_data *data, k5_json_object obj, const char *key);
+static krb5_error_code
+int32_to_value(krb5_int32 int32, k5_json_object obj, const char *key);
+static krb5_error_code
+bool_to_value(krb5_boolean b, k5_json_object obj, const char *key);
+static krb5_error_code
+addr_to_obj(krb5_address *a, k5_json_object obj);
+static krb5_error_code
+eventinfo_to_value(k5_json_object obj, const char *name,
+ const int stage, const krb5_boolean ev_success);
+static krb5_error_code
+addr_to_value(const krb5_address *address, k5_json_object obj,
+ const char *key);
+static krb5_error_code
+req_to_value(krb5_kdc_req *req, const krb5_boolean ev_success,
+ k5_json_object obj);
+static krb5_error_code
+rep_to_value(krb5_kdc_rep *rep, const krb5_boolean ev_success,
+ k5_json_object obj);
+static krb5_error_code
+tkt_to_value(krb5_ticket *tkt, k5_json_object obj, const char *key);
+static char *map_patype(krb5_preauthtype pa_type);
+
+#define NULL_STATE "state is NULL"
+#define T_RENEWED 1
+#define T_NOT_RENEWED 2
+#define T_VALIDATED 1
+#define T_NOT_VALIDATED 2
+
+/* KDC server STOP. Returns 0 on success. */
+krb5_error_code
+kau_j_kdc_stop(const krb5_boolean ev_success, char **jout)
+{
+ krb5_error_code ret = 0;
+ k5_json_object obj = NULL;
+
+ *jout = NULL;
+
+ /* Main object. */
+ if (k5_json_object_create(&obj))
+ return ENOMEM;
+
+ /* Audit event_ID and ev_success. */
+ ret = string_to_value("KDC_STOP", obj, AU_EVENT_NAME);
+ if (!ret)
+ ret = bool_to_value(ev_success, obj, AU_EVENT_STATUS);
+ if (!ret)
+ ret = k5_json_encode(obj, jout);
+ k5_json_release(obj);
+
+ return ret;
+}
+
+/* KDC server START. Returns 0 on success. */
+krb5_error_code
+kau_j_kdc_start(const krb5_boolean ev_success, char **jout)
+{
+ krb5_error_code ret = 0;
+ k5_json_object obj = NULL;
+
+ *jout = NULL;
+
+ /* Main object. */
+ if (k5_json_object_create(&obj))
+ return ENOMEM;
+
+ /* Audit event_ID and ev_success. */
+ ret = string_to_value("KDC_START", obj, AU_EVENT_NAME);
+ if (!ret)
+ ret = bool_to_value(ev_success, obj, AU_EVENT_STATUS);
+ if (!ret)
+ ret = k5_json_encode(obj, jout);
+ k5_json_release(obj);
+
+ return ret;
+}
+
+/* AS-REQ. Returns 0 on success. */
+krb5_error_code
+kau_j_as_req(const krb5_boolean ev_success, krb5_audit_state *state,
+ char **jout)
+{
+ krb5_error_code ret = 0;
+ k5_json_object obj = NULL;
+
+ *jout = NULL;
+
+ if (!state) {
+ *jout = NULL_STATE;
+ return 0;
+ }
+
+ /* Main object. */
+ if (k5_json_object_create(&obj))
+ return ENOMEM;
+ /* Audit event_ID and ev_success. */
+ ret = eventinfo_to_value(obj, "AS_REQ", state->stage, ev_success);
+ if (ret)
+ goto error;
+ /* TGT ticket ID */
+ ret = string_to_value(state->tkt_out_id, obj, AU_TKT_OUT_ID);
+ if (ret)
+ goto error;
+ /* Request ID. */
+ ret = string_to_value(state->req_id, obj, AU_REQ_ID);
+ if (ret)
+ goto error;
+ /* Client's port and address. */
+ ret = int32_to_value(state->cl_port, obj, AU_FROMPORT);
+ if (ret)
+ goto error;
+ ret = addr_to_value(state->cl_addr, obj, AU_FROMADDR);
+ if (ret)
+ goto error;
+ /* KDC status msg */
+ ret = string_to_value(state->status, obj, AU_KDC_STATUS);
+ if (ret)
+ goto error;
+ /* non-local client's referral realm. */
+ ret = data_to_value(state->cl_realm, obj, AU_CREF_REALM);
+ if (ret)
+ goto error;
+ /* Request. */
+ ret = req_to_value(state->request, ev_success, obj);
+ if (ret == ENOMEM)
+ goto error;
+ /* Reply/ticket info. */
+ ret = rep_to_value(state->reply, ev_success, obj);
+ if (ret == ENOMEM)
+ goto error;
+ ret = k5_json_encode(obj, jout);
+
+error:
+ k5_json_release(obj);
+ return ret;
+}
+
+/* TGS-REQ. Returns 0 on success. */
+krb5_error_code
+kau_j_tgs_req(const krb5_boolean ev_success, krb5_audit_state *state,
+ char **jout)
+{
+ krb5_error_code ret = 0;
+ k5_json_object obj = NULL;
+ krb5_kdc_req *req = state->request;
+ int tkt_validated = 0, tkt_renewed = 0;
+
+ *jout = NULL;
+
+ if (!state) {
+ *jout = NULL_STATE;
+ return 0;
+ }
+
+ /* Main object. */
+ if (k5_json_object_create(&obj))
+ return ENOMEM;
+
+ /* Audit Event ID and ev_success. */
+ ret = eventinfo_to_value(obj, "TGS_REQ", state->stage, ev_success);
+ if (ret)
+ goto error;
+ /* Primary and derived ticket IDs. */
+ ret = string_to_value(state->tkt_in_id, obj, AU_TKT_IN_ID);
+ if (ret)
+ goto error;
+ ret = string_to_value(state->tkt_out_id, obj, AU_TKT_OUT_ID);
+ if (ret)
+ goto error;
+ /* Request ID */
+ ret = string_to_value(state->req_id, obj, AU_REQ_ID);
+ if (ret)
+ goto error;
+ /* client’s address and port. */
+ ret = int32_to_value(state->cl_port, obj, AU_FROMPORT);
+ if (ret)
+ goto error;
+ ret = addr_to_value(state->cl_addr, obj, AU_FROMADDR);
+ if (ret)
+ goto error;
+ /* Ticket was renewed, validated. */
+ if ((ev_success == TRUE) && (req != NULL)) {
+ tkt_renewed = (req->kdc_options & KDC_OPT_RENEW) ?
+ T_RENEWED : T_NOT_RENEWED;
+ tkt_validated = (req->kdc_options & KDC_OPT_VALIDATE) ?
+ T_VALIDATED : T_NOT_VALIDATED;
+ }
+ ret = int32_to_value(tkt_renewed, obj, AU_TKT_RENEWED);
+ if (ret)
+ goto error;
+ ret = int32_to_value(tkt_validated, obj, AU_TKT_VALIDATED);
+ if (ret)
+ goto error;
+ /* KDC status msg, including "ISSUE". */
+ ret = string_to_value(state->status, obj, AU_KDC_STATUS);
+ if (ret)
+ goto error;
+ /* request */
+ ret = req_to_value(req, ev_success, obj);
+ if (ret == ENOMEM)
+ goto error;
+ /* reply/ticket */
+ ret = rep_to_value(state->reply, ev_success, obj);
+ if (ret == ENOMEM)
+ goto error;
+ ret = k5_json_encode(obj, jout);
+
+error:
+ k5_json_release(obj);
+ return ret;
+}
+
+/* S4U2Self protocol extension. Returns 0 on success. */
+krb5_error_code
+kau_j_tgs_s4u2self(const krb5_boolean ev_success, krb5_audit_state *state,
+ char **jout)
+{
+ krb5_error_code ret = 0;
+ k5_json_object obj = NULL;
+
+ *jout = NULL;
+
+ if (!state) {
+ *jout = NULL_STATE;
+ return 0;
+ }
+
+ /* Main object. */
+ if (k5_json_object_create(&obj))
+ return ENOMEM;
+
+ /* Audit Event ID and ev_success. */
+ ret = eventinfo_to_value(obj, "S4U2SELF", state->stage, ev_success);
+ if (ret)
+ goto error;
+ /* Front-end server's TGT ticket ID. */
+ ret = string_to_value(state->tkt_in_id, obj, AU_TKT_IN_ID);
+ if (ret)
+ goto error;
+ /* service "to self" ticket or referral TGT ticket ID. */
+ ret = string_to_value(state->tkt_out_id, obj, AU_TKT_OUT_ID);
+ if (ret)
+ goto error;
+ /* Request ID. */
+ ret = string_to_value(state->req_id, obj, AU_REQ_ID);
+ if (ret)
+ goto error;
+ if (ev_success == FALSE) {
+ /* KDC status msg. */
+ ret = string_to_value(state->status, obj, AU_KDC_STATUS);
+ if (ret)
+ goto error;
+ /* Local policy or S4U protocol constraints. */
+ ret = int32_to_value(state->violation, obj, AU_VIOLATION);
+ if (ret)
+ goto error;
+ }
+ /* Impersonated user. */
+ ret = princ_to_value(state->s4u2self_user, obj, AU_REQ_S4U2S_USER);
+ if (ret)
+ goto error;
+
+ ret = k5_json_encode(obj, jout);
+
+error:
+ k5_json_release(obj);
+ return ret;
+}
+
+/* S4U2Proxy protocol extension. Returns 0 on success. */
+krb5_error_code
+kau_j_tgs_s4u2proxy(const krb5_boolean ev_success, krb5_audit_state *state,
+ char **jout)
+{
+ krb5_error_code ret = 0;
+ k5_json_object obj = NULL;
+ krb5_kdc_req *req = state->request;
+
+ *jout = NULL;
+
+ if (!state) {
+ *jout = NULL_STATE;
+ return 0;
+ }
+
+ /* Main object. */
+ if (k5_json_object_create(&obj))
+ return ENOMEM;
+
+ /* Audit Event ID and ev_success. */
+ ret = eventinfo_to_value(obj, "S4U2PROXY", state->stage, ev_success);
+ if (ret)
+ goto error;
+ /* Front-end server's TGT ticket ID. */
+ ret = string_to_value(state->tkt_in_id, obj, AU_TKT_IN_ID);
+ if (ret)
+ goto error;
+ /* Resource service or referral TGT ticket ID. */
+ ret = string_to_value(state->tkt_out_id, obj, AU_TKT_OUT_ID);
+ if (ret)
+ goto error;
+ /* User's evidence ticket ID. */
+ ret = string_to_value(state->evid_tkt_id, obj, AU_EVIDENCE_TKT_ID);
+ if (ret)
+ goto error;
+ /* Request ID. */
+ ret = string_to_value(state->req_id, obj, AU_REQ_ID);
+ if (ret)
+ goto error;
+
+ if (ev_success == FALSE) {
+ /* KDC status msg. */
+ ret = string_to_value(state->status, obj, AU_KDC_STATUS);
+ if (ret)
+ goto error;
+ /* Local policy or S4U protocol constraints. */
+ ret = int32_to_value(state->violation, obj, AU_VIOLATION);
+ if (ret)
+ goto error;
+ }
+ /* Delegated user. */
+ if (req != NULL) {
+ ret = princ_to_value(req->second_ticket[0]->enc_part2->client,
+ obj, AU_REQ_S4U2P_USER);
+ if (ret)
+ goto error;
+ }
+ ret = k5_json_encode(obj, jout);
+
+error:
+ k5_json_release(obj);
+ return ret;
+}
+
+/* U2U. Returns 0 on success. */
+krb5_error_code
+kau_j_tgs_u2u(const krb5_boolean ev_success, krb5_audit_state *state,
+ char **jout)
+{
+ krb5_error_code ret = 0;
+ k5_json_object obj = NULL;
+ krb5_kdc_req *req = state->request;
+
+ if (!state) {
+ *jout = NULL_STATE;
+ return 0;
+ }
+
+ *jout = NULL;
+
+ /* Main object. */
+ if (k5_json_object_create(&obj))
+ return ENOMEM;
+ /* Audit Event ID and ev_success. */
+ ret = eventinfo_to_value(obj, "U2U", state->stage, ev_success);
+ if (ret)
+ goto error;
+ /* Front-end server's TGT ticket ID. */
+ ret = string_to_value(state->tkt_in_id, obj, AU_TKT_IN_ID);
+ if (ret)
+ goto error;
+ /* Service ticket ID. */
+ ret = string_to_value(state->tkt_out_id, obj, AU_TKT_OUT_ID);
+ if (ret)
+ goto error;
+ /* Request ID. */
+ ret = string_to_value(state->req_id, obj, AU_REQ_ID);
+ if (ret)
+ goto error;
+
+ if (ev_success == FALSE) {
+ /* KDC status msg. */
+ ret = string_to_value(state->status, obj, AU_KDC_STATUS);
+ if (ret)
+ goto error;
+ }
+ /* Client in the second ticket. */
+ if (req != NULL) {
+ ret = princ_to_value(req->second_ticket[0]->enc_part2->client,
+ obj, AU_REQ_U2U_USER);
+ if (ret)
+ goto error;
+ }
+ /* Enctype of a session key of the second ticket. */
+ ret = int32_to_value(req->second_ticket[0]->enc_part2->session->enctype,
+ obj, AU_SRV_ETYPE);
+ if (ret)
+ goto error;
+
+ ret = k5_json_encode(obj, jout);
+
+error:
+ k5_json_release(obj);
+ return ret;
+}
+
+/* Low level utilities */
+
+/* Converts string into a property of a JSON object. Returns 0 on success.*/
+static krb5_error_code
+string_to_value(const char *in, k5_json_object obj, const char *key)
+{
+ krb5_error_code ret = 0;
+ k5_json_string str = NULL;
+
+ if (in == NULL)
+ return 0;
+
+ ret = k5_json_string_create(in, &str);
+ if (ret)
+ return ret;
+ ret = k5_json_object_set(obj, key, str);
+ k5_json_release(str);
+
+ return ret;
+}
+
+/*
+ * Converts a krb5_data struct into a property of a JSON object.
+ * (Borrowed from preauth_otp.c)
+ * Returns 0 on success.
+ */
+static krb5_error_code
+data_to_value(krb5_data *data, k5_json_object obj, const char *key)
+{
+ krb5_error_code ret = 0;
+ k5_json_string str = NULL;
+
+ if (data == NULL || data->data == NULL || data->length < 1)
+ return 0;
+
+ ret = k5_json_string_create_len(data->data, data->length, &str);
+ if (ret)
+ return ret;
+ ret = k5_json_object_set(obj, key, str);
+ k5_json_release(str);
+
+ return ret;
+}
+
+/*
+ * Converts krb5_int32 into a property of a JSON object.
+ * Returns 0 on success.
+ */
+static krb5_error_code
+int32_to_value(krb5_int32 int32, k5_json_object obj, const char *key)
+{
+ krb5_error_code ret = 0;
+ k5_json_number num = NULL;
+
+ ret = k5_json_number_create(int32, &num);
+ if (ret)
+ return ENOMEM;
+ ret = k5_json_object_set(obj, key, num);
+ k5_json_release(num);
+
+ return ret;
+}
+
+/*
+ * Converts krb5_boolean into a property of a JSON object.
+ * Returns 0 on success.
+ */
+static krb5_error_code
+bool_to_value(krb5_boolean in, k5_json_object obj, const char *key)
+{
+ krb5_error_code ret = 0;
+ k5_json_bool b = 0;
+
+ ret = k5_json_bool_create(in, &b);
+ if (ret)
+ return ENOMEM;
+
+ ret = k5_json_object_set(obj, key, b);
+ k5_json_release(b);
+
+ return ret;
+}
+
+/* Wrapper-level utilities */
+
+/* Wrapper for stage and event_status tags. Returns 0 on success. */
+static krb5_error_code
+eventinfo_to_value(k5_json_object obj, const char *name,
+ const int stage, const krb5_boolean ev_success)
+{
+ krb5_error_code ret = 0;
+
+ ret = string_to_value(name, obj, AU_EVENT_NAME);
+ if (ret)
+ return ret;
+ ret = int32_to_value(stage, obj, AU_STAGE);
+ if (!ret)
+ ret = bool_to_value(ev_success, obj, AU_EVENT_STATUS);
+
+ return ret;
+}
+
+/*
+ * Converts krb5_principal into a property of a JSON object.
+ * Returns 0 on success.
+ */
+static krb5_error_code
+princ_to_value(krb5_principal princ, k5_json_object obj, const char *key)
+{
+ krb5_error_code ret = 0;
+ k5_json_object tmp = NULL;
+ k5_json_array arr = NULL;
+ k5_json_string str = NULL;
+ int i = 0;
+
+ if (princ == NULL || princ->data == NULL)
+ return 0;
+
+ /* Main object. */
+ if (k5_json_object_create(&tmp))
+ return ENOMEM;
+
+ ret = k5_json_array_create(&arr);
+ if (ret)
+ goto error;
+ for (i = 0; i < princ->length; i++) {
+ ret = k5_json_string_create_len((&princ->data[i])->data,
+ (&princ->data[i])->length, &str);
+ if (ret)
+ goto error;
+ ret = k5_json_array_add(arr, str);
+ k5_json_release(str);
+ if (ret)
+ goto error;
+ }
+ ret = k5_json_object_set(tmp, AU_COMPONENTS, arr);
+ if (ret)
+ goto error;
+ ret = data_to_value(&princ->realm, tmp, AU_REALM);
+ if (ret)
+ goto error;
+ ret = int32_to_value(princ->length, tmp, AU_LENGTH);
+ if (ret)
+ goto error;
+ ret = int32_to_value(princ->type, tmp, AU_TYPE);
+ if (ret)
+ goto error;
+
+ ret = k5_json_object_set(obj, key, tmp);
+
+error:
+ k5_json_release(tmp);
+ k5_json_release(arr);
+ return ret;
+}
+
+/*
+ * Helper for JSON encoding of krb5_address.
+ * Returns 0 on success.
+ */
+static krb5_error_code
+addr_to_obj(krb5_address *a, k5_json_object obj)
+{
+ krb5_error_code ret = 0;
+ k5_json_number num = NULL;
+ k5_json_array arr = NULL;
+ int i;
+
+ if (a == NULL || a->contents == NULL || a->length <= 0)
+ return 0;
+
+ ret = int32_to_value(a->addrtype, obj, AU_TYPE);
+ if (ret)
+ goto error;
+ ret = int32_to_value(a->length, obj, AU_LENGTH);
+ if (ret)
+ goto error;
+
+ if (a->addrtype == ADDRTYPE_INET) {
+ ret = k5_json_array_create(&arr);
+ if (ret)
+ goto error;
+ for (i = 0; i < (int)a->length; i++) {
+ ret = k5_json_number_create(a->contents[i], &num);
+ if (ret)
+ goto error;
+ ret = k5_json_array_add(arr, num);
+ k5_json_release(num);
+ if (ret)
+ goto error;
+ }
+ ret = k5_json_object_set(obj, AU_IPV4, arr);
+ if (ret)
+ goto error;
+ }
+
+error:
+ k5_json_release(arr);
+ return ret;
+}
+
+/*
+ * Converts krb5_fulladdr into a property of a JSON object.
+ * Returns 0 on success.
+ */
+static krb5_error_code
+addr_to_value(const krb5_address *address, k5_json_object obj, const char *key)
+{
+ krb5_error_code ret = 0;
+ k5_json_object addr_obj = NULL;
+
+ if (address == NULL)
+ return 0;
+
+ ret = k5_json_object_create(&addr_obj);
+ if (ret)
+ return ret;
+ ret = addr_to_obj((krb5_address *)address, addr_obj);
+ if (!ret)
+ ret = k5_json_object_set(obj, key, addr_obj);
+ k5_json_release(addr_obj);
+
+ return ret;
+}
+
+/*
+ * Helper for JSON encoding of krb5_kdc_req.
+ * Returns 0 on success.
+ */
+static krb5_error_code
+req_to_value(krb5_kdc_req *req, const krb5_boolean ev_success,
+ k5_json_object obj)
+{
+ krb5_error_code ret = 0;
+ k5_json_number num = NULL;
+ k5_json_string str = NULL;
+ k5_json_object tmpa = NULL;
+ k5_json_array arr = NULL, arra = NULL, arrpa = NULL;
+ krb5_pa_data **padata;
+ int i = 0;
+
+ if (req == NULL)
+ return 0;
+
+ ret = princ_to_value(req->client, obj, AU_REQ_CLIENT);
+ if (ret)
+ goto error;
+ ret = princ_to_value(req->server, obj, AU_REQ_SERVER);
+ if (ret)
+ goto error;
+
+ ret = int32_to_value(req->kdc_options, obj, AU_REQ_KDC_OPTIONS);
+ if (ret)
+ goto error;
+ ret = int32_to_value(req->from, obj, AU_REQ_TKT_START);
+ if (ret)
+ goto error;
+ ret = int32_to_value(req->till, obj, AU_REQ_TKT_END);
+ if (ret)
+ goto error;
+ ret = int32_to_value(req->rtime, obj, AU_REQ_TKT_RENEW_TILL);
+ if (ret)
+ goto error;
+ /* Available/requested enctypes. */
+ ret = k5_json_array_create(&arr);
+ if (ret)
+ goto error;
+ for (i = 0; (i < req->nktypes); i++) {
+ if (req->ktype[i] > 0) {
+ ret = k5_json_number_create(req->ktype[i], &num);
+ if (ret)
+ goto error;
+ ret = k5_json_array_add(arr, num);
+ k5_json_release(num);
+ if (ret)
+ goto error;
+ }
+ }
+ ret = k5_json_object_set(obj, AU_REQ_AVAIL_ETYPES, arr);
+ if (ret)
+ goto error;
+ /* Pre-auth types. */
+ if (ev_success == TRUE && req->padata) {
+ ret = k5_json_array_create(&arrpa);
+ if (ret)
+ goto error;
+ for (padata = req->padata; *padata; padata++) {
+ if (strlen(map_patype((*padata)->pa_type)) > 1) {
+ ret = k5_json_string_create(map_patype((*padata)->pa_type),
+ &str);
+ if (ret)
+ goto error;
+ ret = k5_json_array_add(arrpa, str);
+ k5_json_release(str);
+ if (ret)
+ goto error;
+ }
+ }
+ ret = k5_json_object_set(obj, AU_REQ_PA_TYPE, arrpa);
+ }
+ /* List of requested addresses. */
+ if (req->addresses) {
+ ret = k5_json_array_create(&arra);
+ if (ret)
+ goto error;
+ for (i = 0; req->addresses[i] != NULL; i++) {
+ ret = k5_json_object_create(&tmpa);
+ if (ret)
+ goto error;
+ ret = addr_to_obj(req->addresses[i], tmpa);
+ if (ret)
+ goto error;
+ ret = k5_json_array_add(arra, tmpa);
+ k5_json_release(tmpa);
+ if (ret)
+ goto error;
+ }
+ ret = k5_json_object_set(obj, AU_REQ_ADDRESSES, arra);
+ if (ret)
+ goto error;
+ }
+error:
+ k5_json_release(arr);
+ k5_json_release(arra);
+ k5_json_release(arrpa);
+ return ret;
+}
+
+/*
+ * Helper for JSON encoding of krb5_kdc_rep.
+ * Returns 0 on success.
+ */
+static krb5_error_code
+rep_to_value(krb5_kdc_rep *rep, const krb5_boolean ev_success,
+ k5_json_object obj)
+{
+ krb5_error_code ret = 0;
+ krb5_pa_data **padata;
+ k5_json_array arrpa = NULL;
+ k5_json_string str = NULL;
+
+ if (rep == NULL)
+ return 0;
+
+ if (ev_success == TRUE) {
+ ret = tkt_to_value(rep->ticket, obj, AU_REP_TICKET);
+ /* Enctype of the reply-encrypting key. */
+ ret = int32_to_value(rep->enc_part.enctype, obj, AU_REP_ETYPE);
+ if (ret)
+ goto error;
+ } else {
+
+ if (rep->padata) {
+ ret = k5_json_array_create(&arrpa);
+ if (ret)
+ goto error;
+ for (padata = rep->padata; *padata; padata++) {
+ if (strlen(map_patype((*padata)->pa_type)) > 1) {
+ ret = k5_json_string_create(map_patype((*padata)->pa_type),
+ &str);
+ if (ret)
+ goto error;
+ ret = k5_json_array_add(arrpa, str);
+ k5_json_release(str);
+ if (ret)
+ goto error;
+ }
+ }
+ }
+ ret = k5_json_object_set(obj, AU_REP_PA_TYPE, arrpa);
+ }
+error:
+ k5_json_release(arrpa);
+ return ret;
+}
+
+/*
+ * Converts krb5_ticket into a property of a JSON object.
+ * Returns 0 on success.
+ */
+static krb5_error_code
+tkt_to_value(krb5_ticket *tkt, k5_json_object obj,
+ const char *key)
+{
+ krb5_error_code ret = 0;
+ k5_json_object tmp = NULL;
+ krb5_enc_tkt_part *part2 = NULL;
+
+ if (tkt == NULL)
+ return 0;
+
+ /* Main object. */
+ if (k5_json_object_create(&tmp))
+ return ENOMEM;
+
+ /*
+ * CNAME - potentially redundant data...
+ * ...but it is part of the ticket. So, record it as such.
+ */
+ ret = princ_to_value(tkt->server, tmp, AU_CNAME);
+ if (ret)
+ goto error;
+ ret = princ_to_value(tkt->server, tmp, AU_SNAME);
+ if (ret)
+ goto error;
+ /* Enctype of a long-term key of service. */
+ if (tkt->enc_part.enctype)
+ ret = int32_to_value(tkt->enc_part.enctype, tmp, AU_SRV_ETYPE);
+ if (ret)
+ goto error;
+ if (tkt->enc_part2)
+ part2 = tkt->enc_part2;
+ if (part2) {
+ ret = princ_to_value(part2->client, tmp, AU_CNAME);
+ if (ret)
+ goto error;
+ ret = int32_to_value(part2->flags, tmp, AU_FLAGS);
+ if (ret)
+ goto error;
+ /* Chosen by KDC session key enctype (short-term key). */
+ ret = int32_to_value(part2->session->enctype, tmp, AU_SESS_ETYPE);
+ if (ret)
+ goto error;
+ if (&part2->times) {
+ ret = int32_to_value(part2->times.starttime, tmp, AU_START);
+ if (ret)
+ goto error;
+ ret = int32_to_value(part2->times.endtime, tmp, AU_END);
+ if (ret)
+ goto error;
+ ret = int32_to_value(part2->times.renew_till, tmp, AU_RENEW_TILL);
+ if (ret)
+ goto error;
+ ret = int32_to_value(part2->times.authtime, tmp, AU_AUTHTIME);
+ if (ret)
+ goto error;
+ }
+ if (&part2->transited && &part2->transited.tr_contents &&
+ part2->transited.tr_contents.length > 0) {
+ ret = data_to_value(&part2->transited.tr_contents,
+ tmp, AU_TR_CONTENTS);
+ if (ret)
+ goto error;
+ }
+ } /* part2 != NULL */
+
+ if (!ret)
+ ret = k5_json_object_set(obj, key, tmp);
+
+error:
+ k5_json_release(tmp);
+ return ret;
+}
+
+/* Map preauth numeric type to the naming string. */
+struct _patype_str {
+ krb5_preauthtype id;
+ char *name;
+};
+struct _patype_str patype_str[] = {
+ {KRB5_PADATA_ENC_TIMESTAMP, "ENC_TIMESTAMP"},
+ {KRB5_PADATA_PW_SALT, "PW_SALT"},
+ {KRB5_PADATA_ENC_UNIX_TIME, "ENC_UNIX_TIME"},
+ {KRB5_PADATA_SAM_CHALLENGE, "SAM_CHALLENGE"},
+ {KRB5_PADATA_SAM_RESPONSE, "SAM_RESPONSE"},
+ {KRB5_PADATA_PK_AS_REQ_OLD, "PK_AS_REQ_OLD"},
+ {KRB5_PADATA_PK_AS_REP_OLD, "PK_AS_REP_OLD"},
+ {KRB5_PADATA_PK_AS_REQ, "PK_AS_REQ"},
+ {KRB5_PADATA_PK_AS_REP, "PK_AS_REP"},
+ {KRB5_PADATA_ETYPE_INFO2, "ETYPE_INFO2"},
+ {KRB5_PADATA_SAM_CHALLENGE_2, "SAM_CHALLENGE_2"},
+ {KRB5_PADATA_SAM_RESPONSE_2, "SAM_RESPONSE_2"},
+ {KRB5_PADATA_PAC_REQUEST, "PAC_REQUEST"},
+ {KRB5_PADATA_FOR_USER, "FOR_USER"},
+ {KRB5_PADATA_S4U_X509_USER, "S4U_X509_USER"},
+ {KRB5_PADATA_ENCRYPTED_CHALLENGE, "ENCRYPTED_CHALLENGE"},
+ {KRB5_PADATA_OTP_CHALLENGE, "OTP_CHALLENGE"},
+ {KRB5_PADATA_OTP_REQUEST, "OTP_REQUEST"},
+ {KRB5_PADATA_OTP_PIN_CHANGE, "OTP_PIN_CHANGE"}
+};
+
+
+static char *
+map_patype(krb5_preauthtype pa_type)
+{
+ int i = 0;
+ int n = sizeof(patype_str)/sizeof(patype_str[0]);
+
+ for (i = 0; i < n; i++) {
+ if (pa_type == patype_str[i].id)
+ return patype_str[i].name;
+ }
+ return "";
+}
--- /dev/null
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* plugins/audit/kdc_j_encode.h - Declarations for KDC audit json encoders */
+/*
+ * Copyright 2013 by the Massachusetts Institute of Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef KRB5_KDC_J_ENCODE_H_INCLUDED
+#define KRB5_KDC_J_ENCODE_H_INCLUDED
+
+#include <krb5/audit_plugin.h>
+
+/* Maximum length of the name of preauth type. */
+#define MAX_PATYPE_NAME_LEN 32
+
+krb5_error_code
+kau_j_kdc_stop(const krb5_boolean ev_success, char **jout);
+
+krb5_error_code
+kau_j_kdc_start(const krb5_boolean ev_success, char **jout);
+
+krb5_error_code
+kau_j_as_req(const krb5_boolean ev_success, krb5_audit_state *state,
+ char **jout);
+
+krb5_error_code
+kau_j_tgs_req(const krb5_boolean ev_success, krb5_audit_state *state,
+ char **jout);
+
+krb5_error_code
+kau_j_tgs_s4u2self(const krb5_boolean ev_success, krb5_audit_state *state,
+ char **jout);
+
+krb5_error_code
+kau_j_tgs_s4u2proxy(const krb5_boolean ev_success, krb5_audit_state *state,
+ char **jout);
+
+krb5_error_code
+kau_j_tgs_u2u(const krb5_boolean ev_success, krb5_audit_state *state,
+ char **jout);
+
+#endif /* KRB5_KDC_J_ENCODE_H_INCLUDED */
--- /dev/null
+kau_j_kdc_stop
+kau_j_kdc_start
+kau_j_as_req
+kau_j_tgs_req
+kau_j_tgs_s4u2self
+kau_j_tgs_s4u2proxy
+kau_j_tgs_u2u
--- /dev/null
+mydir=plugins$(S)audit$(S)simple
+BUILDTOP=$(REL)..$(S)..$(S)..
+
+LIBBASE=k5audit
+LIBMAJOR=1
+LIBMINOR=1
+RELDIR=../plugins/audit/simple
+
+#Depends on libkrb5 and libkrb5support.
+SHLIB_EXPDEPS= $(KRB5_BASE_DEPLIBS)
+SHLIB_EXPLIBS= $(KRB5_BASE_LIBS)
+
+STOBJLISTS= OBJS.ST ../OBJS.ST
+STLIBOBJS= au_simple_main.o
+
+SRCS= $(srcdir)/au_simple_main.c
+
+all-unix:: all-liblinks
+install-unix:: install-libs
+clean-unix:: clean-liblinks clean-libs clean-libobjs
+
+clean::
+ $(RM) au_simple_main.o kdc_j_encode.o
+ $(RM) lib$(LIBBASE)$(SO_EXT)
+
+@libnover_frag@
+@libobj_frag@
--- /dev/null
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* plugins/audit/au_simple_main.c - Sample Audit plugin implementation */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This is a demo implementation of Audit JSON-based module.
+ * It utilizes MIT Kerberos <kdc_j_encode.h> routines for JSON processing and
+ * the Fedora/Debian libaudit library for audit logs.
+ */
+
+#include <k5-int.h>
+#include <krb5/audit_plugin.h>
+#include <libaudit.h>
+#include <kdc_j_encode.h>
+
+krb5_error_code
+audit_simple_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+struct krb5_audit_moddata_st {
+ int fd;
+};
+
+/* Open connection to the audit system. Returns 0 on success. */
+static krb5_error_code
+open_au(krb5_audit_moddata *auctx_out)
+{
+ krb5_error_code ret;
+ int fd = 0;
+ krb5_audit_moddata auctx;
+
+ auctx = k5calloc(1, sizeof(*auctx), &ret);
+ if (ret)
+ return ENOMEM;
+ fd = audit_open();
+ if (fd < 0)
+ return KRB5_PLUGIN_NO_HANDLE; /* audit module is unavailable */
+
+ auctx->fd = fd;
+ *auctx_out = auctx;
+
+ return 0;
+}
+
+/* Close connection to the audit system. Returns 0 on success. */
+static krb5_error_code
+close_au(krb5_audit_moddata auctx)
+{
+ int fd = auctx->fd;
+
+ audit_close(fd);
+ return 0;
+}
+
+/* Log KDC-start event. Returns 0 on success. */
+static krb5_error_code
+j_kdc_start(krb5_audit_moddata auctx, krb5_boolean ev_success)
+{
+ krb5_error_code ret = 0;
+ int local_type = AUDIT_USER_START;
+ int fd = auctx->fd;
+ char *jout = NULL;
+
+ if (fd < 0)
+ return KRB5_PLUGIN_NO_HANDLE; /* audit module is unavailable */
+
+ ret = kau_j_kdc_start(ev_success, &jout);
+ if (ret)
+ return ret;
+ if (audit_log_user_message(fd, local_type, jout,
+ NULL, NULL, NULL, ev_success) <= 0)
+ ret = EIO;
+ free(jout);
+ return ret;
+}
+
+/* Log KDC-stop event. Returns 0 on success. */
+static krb5_error_code
+j_kdc_stop(krb5_audit_moddata auctx, krb5_boolean ev_success)
+{
+ krb5_error_code ret = 0;
+ int local_type = AUDIT_USER_END;
+ int fd = auctx->fd;
+ char *jout = NULL;
+
+ if (fd < 0)
+ return KRB5_PLUGIN_NO_HANDLE; /* audit module is unavailable */
+
+ ret = kau_j_kdc_stop(ev_success, &jout);
+ if (ret)
+ return ret;
+ if (audit_log_user_message(fd, local_type, jout,
+ NULL, NULL, NULL, ev_success) <= 0)
+ ret = EIO;
+ free(jout);
+ return ret;
+}
+
+/* Log AS_REQ event. Returns 0 on success */
+static krb5_error_code
+j_as_req(krb5_audit_moddata auctx, krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ krb5_error_code ret = 0;
+ int local_type = AUDIT_USER_AUTH;
+ int fd = auctx->fd;
+ char *jout = NULL;
+
+ if (fd < 0)
+ return KRB5_PLUGIN_NO_HANDLE; /* audit module is unavailable */
+
+ ret = kau_j_as_req(ev_success, state, &jout);
+ if (ret)
+ return ret;
+ if (audit_log_user_message(fd, local_type, jout,
+ NULL, NULL, NULL, ev_success) <= 0)
+ ret = EIO;
+ free(jout);
+ return ret;
+}
+
+/* Log TGS_REQ event. Returns 0 on success */
+static krb5_error_code
+j_tgs_req(krb5_audit_moddata auctx, krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ krb5_error_code ret = 0;
+ int local_type = AUDIT_USER_AUTH;
+ int fd = auctx->fd;
+ char *jout = NULL;
+
+ if (fd < 0)
+ return KRB5_PLUGIN_NO_HANDLE; /* audit module is unavailable */
+
+ ret = kau_j_tgs_req(ev_success, state, &jout);
+ if (ret)
+ return ret;
+ if (audit_log_user_message(fd, local_type, jout,
+ NULL, NULL, NULL, ev_success) <= 0)
+ ret = EIO;
+ free(jout);
+ return ret;
+}
+
+/* Log S4U2SELF event. Returns 0 on success */
+static krb5_error_code
+j_tgs_s4u2self(krb5_audit_moddata auctx, krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ krb5_error_code ret = 0;
+ int local_type = AUDIT_USER_AUTH;
+ int fd = auctx->fd;
+ char *jout = NULL;
+
+ if (fd < 0)
+ return KRB5_PLUGIN_NO_HANDLE; /* audit module is unavailable */
+
+ ret = kau_j_tgs_s4u2self(ev_success, state, &jout);
+ if (ret)
+ return ret;
+ if (audit_log_user_message(fd, local_type, jout,
+ NULL, NULL, NULL, ev_success) <= 0)
+ ret = EIO;
+ free(jout);
+ return ret;
+}
+
+/* Log S4U2PROXY event. Returns 0 on success */
+static krb5_error_code
+j_tgs_s4u2proxy(krb5_audit_moddata auctx, krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ krb5_error_code ret = 0;
+ int local_type = AUDIT_USER_AUTH;
+ int fd = auctx->fd;
+ char *jout = NULL;
+
+ if (fd < 0)
+ return KRB5_PLUGIN_NO_HANDLE; /* audit module is unavailable */
+
+ ret = kau_j_tgs_s4u2proxy(ev_success, state, &jout);
+ if (ret)
+ return ret;
+ if (audit_log_user_message(fd, local_type, jout,
+ NULL, NULL, NULL, ev_success) <= 0)
+ ret = EIO;
+ free(jout);
+ return ret;
+}
+
+/* Log user-to-user event. Returns 0 on success */
+static krb5_error_code
+j_tgs_u2u(krb5_audit_moddata auctx, krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ krb5_error_code ret = 0;
+ int local_type = AUDIT_USER_AUTH;
+ int fd = auctx->fd;
+ char *jout = NULL;
+
+ if (fd < 0)
+ return KRB5_PLUGIN_NO_HANDLE; /* audit module is unavailable */
+
+ ret = kau_j_tgs_u2u(ev_success, state, &jout);
+ if (ret)
+ return ret;
+ if (audit_log_user_message(fd, local_type, jout,
+ NULL, NULL, NULL, ev_success) <= 0)
+ ret = EIO;
+ free(jout);
+ return ret;
+}
+
+krb5_error_code
+audit_simple_initvt(krb5_context context, int maj_ver,
+ int min_ver, krb5_plugin_vtable vtable)
+{
+ krb5_audit_vtable vt;
+
+ if (maj_ver != 1)
+ return KRB5_PLUGIN_VER_NOTSUPP;
+
+ vt = (krb5_audit_vtable)vtable;
+ vt->name = "simple";
+ vt->open = open_au;
+ vt->close = close_au;
+ vt->kdc_start = j_kdc_start;
+ vt->kdc_stop = j_kdc_stop;
+ vt->as_req = j_as_req;
+ vt->tgs_req = j_tgs_req;
+ vt->tgs_s4u2self = j_tgs_s4u2self;
+ vt->tgs_s4u2proxy = j_tgs_s4u2proxy;
+ vt->tgs_u2u = j_tgs_u2u;
+ return 0;
+}
--- /dev/null
+#
+# Generated makefile dependencies follow.
+#
+au_simple_main.so au_simple_main.po $(OUTPRE)au_simple_main.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/kdc_j_encode.h \
+ $(BUILDTOP)/include/krb5/audit_plugin.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ au_simple_main.c
--- /dev/null
+audit_simple_initvt
--- /dev/null
+mydir=plugins$(S)audit$(S)test
+BUILDTOP=$(REL)..$(S)..$(S)..
+
+LIBBASE=k5audit_test
+LIBMAJOR=0
+LIBMINOR=0
+RELDIR=../plugins/audit/test
+# Depends on libkrb5 and libkrb5support.
+SHLIB_EXPDEPS= $(KRB5_BASE_DEPLIBS)
+SHLIB_EXPLIBS= $(KRB5_BASE_LIBS)
+
+STOBJLISTS= OBJS.ST ../OBJS.ST
+STLIBOBJS= au_test.o
+
+SRCS= $(srcdir)/au_test.c
+
+all-unix:: all-liblinks
+clean-unix:: clean-liblinks clean-libs clean-libobjs
+
+@libnover_frag@
+@libobj_frag@
--- /dev/null
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* plugins/audit/au_test.c - Test Audit plugin implementation */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * This test is to verify the JSON-based KDC audit functionality.
+ * It utilized MIT Kerberos <kdc_j_encode.h> routines for JSON processing.
+ */
+
+#include <k5-int.h>
+#include <krb5/audit_plugin.h>
+#include <kdc_j_encode.h>
+#include "k5-thread.h"
+
+struct krb5_audit_moddata_st {
+ int au_fd;
+};
+
+krb5_error_code
+audit_test_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+static FILE *au_fd;
+static k5_mutex_t lock = K5_MUTEX_PARTIAL_INITIALIZER;
+
+/* Open connection to the audit system. Returns 0 on success. */
+static krb5_error_code
+open_au(krb5_audit_moddata *auctx)
+{
+ au_fd = fopen("au.log", "a+");
+ if ( au_fd < 0) {
+ return KRB5_PLUGIN_NO_HANDLE; /* audit module is unavailable */
+ }
+ k5_mutex_init(&lock);
+ return 0;
+}
+
+/* Close connection to the audit system. Returns 0. */
+static krb5_error_code
+close_au(krb5_audit_moddata auctx)
+{
+ fclose(au_fd);
+ k5_mutex_destroy(&lock);
+ return 0;
+}
+
+/* Log KDC-start event. Returns 0 on success. */
+static krb5_error_code
+j_kdc_start(krb5_audit_moddata auctx, krb5_boolean ev_success)
+{
+ krb5_error_code ret = 0;
+ char *jout = NULL;
+
+ ret = kau_j_kdc_start(ev_success, &jout);
+ if (ret)
+ return ret;
+ k5_mutex_lock(&lock);
+ fprintf(au_fd,"%s\n", jout);
+ fflush(au_fd);
+ k5_mutex_unlock(&lock);
+ free(jout);
+ return ret;
+}
+
+/* Log KDC-stop event. Returns 0 on success. */
+static krb5_error_code
+j_kdc_stop(krb5_audit_moddata auctx, krb5_boolean ev_success)
+{
+ krb5_error_code ret = 0;
+ char *jout = NULL;
+
+ ret = kau_j_kdc_stop(ev_success, &jout);
+ if (ret)
+ return ret;
+ k5_mutex_lock(&lock);
+ fprintf(au_fd,"%s\n", jout);
+ fflush(au_fd);
+ k5_mutex_unlock(&lock);
+ free(jout);
+ return ret;
+}
+
+/* Log AS_REQ event. Returns 0 on success. */
+static krb5_error_code
+j_as_req(krb5_audit_moddata auctx, krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ krb5_error_code ret = 0;
+ char *jout = NULL;
+
+ ret = kau_j_as_req(ev_success, state, &jout);
+ if (ret)
+ return ret;
+ k5_mutex_lock(&lock);
+ fprintf(au_fd,"%s\n", jout);
+ fflush(au_fd);
+ k5_mutex_unlock(&lock);
+ free(jout);
+ return ret;
+}
+
+/* Log TGS_REQ event. Returns 0 on success. */
+static krb5_error_code
+j_tgs_req(krb5_audit_moddata auctx, krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ krb5_error_code ret = 0;
+ char *jout = NULL;
+
+ ret = kau_j_tgs_req(ev_success, state, &jout);
+ if (ret)
+ return ret;
+ k5_mutex_lock(&lock);
+ fprintf(au_fd,"%s\n", jout);
+ fflush(au_fd);
+ k5_mutex_unlock(&lock);
+ free(jout);
+ return ret;
+}
+
+/* Log S4U2SELF event. Returns 0 on success. */
+static krb5_error_code
+j_tgs_s4u2self(krb5_audit_moddata auctx, krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ krb5_error_code ret = 0;
+ char *jout = NULL;
+
+ ret = kau_j_tgs_s4u2self(ev_success, state, &jout);
+ if (ret)
+ return ret;
+ k5_mutex_lock(&lock);
+ fprintf(au_fd,"%s\n", jout);
+ fflush(au_fd);
+ k5_mutex_unlock(&lock);
+ free(jout);
+ return ret;
+}
+
+/* Log S4U2PROXY event. Returns 0 on success. */
+static krb5_error_code
+j_tgs_s4u2proxy(krb5_audit_moddata auctx, krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ krb5_error_code ret = 0;
+ char *jout = NULL;
+
+ ret = kau_j_tgs_s4u2proxy(ev_success, state, &jout);
+ if (ret)
+ return ret;
+ k5_mutex_lock(&lock);
+ fprintf(au_fd,"%s\n", jout);
+ fflush(au_fd);
+ k5_mutex_unlock(&lock);
+ free(jout);
+ return ret;
+}
+
+/* Log user-to-user event. Returns 0 on success. */
+static krb5_error_code
+j_tgs_u2u(krb5_audit_moddata auctx, krb5_boolean ev_success,
+ krb5_audit_state *state)
+{
+ krb5_error_code ret = 0;
+ char *jout = NULL;
+
+ ret = kau_j_tgs_u2u(ev_success, state, &jout);
+ if (ret)
+ return ret;
+ k5_mutex_lock(&lock);
+ fprintf(au_fd,"%s\n", jout);
+ fflush(au_fd);
+ k5_mutex_unlock(&lock);
+ free(jout);
+ return ret;
+}
+
+krb5_error_code
+audit_test_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_audit_vtable vt;
+
+ if (maj_ver != 1)
+ return KRB5_PLUGIN_VER_NOTSUPP;
+
+ vt = (krb5_audit_vtable)vtable;
+ vt->name = "test";
+
+ vt->open = open_au;
+ vt->close = close_au;
+ vt->kdc_start = j_kdc_start;
+ vt->kdc_stop = j_kdc_stop;
+ vt->as_req = j_as_req;
+ vt->tgs_req = j_tgs_req;
+ vt->tgs_s4u2self = j_tgs_s4u2self;
+ vt->tgs_s4u2proxy = j_tgs_s4u2proxy;
+ vt->tgs_u2u = j_tgs_u2u;
+
+ return 0;
+}
--- /dev/null
+#
+# Generated makefile dependencies follow.
+#
+au_test.so au_test.po $(OUTPRE)au_test.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/kdc_j_encode.h $(BUILDTOP)/include/krb5/audit_plugin.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h au_test.c
--- /dev/null
+audit_test_initvt
$(RUNPYTEST) $(srcdir)/t_cve-2012-1015.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_cve-2013-1416.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_cve-2013-1417.py $(PYTESTFLAGS)
+ $(RM) au.log
+ $(RUNPYTEST) $(srcdir)/t_audit.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/jsonwalker.py -d $(srcdir)/au_dict.json \
+ -i au.log
clean::
$(RM) gcred hist hrealm kdbtest plugorder responder
$(RM) t_init_creds t_localauth krb5.conf kdc.conf
$(RM) -rf kdc_realm/sandbox ldap
+ $(RM) au.log
--- /dev/null
+{
+"event_name":"",
+"event_success":0,
+"evidence_tkt_id":"",
+"fromport":0,
+"fromaddr":{
+ "type":0,
+ "length":0,
+ "ipv4":[]},
+"kdc_status":"",
+"rep_etype":0,
+"rep.ticket":{
+ "authtime":0,
+ "cname":{
+ "components":[],
+ "realm":"",
+ "length":0,
+ "type":0},
+ "end":0,
+ "flags":0,
+ "sess_etype":0,
+ "srv_etype":0,
+ "sname":{
+ "components":[],
+ "realm":"",
+ "length":0,
+ "type":0}},
+"req.avail_etypes":[],
+"req.client":{
+ "components":[],
+ "realm":"",
+ "length":0,
+ "type":0},
+"req_id":"",
+"req.kdc_options":0,
+"req.pa_type":[]
+"req.server":{
+ "components":[],
+ "realm":"",
+ "length":0,
+ "type":0},
+"req.tkt_end":0,
+"s4u2proxy_user":{
+ "components":[],
+ "realm":"",
+ "length":0,
+ "type":0},
+"s4u2self_user":{
+ "components":[],
+ "realm":"",
+ "length":0,
+ "type":0},
+"stage":1,
+"tkt_in_id":"",
+"tkt_renewed":0,
+"tkt_out_id":"",
+"tkt_validated":0,
+"u2u_user":{
+ "components":[],
+ "realm":"",
+ "length":0,
+ "type":0},
+"violation":0
+}
--- /dev/null
+#!/usr/bin/python
+
+import sys
+try:
+ import cjson
+except ImportError:
+ print "Warning: skipping audit log verification because the cjson module" \
+ " is unavailable"
+ sys.exit(0)
+from collections import defaultdict
+from optparse import OptionParser
+
+class Parser(object):
+ DEFAULTS = {int:0,
+ str:'',
+ list:[]}
+
+ def __init__(self, defconf=None):
+ self.defaults = None
+ if defconf is not None:
+ self.defaults = self.flatten(defconf)
+
+ def run(self, logs, verbose=None):
+ result = self.parse(logs)
+ if len(result) != len(self.defaults):
+ diff = set(self.defaults.keys()).difference(result.keys())
+ print 'Test failed.'
+ print 'The following attributes were not set:'
+ for it in diff:
+ print it
+ sys.exit(1)
+
+ def flatten(self, defaults):
+ """
+ Flattens pathes to attributes.
+
+ Parameters
+ ----------
+ defaults : a dictionaries populated with default values
+
+ Returns :
+ dict : with flattened attributes
+ """
+ result = dict()
+ for path,value in self._walk(defaults):
+ if path in result:
+ print 'Warning: attribute path %s already exists' % path
+ result[path] = value
+
+ return result
+
+ def parse(self, logs):
+ result = defaultdict(list)
+ for msg in logs:
+ # each message is treated as a dictionary of dictionaries
+ for a,v in self._walk(msg):
+ # see if path is registered in defaults
+ if a in self.defaults:
+ dv = self.defaults.get(a)
+ if dv is None:
+ # determine default value by type
+ if v is not None:
+ dv = self.DEFAULTS[type(v)]
+ else:
+ print 'Warning: attribute %s is set to None' % a
+ continue
+ # by now we have default value
+ if v != dv:
+ # test passed
+ result[a].append(v)
+ return result
+
+ def _walk(self, adict):
+ """
+ Generator that works through dictionary.
+ """
+ for a,v in adict.iteritems():
+ if isinstance(v,dict):
+ for (attrpath,u) in self._walk(v):
+ yield (a+'.'+attrpath,u)
+ else:
+ yield (a,v)
+
+
+if __name__ == '__main__':
+
+ parser = OptionParser()
+ parser.add_option("-i", "--logfile", dest="filename",
+ help="input log file in json fmt", metavar="FILE")
+ parser.add_option("-d", "--defaults", dest="defaults",
+ help="dictionary with defaults", metavar="FILE")
+
+ (options, args) = parser.parse_args()
+ if options.filename is not None:
+ with open(options.filename, 'r') as f:
+ content = list()
+ for l in f:
+ content.append(cjson.decode(l.rstrip()))
+ f.close()
+ else:
+ print 'Input file in jason format is required'
+ exit()
+
+ defaults = None
+ if options.defaults is not None:
+ with open(options.defaults, 'r') as f:
+ defaults = cjson.decode(f.read())
+ f.close()
+
+ # run test
+ p = Parser(defaults)
+ p.run(content)
+ exit()
--- /dev/null
+#!/usr/bin/python
+from k5test import *
+
+conf = {'plugins': {'audit': {
+ 'module': 'test:$plugins/audit/test/k5audit_test.so'}}}
+
+realm = K5Realm(krb5_conf=conf, get_creds=False)
+realm.addprinc('target')
+realm.run_kadminl('modprinc +ok_to_auth_as_delegate ' + realm.host_princ)
+
+# Make normal AS and TGS requests so they will be audited.
+realm.kinit(realm.host_princ, flags=['-k', '-f'])
+realm.run([kvno, 'target'])
+
+# Make S4U2Self and S4U2Proxy requests so they will be audited. The
+# S4U2Proxy request is expected to fail.
+out = realm.run([kvno, '-k', realm.keytab, '-U', 'user', '-P', 'target'],
+ expected_code=1)
+if 'NOT_ALLOWED_TO_DELEGATE' not in out:
+ fail('Unexpected error for S4U2Proxy')
+
+# Make a U2U request so it will be audited.
+uuserver = os.path.join(buildtop, 'appl', 'user_user', 'uuserver')
+uuclient = os.path.join(buildtop, 'appl', 'user_user', 'uuclient')
+port_arg = str(realm.server_port())
+realm.start_server([uuserver, port_arg], 'Server started')
+output = realm.run([uuclient, hostname, 'testing message', port_arg])
+if 'Hello' not in output:
+ fail('U2U request failed unexpectedly')
+
+success('Audit tests')