summaryrefslogtreecommitdiffstats
path: root/lib/audit_logging
diff options
context:
space:
mode:
Diffstat (limited to 'lib/audit_logging')
-rw-r--r--lib/audit_logging/audit_logging.c1084
-rw-r--r--lib/audit_logging/audit_logging.h101
-rw-r--r--lib/audit_logging/tests/audit_logging_error_test.c869
-rw-r--r--lib/audit_logging/tests/audit_logging_test.c885
-rw-r--r--lib/audit_logging/wscript_build56
5 files changed, 2995 insertions, 0 deletions
diff --git a/lib/audit_logging/audit_logging.c b/lib/audit_logging/audit_logging.c
new file mode 100644
index 0000000..43acf95
--- /dev/null
+++ b/lib/audit_logging/audit_logging.c
@@ -0,0 +1,1084 @@
+/*
+ common routines for audit logging
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Error handling:
+ *
+ */
+
+#include "includes.h"
+
+#include "librpc/ndr/libndr.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/messaging/messaging.h"
+#include "auth/common_auth.h"
+#include "audit_logging.h"
+
+/*
+ * @brief Get a human readable timestamp.
+ *
+ * Returns the current time formatted as
+ * "Tue, 14 Mar 2017 08:38:42.209028 NZDT"
+ *
+ * The returned string is allocated by talloc in the supplied context.
+ * It is the callers responsibility to free it.
+ *
+ * @param mem_ctx talloc memory context that owns the returned string.
+ *
+ * @return a human readable time stamp, or NULL in the event of an error.
+ *
+ */
+char* audit_get_timestamp(TALLOC_CTX *frame)
+{
+ char buffer[40]; /* formatted time less usec and timezone */
+ char tz[10]; /* formatted time zone */
+ struct tm* tm_info; /* current local time */
+ struct timeval tv; /* current system time */
+ int ret; /* response code */
+ char * ts; /* formatted time stamp */
+
+ ret = gettimeofday(&tv, NULL);
+ if (ret != 0) {
+ DBG_ERR("Unable to get time of day: (%d) %s\n",
+ errno,
+ strerror(errno));
+ return NULL;
+ }
+
+ tm_info = localtime(&tv.tv_sec);
+ if (tm_info == NULL) {
+ DBG_ERR("Unable to determine local time\n");
+ return NULL;
+ }
+
+ strftime(buffer, sizeof(buffer)-1, "%a, %d %b %Y %H:%M:%S", tm_info);
+ strftime(tz, sizeof(tz)-1, "%Z", tm_info);
+ ts = talloc_asprintf(frame, "%s.%06ld %s", buffer, (long)tv.tv_usec, tz);
+ if (ts == NULL) {
+ DBG_ERR("Out of memory formatting time stamp\n");
+ }
+ return ts;
+}
+
+/*
+ * @brief write an audit message to the audit logs.
+ *
+ * Write a human readable text audit message to the samba logs.
+ *
+ * @param prefix Text to be printed at the start of the log line
+ * @param message The content of the log line.
+ * @param debub_class The debug class to log the message with.
+ * @param debug_level The debug level to log the message with.
+ */
+void audit_log_human_text(const char* prefix,
+ const char* message,
+ int debug_class,
+ int debug_level)
+{
+ DEBUGC(debug_class, debug_level, ("%s %s\n", prefix, message));
+}
+
+#ifdef HAVE_JANSSON
+/*
+ * Constant for empty json object initialisation
+ */
+const struct json_object json_empty_object = {.valid = false, .root = NULL};
+/*
+ * @brief write a json object to the samba audit logs.
+ *
+ * Write the json object to the audit logs as a formatted string
+ *
+ * @param message The content of the log line.
+ * @param debub_class The debug class to log the message with.
+ * @param debug_level The debug level to log the message with.
+ */
+void audit_log_json(struct json_object* message,
+ int debug_class,
+ int debug_level)
+{
+ TALLOC_CTX *frame = NULL;
+ char *s = NULL;
+
+ if (json_is_invalid(message)) {
+ DBG_ERR("Invalid JSON object, unable to log\n");
+ return;
+ }
+
+ frame = talloc_stackframe();
+ s = json_to_string(frame, message);
+ if (s == NULL) {
+ DBG_ERR("json_to_string returned NULL, "
+ "JSON audit message could not written\n");
+ TALLOC_FREE(frame);
+ return;
+ }
+ /*
+ * This is very strange, but we call this routine to get a log
+ * output without the header. JSON logs all have timestamps
+ * so this only makes parsing harder.
+ *
+ * We push out the raw JSON blob without a prefix, consumers
+ * can find such lines by the leading {
+ */
+ DEBUGADDC(debug_class, debug_level, ("%s\n", s));
+ TALLOC_FREE(frame);
+}
+
+/*
+ * @brief get a connection to the messaging event server.
+ *
+ * Get a connection to the messaging event server registered by server_name.
+ *
+ * @param msg_ctx a valid imessaging_context.
+ * @param server_name name of messaging event server to connect to.
+ * @param server_id The event server details to populate
+ *
+ * @return NTSTATUS
+ */
+static NTSTATUS get_event_server(
+ struct imessaging_context *msg_ctx,
+ const char *server_name,
+ struct server_id *event_server)
+{
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ unsigned num_servers, i;
+ struct server_id *servers;
+
+ status = irpc_servers_byname(
+ msg_ctx,
+ frame,
+ server_name,
+ &num_servers,
+ &servers);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Failed to find the target '%s' on the message bus "
+ "to send JSON audit events to: %s\n",
+ server_name,
+ nt_errstr(status));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ /*
+ * Select the first server that is listening, because we get
+ * connection refused as NT_STATUS_OBJECT_NAME_NOT_FOUND
+ * without waiting
+ */
+ for (i = 0; i < num_servers; i++) {
+ status = imessaging_send(
+ msg_ctx,
+ servers[i],
+ MSG_PING,
+ &data_blob_null);
+ if (NT_STATUS_IS_OK(status)) {
+ *event_server = servers[i];
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+ }
+ DBG_NOTICE(
+ "Failed to find '%s' registered on the message bus to "
+ "send JSON audit events to: %s\n",
+ server_name,
+ nt_errstr(status));
+ TALLOC_FREE(frame);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+/*
+ * @brief send an audit message to a messaging event server.
+ *
+ * Send the message to a registered and listening event server.
+ * Note: Any errors are logged, and the message is not sent. This is to ensure
+ * that a poorly behaved event server does not impact Samba.
+ *
+ * As it is possible to lose messages, especially during server
+ * shut down, currently this function is primarily intended for use
+ * in integration tests.
+ *
+ * @param msg_ctx an imessaging_context, can be NULL in which case no message
+ * will be sent.
+ * @param server_name the naname of the event server to send the message to.
+ * @param messag_type A message type defined in librpc/idl/messaging.idl
+ * @param message The message to send.
+ *
+ */
+void audit_message_send(
+ struct imessaging_context *msg_ctx,
+ const char *server_name,
+ uint32_t message_type,
+ struct json_object *message)
+{
+ struct server_id event_server = {
+ .pid = 0,
+ };
+ NTSTATUS status;
+
+ const char *message_string = NULL;
+ DATA_BLOB message_blob = data_blob_null;
+ TALLOC_CTX *ctx = NULL;
+
+ if (json_is_invalid(message)) {
+ DBG_ERR("Invalid JSON object, unable to send\n");
+ return;
+ }
+ if (msg_ctx == NULL) {
+ DBG_DEBUG("No messaging context\n");
+ return;
+ }
+
+ ctx = talloc_new(NULL);
+ if (ctx == NULL) {
+ DBG_ERR("Out of memory creating temporary context\n");
+ return;
+ }
+
+ /* Need to refetch the address each time as the destination server may
+ * have disconnected and reconnected in the interim, in which case
+ * messages may get lost
+ */
+ status = get_event_server(msg_ctx, server_name, &event_server);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(ctx);
+ return;
+ }
+
+ message_string = json_to_string(ctx, message);
+ message_blob = data_blob_string_const(message_string);
+ status = imessaging_send(
+ msg_ctx,
+ event_server,
+ message_type,
+ &message_blob);
+
+ /*
+ * If the server crashed, try to find it again
+ */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ status = get_event_server(msg_ctx, server_name, &event_server);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(ctx);
+ return;
+ }
+ imessaging_send(
+ msg_ctx,
+ event_server,
+ message_type,
+ &message_blob);
+ }
+ TALLOC_FREE(ctx);
+}
+
+/*
+ * @brief Create a new struct json_object, wrapping a JSON Object.
+ *
+ * Create a new json object, the json_object wraps the underlying json
+ * implementations JSON Object representation.
+ *
+ * Free with a call to json_free_object, note that the jansson implementation
+ * allocates memory with malloc and not talloc.
+ *
+ * @return a struct json_object, valid will be set to false if the object
+ * could not be created.
+ *
+ */
+struct json_object json_new_object(void) {
+
+ struct json_object object = json_empty_object;
+
+ object.root = json_object();
+ if (object.root == NULL) {
+ object.valid = false;
+ DBG_ERR("Unable to create JSON object\n");
+ return object;
+ }
+ object.valid = true;
+ return object;
+}
+
+/*
+ * @brief Create a new struct json_object wrapping a JSON Array.
+ *
+ * Create a new json object, the json_object wraps the underlying json
+ * implementations JSON Array representation.
+ *
+ * Free with a call to json_free_object, note that the jansson implementation
+ * allocates memory with malloc and not talloc.
+ *
+ * @return a struct json_object, error will be set to true if the array
+ * could not be created.
+ *
+ */
+struct json_object json_new_array(void) {
+
+ struct json_object array = json_empty_object;
+
+ array.root = json_array();
+ if (array.root == NULL) {
+ array.valid = false;
+ DBG_ERR("Unable to create JSON array\n");
+ return array;
+ }
+ array.valid = true;
+ return array;
+}
+
+
+/*
+ * @brief free and invalidate a previously created JSON object.
+ *
+ * Release any resources owned by a json_object, and then mark the structure
+ * as invalid. It is safe to call this multiple times on an object.
+ *
+ */
+void json_free(struct json_object *object)
+{
+ if (object->root != NULL) {
+ json_decref(object->root);
+ }
+ object->root = NULL;
+ object->valid = false;
+}
+
+/*
+ * @brief is the current JSON object invalid?
+ *
+ * Check the state of the object to determine if it is invalid.
+ *
+ * @return is the object valid?
+ *
+ */
+bool json_is_invalid(const struct json_object *object)
+{
+ return !object->valid;
+}
+
+/*
+ * @brief Add an integer value to a JSON object.
+ *
+ * Add an integer value named 'name' to the json object.
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name of the value.
+ * @param value the value.
+ *
+ * @return 0 the operation was successful
+ * -1 the operation failed
+ *
+ */
+int json_add_int(struct json_object *object, const char *name, const int value)
+{
+ int ret = 0;
+ json_t *integer = NULL;
+
+ if (json_is_invalid(object)) {
+ DBG_ERR("Unable to add int [%s] value [%d], "
+ "target object is invalid\n",
+ name,
+ value);
+ return JSON_ERROR;
+ }
+
+ integer = json_integer(value);
+ if (integer == NULL) {
+ DBG_ERR("Unable to create integer value [%s] value [%d]\n",
+ name,
+ value);
+ return JSON_ERROR;
+ }
+
+ ret = json_object_set_new(object->root, name, integer);
+ if (ret != 0) {
+ json_decref(integer);
+ DBG_ERR("Unable to add int [%s] value [%d]\n", name, value);
+ }
+ return ret;
+}
+
+/*
+ * @brief Add a boolean value to a JSON object.
+ *
+ * Add a boolean value named 'name' to the json object.
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param value the value.
+ *
+ * @return 0 the operation was successful
+ * -1 the operation failed
+ *
+ */
+int json_add_bool(struct json_object *object,
+ const char *name,
+ const bool value)
+{
+ int ret = 0;
+
+ if (json_is_invalid(object)) {
+ DBG_ERR("Unable to add boolean [%s] value [%d], "
+ "target object is invalid\n",
+ name,
+ value);
+ return JSON_ERROR;
+ }
+
+ ret = json_object_set_new(object->root, name, json_boolean(value));
+ if (ret != 0) {
+ DBG_ERR("Unable to add boolean [%s] value [%d]\n", name, value);
+ }
+ return ret;
+}
+
+/*
+ * @brief Add a string value to a JSON object.
+ *
+ * Add a string value named 'name' to the json object.
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param value the value.
+ *
+ * @return 0 the operation was successful
+ * -1 the operation failed
+ *
+ */
+int json_add_string(struct json_object *object,
+ const char *name,
+ const char *value)
+{
+ int ret = 0;
+
+ if (json_is_invalid(object)) {
+ DBG_ERR("Unable to add string [%s], target object is invalid\n",
+ name);
+ return JSON_ERROR;
+ }
+ if (value) {
+ json_t *string = json_string(value);
+ if (string == NULL) {
+ DBG_ERR("Unable to add string [%s], "
+ "could not create string object\n",
+ name);
+ return JSON_ERROR;
+ }
+ ret = json_object_set_new(object->root, name, string);
+ if (ret != 0) {
+ json_decref(string);
+ DBG_ERR("Unable to add string [%s]\n", name);
+ return ret;
+ }
+ } else {
+ ret = json_object_set_new(object->root, name, json_null());
+ if (ret != 0) {
+ DBG_ERR("Unable to add null string [%s]\n", name);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+/*
+ * @brief Assert that the current JSON object is an array.
+ *
+ * Check that the current object is a JSON array, and if not
+ * invalidate the object. We also log an error message as this indicates
+ * bug in the calling code.
+ *
+ * @param object the JSON object to be validated.
+ */
+void json_assert_is_array(struct json_object *array) {
+
+ if (json_is_invalid(array)) {
+ return;
+ }
+
+ if (json_is_array(array->root) == false) {
+ DBG_ERR("JSON object is not an array\n");
+ array->valid = false;
+ return;
+ }
+}
+
+/*
+ * @brief Add a JSON object to a JSON object.
+ *
+ * Add a JSON object named 'name' to the json object.
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param value the value.
+ *
+ * @return 0 the operation was successful
+ * -1 the operation failed
+ *
+ */
+int json_add_object(struct json_object *object,
+ const char *name,
+ struct json_object *value)
+{
+ int ret = 0;
+ json_t *jv = NULL;
+
+ if (value != NULL && json_is_invalid(value)) {
+ DBG_ERR("Invalid JSON object [%s] supplied\n", name);
+ return JSON_ERROR;
+ }
+ if (json_is_invalid(object)) {
+ DBG_ERR("Unable to add object [%s], target object is invalid\n",
+ name);
+ return JSON_ERROR;
+ }
+
+ jv = value == NULL ? json_null() : value->root;
+
+ if (json_is_array(object->root)) {
+ ret = json_array_append_new(object->root, jv);
+ } else if (json_is_object(object->root)) {
+ ret = json_object_set_new(object->root, name, jv);
+ } else {
+ DBG_ERR("Invalid JSON object type\n");
+ ret = JSON_ERROR;
+ }
+ if (ret != 0) {
+ DBG_ERR("Unable to add object [%s]\n", name);
+ }
+ return ret;
+}
+
+/*
+ * @brief Add a string to a JSON object, truncating if necessary.
+ *
+ *
+ * Add a string value named 'name' to the json object, the string will be
+ * truncated if it is more than len characters long. If len is 0 the value
+ * is encoded as a JSON null.
+ *
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param value the value.
+ * @param len the maximum number of characters to be copied.
+ *
+ * @return 0 the operation was successful
+ * -1 the operation failed
+ *
+ */
+int json_add_stringn(struct json_object *object,
+ const char *name,
+ const char *value,
+ const size_t len)
+{
+
+ int ret = 0;
+ if (json_is_invalid(object)) {
+ DBG_ERR("Unable to add string [%s], target object is invalid\n",
+ name);
+ return JSON_ERROR;
+ }
+
+ if (value != NULL && len > 0) {
+ json_t *string = NULL;
+ char buffer[len+1];
+
+ strncpy(buffer, value, len);
+ buffer[len] = '\0';
+
+ string = json_string(buffer);
+ if (string == NULL) {
+ DBG_ERR("Unable to add string [%s], "
+ "could not create string object\n",
+ name);
+ return JSON_ERROR;
+ }
+ ret = json_object_set_new(object->root, name, string);
+ if (ret != 0) {
+ json_decref(string);
+ DBG_ERR("Unable to add string [%s]\n", name);
+ return ret;
+ }
+ } else {
+ ret = json_object_set_new(object->root, name, json_null());
+ if (ret != 0) {
+ DBG_ERR("Unable to add null string [%s]\n", name);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+/*
+ * @brief Add a version object to a JSON object
+ *
+ * Add a version object to the JSON object
+ * "version":{"major":1, "minor":0}
+ *
+ * The version tag is intended to aid the processing of the JSON messages
+ * The major version number should change when an attribute is:
+ * - renamed
+ * - removed
+ * - its meaning changes
+ * - its contents change format
+ * The minor version should change whenever a new attribute is added and for
+ * minor bug fixes to an attributes content.
+ *
+ *
+ * @param object the JSON object to be updated.
+ * @param major the major version number
+ * @param minor the minor version number
+ *
+ * @return 0 the operation was successful
+ * -1 the operation failed
+ */
+int json_add_version(struct json_object *object, int major, int minor)
+{
+ int ret = 0;
+ struct json_object version;
+
+ if (json_is_invalid(object)) {
+ DBG_ERR("Unable to add version, target object is invalid\n");
+ return JSON_ERROR;
+ }
+
+ version = json_new_object();
+ if (json_is_invalid(&version)) {
+ DBG_ERR("Unable to add version, failed to create object\n");
+ return JSON_ERROR;
+ }
+ ret = json_add_int(&version, "major", major);
+ if (ret != 0) {
+ json_free(&version);
+ return ret;
+ }
+ ret = json_add_int(&version, "minor", minor);
+ if (ret != 0) {
+ json_free(&version);
+ return ret;
+ }
+ ret = json_add_object(object, "version", &version);
+ if (ret != 0) {
+ json_free(&version);
+ return ret;
+ }
+ return ret;
+}
+
+/*
+ * @brief add an ISO 8601 timestamp to the object.
+ *
+ * Add the current date and time as a timestamp in ISO 8601 format
+ * to a JSON object
+ *
+ * "timestamp":"2017-03-06T17:18:04.455081+1300"
+ *
+ *
+ * @param object the JSON object to be updated.
+ *
+ * @return 0 the operation was successful
+ * -1 the operation failed
+ */
+int json_add_timestamp(struct json_object *object)
+{
+ char buffer[40]; /* formatted time less usec and timezone */
+ char timestamp[65]; /* the formatted ISO 8601 time stamp */
+ char tz[10]; /* formatted time zone */
+ struct tm* tm_info; /* current local time */
+ struct timeval tv; /* current system time */
+ int r; /* response code from gettimeofday */
+ int ret; /* return code from json operations */
+
+ if (json_is_invalid(object)) {
+ DBG_ERR("Unable to add time stamp, target object is invalid\n");
+ return JSON_ERROR;
+ }
+
+ r = gettimeofday(&tv, NULL);
+ if (r) {
+ DBG_ERR("Unable to get time of day: (%d) %s\n",
+ errno,
+ strerror(errno));
+ return JSON_ERROR;
+ }
+
+ tm_info = localtime(&tv.tv_sec);
+ if (tm_info == NULL) {
+ DBG_ERR("Unable to determine local time\n");
+ return JSON_ERROR;
+ }
+
+ strftime(buffer, sizeof(buffer)-1, "%Y-%m-%dT%T", tm_info);
+ strftime(tz, sizeof(tz)-1, "%z", tm_info);
+ snprintf(
+ timestamp,
+ sizeof(timestamp),
+ "%s.%06ld%s",
+ buffer,
+ tv.tv_usec,
+ tz);
+ ret = json_add_string(object, "timestamp", timestamp);
+ if (ret != 0) {
+ DBG_ERR("Unable to add time stamp to JSON object\n");
+ }
+ return ret;
+}
+
+/*
+ *@brief Add a tsocket_address to a JSON object
+ *
+ * Add the string representation of a Samba tsocket_address to the object.
+ *
+ * "localAddress":"ipv6::::0"
+ *
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param address the tsocket_address.
+ *
+ * @return 0 the operation was successful
+ * -1 the operation failed
+ *
+ */
+int json_add_address(struct json_object *object,
+ const char *name,
+ const struct tsocket_address *address)
+{
+ int ret = 0;
+
+ if (json_is_invalid(object)) {
+ DBG_ERR("Unable to add address [%s], "
+ "target object is invalid\n",
+ name);
+ return JSON_ERROR;
+ }
+
+ if (address == NULL) {
+ ret = json_object_set_new(object->root, name, json_null());
+ if (ret != 0) {
+ DBG_ERR("Unable to add null address [%s]\n", name);
+ return JSON_ERROR;
+ }
+ } else {
+ TALLOC_CTX *ctx = talloc_new(NULL);
+ char *s = NULL;
+
+ if (ctx == NULL) {
+ DBG_ERR("Out of memory adding address [%s]\n", name);
+ return JSON_ERROR;
+ }
+
+ s = tsocket_address_string(address, ctx);
+ if (s == NULL) {
+ DBG_ERR("Out of memory adding address [%s]\n", name);
+ TALLOC_FREE(ctx);
+ return JSON_ERROR;
+ }
+ ret = json_add_string(object, name, s);
+ if (ret != 0) {
+ DBG_ERR(
+ "Unable to add address [%s] value [%s]\n", name, s);
+ TALLOC_FREE(ctx);
+ return JSON_ERROR;
+ }
+ TALLOC_FREE(ctx);
+ }
+ return ret;
+}
+
+/*
+ * @brief Add a formatted string representation of a sid to a json object.
+ *
+ * Add the string representation of a Samba sid to the object.
+ *
+ * "sid":"S-1-5-18"
+ *
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param sid the sid
+ *
+ * @return 0 the operation was successful
+ * -1 the operation failed
+ *
+ */
+int json_add_sid(struct json_object *object,
+ const char *name,
+ const struct dom_sid *sid)
+{
+ int ret = 0;
+
+ if (json_is_invalid(object)) {
+ DBG_ERR("Unable to add SID [%s], "
+ "target object is invalid\n",
+ name);
+ return JSON_ERROR;
+ }
+
+ if (sid == NULL) {
+ ret = json_object_set_new(object->root, name, json_null());
+ if (ret != 0) {
+ DBG_ERR("Unable to add null SID [%s]\n", name);
+ return ret;
+ }
+ } else {
+ struct dom_sid_buf sid_buf;
+
+ ret = json_add_string(
+ object, name, dom_sid_str_buf(sid, &sid_buf));
+ if (ret != 0) {
+ DBG_ERR("Unable to add SID [%s] value [%s]\n",
+ name,
+ sid_buf.buf);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+/*
+ * @brief Add a formatted string representation of a guid to a json object.
+ *
+ * Add the string representation of a Samba GUID to the object.
+ *
+ * "guid":"1fb9f2ee-2a4d-4bf8-af8b-cb9d4529a9ab"
+ *
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param guid the guid.
+ *
+ * @return 0 the operation was successful
+ * -1 the operation failed
+ *
+ *
+ */
+int json_add_guid(struct json_object *object,
+ const char *name,
+ const struct GUID *guid)
+{
+
+ int ret = 0;
+
+ if (json_is_invalid(object)) {
+ DBG_ERR("Unable to add GUID [%s], "
+ "target object is invalid\n",
+ name);
+ return JSON_ERROR;
+ }
+
+ if (guid == NULL) {
+ ret = json_object_set_new(object->root, name, json_null());
+ if (ret != 0) {
+ DBG_ERR("Unable to add null GUID [%s]\n", name);
+ return ret;
+ }
+ } else {
+ char *guid_str;
+ struct GUID_txt_buf guid_buff;
+
+ guid_str = GUID_buf_string(guid, &guid_buff);
+ ret = json_add_string(object, name, guid_str);
+ if (ret != 0) {
+ DBG_ERR("Unable to guid GUID [%s] value [%s]\n",
+ name,
+ guid_str);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+/*
+ * @brief Replaces the object for a given key with a given json object.
+ *
+ * If key already exists, the value will be replaced. Otherwise the given
+ * value will be added under the given key.
+ *
+ * @param object the JSON object to be updated.
+ * @param key the key which will be updated.
+ * @param new_obj the new value object to be inserted.
+ *
+ * @return 0 the operation was successful
+ * -1 the operation failed (e.j. if one of the paramters is invalid)
+ */
+int json_update_object(struct json_object *object,
+ const char *key,
+ struct json_object *new_obj)
+{
+ int ret = 0;
+
+ if (json_is_invalid(object)) {
+ DBG_ERR("Unable to update key [%s], "
+ "target object is invalid\n",
+ key);
+ return JSON_ERROR;
+ }
+ if (json_is_invalid(new_obj)) {
+ DBG_ERR("Unable to update key [%s], "
+ "new object is invalid\n",
+ key);
+ return JSON_ERROR;
+ }
+
+ if (key == NULL) {
+ DBG_ERR("Unable to add null String as key\n");
+ return JSON_ERROR;
+ }
+
+ ret = json_object_set(object->root, key, new_obj->root);
+ if (ret != 0) {
+ DBG_ERR("Unable to update object\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * @brief Convert a JSON object into a string
+ *
+ * Convert the json object into a string suitable for printing on a log line,
+ * i.e. with no embedded line breaks.
+ *
+ * If the object is invalid it logs an error and returns NULL.
+ *
+ * @param mem_ctx the talloc memory context owning the returned string
+ * @param object the json object.
+ *
+ * @return A string representation of the object or NULL if the object
+ * is invalid.
+ */
+char *json_to_string(TALLOC_CTX *mem_ctx, const struct json_object *object)
+{
+ char *json = NULL;
+ char *json_string = NULL;
+
+ if (json_is_invalid(object)) {
+ DBG_ERR("Invalid JSON object, unable to convert to string\n");
+ return NULL;
+ }
+
+ if (object->root == NULL) {
+ return NULL;
+ }
+
+ /*
+ * json_dumps uses malloc, so need to call free(json) to release
+ * the memory
+ */
+ json = json_dumps(object->root, 0);
+ if (json == NULL) {
+ DBG_ERR("Unable to convert JSON object to string\n");
+ return NULL;
+ }
+
+ json_string = talloc_strdup(mem_ctx, json);
+ if (json_string == NULL) {
+ free(json);
+ DBG_ERR("Unable to copy JSON object string to talloc string\n");
+ return NULL;
+ }
+ free(json);
+
+ return json_string;
+}
+
+/*
+ * @brief get a json array named "name" from the json object.
+ *
+ * Get the array attribute named name, creating it if it does not exist.
+ *
+ * @param object the json object.
+ * @param name the name of the array attribute
+ *
+ * @return The array object, will be created if it did not exist.
+ */
+struct json_object json_get_array(struct json_object *object, const char *name)
+{
+
+ struct json_object array = json_empty_object;
+ json_t *a = NULL;
+ int ret = 0;
+
+ if (json_is_invalid(object)) {
+ DBG_ERR("Invalid JSON object, unable to get array [%s]\n",
+ name);
+ json_free(&array);
+ return array;
+ }
+
+ array = json_new_array();
+ if (json_is_invalid(&array)) {
+ DBG_ERR("Unable to create new array for [%s]\n", name);
+ return array;
+ }
+
+ a = json_object_get(object->root, name);
+ if (a == NULL) {
+ return array;
+ }
+
+ ret = json_array_extend(array.root, a);
+ if (ret != 0) {
+ DBG_ERR("Unable to get array [%s]\n", name);
+ json_free(&array);
+ return array;
+ }
+
+ return array;
+}
+
+/*
+ * @brief get a json object named "name" from the json object.
+ *
+ * Get the object attribute named name, creating it if it does not exist.
+ *
+ * @param object the json object.
+ * @param name the name of the object attribute
+ *
+ * @return The object, will be created if it did not exist.
+ */
+struct json_object json_get_object(struct json_object *object, const char *name)
+{
+
+ struct json_object o = json_new_object();
+ json_t *v = NULL;
+ int ret = 0;
+
+ if (json_is_invalid(object)) {
+ DBG_ERR("Invalid JSON object, unable to get object [%s]\n",
+ name);
+ json_free(&o);
+ return o;
+ }
+
+ v = json_object_get(object->root, name);
+ if (v == NULL) {
+ return o;
+ }
+ ret = json_object_update(o.root, v);
+ if (ret != 0) {
+ DBG_ERR("Unable to get object [%s]\n", name);
+ json_free(&o);
+ return o;
+ }
+ return o;
+}
+#endif
diff --git a/lib/audit_logging/audit_logging.h b/lib/audit_logging/audit_logging.h
new file mode 100644
index 0000000..49576ec
--- /dev/null
+++ b/lib/audit_logging/audit_logging.h
@@ -0,0 +1,101 @@
+/*
+ common routines for audit logging
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef _AUDIT_LOGGING_H_
+#define _AUDIT_LOGGING_H_
+#include <talloc.h>
+#include "lib/messaging/irpc.h"
+#include "lib/tsocket/tsocket.h"
+#include "lib/util/attr.h"
+
+_WARN_UNUSED_RESULT_ char *audit_get_timestamp(TALLOC_CTX *frame);
+void audit_log_human_text(const char *prefix,
+ const char *message,
+ int debug_class,
+ int debug_level);
+
+#ifdef HAVE_JANSSON
+#include <jansson.h>
+/*
+ * Wrapper for jannson JSON object
+ *
+ */
+struct json_object {
+ json_t *root;
+ bool valid;
+};
+extern const struct json_object json_empty_object;
+
+#define JSON_ERROR -1
+
+void audit_log_json(struct json_object *message,
+ int debug_class,
+ int debug_level);
+void audit_message_send(struct imessaging_context *msg_ctx,
+ const char *server_name,
+ uint32_t message_type,
+ struct json_object *message);
+_WARN_UNUSED_RESULT_ struct json_object json_new_object(void);
+_WARN_UNUSED_RESULT_ struct json_object json_new_array(void);
+void json_free(struct json_object *object);
+void json_assert_is_array(struct json_object *array);
+_WARN_UNUSED_RESULT_ bool json_is_invalid(const struct json_object *object);
+
+_WARN_UNUSED_RESULT_ int json_add_int(struct json_object *object,
+ const char *name,
+ const int value);
+_WARN_UNUSED_RESULT_ int json_add_bool(struct json_object *object,
+ const char *name,
+ const bool value);
+_WARN_UNUSED_RESULT_ int json_add_string(struct json_object *object,
+ const char *name,
+ const char *value);
+_WARN_UNUSED_RESULT_ int json_add_object(struct json_object *object,
+ const char *name,
+ struct json_object *value);
+_WARN_UNUSED_RESULT_ int json_add_stringn(struct json_object *object,
+ const char *name,
+ const char *value,
+ const size_t len);
+_WARN_UNUSED_RESULT_ int json_add_version(struct json_object *object,
+ int major,
+ int minor);
+_WARN_UNUSED_RESULT_ int json_add_timestamp(struct json_object *object);
+_WARN_UNUSED_RESULT_ int json_add_address(
+ struct json_object *object,
+ const char *name,
+ const struct tsocket_address *address);
+_WARN_UNUSED_RESULT_ int json_add_sid(struct json_object *object,
+ const char *name,
+ const struct dom_sid *sid);
+_WARN_UNUSED_RESULT_ int json_add_guid(struct json_object *object,
+ const char *name,
+ const struct GUID *guid);
+
+_WARN_UNUSED_RESULT_ int json_update_object(struct json_object *object,
+ const char *key,
+ struct json_object *new_obj);
+
+_WARN_UNUSED_RESULT_ struct json_object json_get_array(
+ struct json_object *object, const char *name);
+_WARN_UNUSED_RESULT_ struct json_object json_get_object(
+ struct json_object *object, const char *name);
+_WARN_UNUSED_RESULT_ char *json_to_string(TALLOC_CTX *mem_ctx,
+ const struct json_object *object);
+#endif
+#endif
diff --git a/lib/audit_logging/tests/audit_logging_error_test.c b/lib/audit_logging/tests/audit_logging_error_test.c
new file mode 100644
index 0000000..bbcc074
--- /dev/null
+++ b/lib/audit_logging/tests/audit_logging_error_test.c
@@ -0,0 +1,869 @@
+/*
+ * Unit tests for the audit_logging library.
+ *
+ * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * from cmocka.c:
+ * These headers or their equivalents should be included prior to
+ * including
+ * this header file.
+ *
+ * #include <stdarg.h>
+ * #include <stddef.h>
+ * #include <setjmp.h>
+ *
+ * This allows test applications to use custom definitions of C standard
+ * library functions and types.
+ *
+ */
+
+/*
+ * Unit tests for lib/audit_logging/audit_logging.c
+ *
+ * These tests exercise the error handling code and mock the jannson functions
+ * to trigger errors.
+ *
+ */
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "includes.h"
+
+#include "librpc/ndr/libndr.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/messaging/messaging.h"
+#include "auth/common_auth.h"
+
+#include "lib/audit_logging/audit_logging.h"
+
+const int JANSSON_FAILURE = -1;
+const int CALL_ORIG = -2;
+
+/*
+ * cmocka wrappers for json_object
+ */
+json_t *__wrap_json_object(void);
+json_t *__real_json_object(void);
+json_t *__wrap_json_object(void)
+{
+
+ bool fail = (bool)mock();
+ if (fail) {
+ return NULL;
+ }
+ return __real_json_object();
+}
+
+/*
+ * cmocka wrappers for json_array
+ */
+json_t *__wrap_json_array(void);
+json_t *__real_json_array(void);
+json_t *__wrap_json_array(void)
+{
+
+ bool fail = (bool)mock();
+ if (fail) {
+ return NULL;
+ }
+ return __real_json_array();
+}
+
+/*
+ * cmoka wrappers for json_integer
+ */
+json_t *__wrap_json_integer(json_int_t value);
+json_t *__real_json_integer(json_int_t value);
+json_t *__wrap_json_integer(json_int_t value)
+{
+
+ bool fail = (bool)mock();
+ if (fail) {
+ return NULL;
+ }
+ return __real_json_integer(value);
+}
+
+/*
+ * cmocka wrappers for json_string
+ */
+json_t *__wrap_json_string(const char *value);
+json_t *__real_json_string(const char *value);
+json_t *__wrap_json_string(const char *value)
+{
+
+ bool fail = (bool)mock();
+ if (fail) {
+ return NULL;
+ }
+ return __real_json_string(value);
+}
+
+/*
+ * cmocka wrappers for json_dumps
+ */
+char *__wrap_json_dumps(const json_t *json, size_t flags);
+char *__real_json_dumps(const json_t *json, size_t flags);
+char *__wrap_json_dumps(const json_t *json, size_t flags)
+{
+
+ bool fail = (bool)mock();
+ if (fail) {
+ return NULL;
+ }
+ return __real_json_dumps(json, flags);
+}
+
+/*
+ * cmocka wrappers for json_object_set_new
+ */
+int __wrap_json_object_set_new(json_t *object, const char *key, json_t *value);
+int __real_json_object_set_new(json_t *object, const char *key, json_t *value);
+int __wrap_json_object_set_new(json_t *object, const char *key, json_t *value)
+{
+ int rc = (int)mock();
+ if (rc != CALL_ORIG) {
+ return rc;
+ }
+ return __real_json_object_set_new(object, key, value);
+}
+
+/*
+ * cmocka wrappers for json_array_append_new
+ */
+int __wrap_json_array_append_new(json_t *object,
+ const char *key,
+ json_t *value);
+int __real_json_array_append_new(json_t *object,
+ const char *key,
+ json_t *value);
+int __wrap_json_array_append_new(json_t *object, const char *key, json_t *value)
+{
+ int rc = (int)mock();
+ if (rc != CALL_ORIG) {
+ return rc;
+ }
+ return __real_json_array_append_new(object, key, value);
+}
+
+/*
+ * cmocka wrappers for json_array_extend
+ */
+int __wrap_json_array_extend(json_t *array, json_t *other_array);
+int __real_json_array_extend(json_t *array, json_t *other_array);
+int __wrap_json_array_extend(json_t *array, json_t *other_array)
+{
+
+ int rc = (int)mock();
+ if (rc != CALL_ORIG) {
+ return rc;
+ }
+ return __real_json_array_extend(array, other_array);
+}
+
+/*
+ * cmocka wrappers for json_object_update
+ */
+int __wrap_json_object_update(json_t *object, json_t *other_object);
+int __real_json_object_update(json_t *object, json_t *other_object);
+int __wrap_json_object_update(json_t *object, json_t *other_object)
+{
+
+ int rc = (int)mock();
+ if (rc != CALL_ORIG) {
+ return rc;
+ }
+ return __real_json_array_extend(object, other_object);
+}
+
+/*
+ * cmocka wrappers for gettimeofday
+ */
+int __wrap_gettimeofday(struct timeval *tv, struct timezone *tz);
+int __real_gettimeofday(struct timeval *tv, struct timezone *tz);
+int __wrap_gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+
+ int rc = (int)mock();
+ if (rc != 0) {
+ return rc;
+ }
+ return __real_gettimeofday(tv, tz);
+}
+
+/*
+ * cmocka wrappers for localtime
+ */
+struct tm *__wrap_localtime(const time_t *timep);
+struct tm *__real_localtime(const time_t *timep);
+struct tm *__wrap_localtime(const time_t *timep)
+{
+ bool fail = (bool)mock();
+ if (fail) {
+ return NULL;
+ }
+ return __real_localtime(timep);
+}
+
+/*
+ * cmocka wrappers for talloc_named_const
+ */
+static const void *REAL_TALLOC = "Here";
+
+void *__wrap_talloc_named_const(const void *context,
+ size_t size,
+ const char *name);
+void *__real_talloc_named_const(const void *context,
+ size_t size,
+ const char *name);
+void *__wrap_talloc_named_const(const void *context,
+ size_t size,
+ const char *name)
+{
+
+ void *ret = (void *)mock();
+
+ if (ret == NULL) {
+ return NULL;
+ }
+ return __real_talloc_named_const(context, size, name);
+}
+
+/*
+ * cmocka wrappers for talloc_strdup
+ */
+char *__wrap_talloc_strdup(const void *t, const char *p);
+char *__real_talloc_strdup(const void *t, const char *p);
+char *__wrap_talloc_strdup(const void *t, const char *p)
+{
+
+ void *ret = (void *)mock();
+
+ if (ret == NULL) {
+ return NULL;
+ }
+ return __real_talloc_strdup(t, p);
+}
+
+char *__wrap_tsocket_address_string(const struct tsocket_address *addr,
+ TALLOC_CTX *mem_ctx);
+char *__real_tsocket_address_string(const struct tsocket_address *addr,
+ TALLOC_CTX *mem_ctx);
+char *__wrap_tsocket_address_string(const struct tsocket_address *addr,
+ TALLOC_CTX *mem_ctx)
+{
+
+ bool fail = (bool)mock();
+ if (fail) {
+ return NULL;
+ }
+ return __real_tsocket_address_string(addr, mem_ctx);
+}
+
+static void test_json_add_int(_UNUSED_ void **state)
+{
+ struct json_object object;
+ int rc = 0;
+
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+
+ /*
+ * Test json integer failure
+ */
+ will_return(__wrap_json_integer, true);
+ rc = json_add_int(&object, "name", 2);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Test json object set new failure
+ */
+ will_return(__wrap_json_integer, false);
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_int(&object, "name", 2);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+ json_free(&object);
+}
+
+static void test_json_add_bool(_UNUSED_ void **state)
+{
+ struct json_object object;
+ int rc = 0;
+
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+
+ /*
+ * json_boolean does not return an error code.
+ * Test json object set new failure
+ */
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_bool(&object, "name", true);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+}
+
+static void test_json_add_string(_UNUSED_ void **state)
+{
+ struct json_object object;
+ int rc = 0;
+
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+ assert_false(json_is_invalid(&object));
+
+ /*
+ * Test json string failure
+ */
+ will_return(__wrap_json_string, true);
+ rc = json_add_string(&object, "name", "value");
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Test json object set new failure
+ */
+ will_return(__wrap_json_string, false);
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_string(&object, "name", "value");
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Test json object set new failure for a NULL string
+ */
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_string(&object, "null", NULL);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+}
+
+static void test_json_add_object(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_object value;
+ int rc = 0;
+
+ will_return(__wrap_json_object, false);
+ will_return(__wrap_json_object, false);
+
+ object = json_new_object();
+ value = json_new_object();
+
+ /*
+ * Test json object set new failure
+ */
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_object(&object, "name", &value);
+
+ assert_false(json_is_invalid(&object));
+ assert_false(json_is_invalid(&value));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Test json object set new failure for a NULL value
+ */
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_object(&object, "null", NULL);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+ json_free(&value);
+}
+
+static void test_json_add_to_array(_UNUSED_ void **state)
+{
+ struct json_object array;
+ struct json_object value;
+ int rc = 0;
+
+ will_return(__wrap_json_array, false);
+ will_return(__wrap_json_object, false);
+
+ array = json_new_array();
+ value = json_new_object();
+
+ /*
+ * Test json array append new failure
+ */
+ will_return(__wrap_json_array_append_new, JANSSON_FAILURE);
+ rc = json_add_object(&array, "name", &value);
+
+ assert_false(json_is_invalid(&array));
+ assert_false(json_is_invalid(&value));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Test json append new failure with a NULL value
+ */
+ will_return(__wrap_json_array_append_new, JANSSON_FAILURE);
+ rc = json_add_object(&array, "null", NULL);
+
+ assert_false(json_is_invalid(&array));
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&array);
+ json_free(&value);
+}
+
+static void test_json_add_timestamp(_UNUSED_ void **state)
+{
+ struct json_object object;
+ int rc = 0;
+
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+
+ /*
+ * Test json string failure
+ */
+ will_return(__wrap_gettimeofday, 0);
+ will_return(__wrap_localtime, false);
+ will_return(__wrap_json_string, true);
+ rc = json_add_timestamp(&object);
+
+ /*
+ * Test json_object_set_new failure
+ */
+ will_return(__wrap_gettimeofday, 0);
+ will_return(__wrap_localtime, false);
+ will_return(__wrap_json_string, false);
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_timestamp(&object);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Test gettimeofday failure
+ */
+ will_return(__wrap_gettimeofday, -1);
+ rc = json_add_timestamp(&object);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Test local time failure
+ */
+ will_return(__wrap_gettimeofday, 0);
+ will_return(__wrap_localtime, true);
+ rc = json_add_timestamp(&object);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+}
+
+static void test_json_add_stringn(_UNUSED_ void **state)
+{
+ struct json_object object;
+ int rc = 0;
+
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+ assert_false(json_is_invalid(&object));
+
+ /*
+ * Test json string failure
+ */
+ will_return(__wrap_json_string, true);
+ rc = json_add_stringn(&object, "name", "value", 3);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Test json object set new failure
+ */
+ will_return(__wrap_json_string, false);
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_stringn(&object, "name", "value", 3);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Test json object set new failure for a NULL string
+ */
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_stringn(&object, "null", NULL, 2);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Test json object set new failure for a zero string size
+ */
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_stringn(&object, "zero", "no value", 0);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+ json_free(&object);
+}
+
+static void test_json_add_version(_UNUSED_ void **state)
+{
+ struct json_object object;
+ int rc = 0;
+
+ /*
+ * Fail creating the version object
+ */
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+
+ will_return(__wrap_json_object, true);
+ rc = json_add_version(&object, 1, 11);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ /*
+ * Fail adding the major version
+ */
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+
+ will_return(__wrap_json_object, false);
+ will_return(__wrap_json_integer, false);
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_version(&object, 2, 12);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ /*
+ * Fail adding the minor version
+ */
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+
+ will_return(__wrap_json_object, false);
+ will_return(__wrap_json_integer, false);
+ will_return(__wrap_json_object_set_new, CALL_ORIG);
+ will_return(__wrap_json_integer, false);
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_version(&object, 3, 13);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ /*
+ * Fail adding the version object
+ */
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+
+ will_return(__wrap_json_object, false);
+ will_return(__wrap_json_integer, false);
+ will_return(__wrap_json_object_set_new, CALL_ORIG);
+ will_return(__wrap_json_integer, false);
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_version(&object, 4, 14);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+}
+
+static void test_json_add_address(_UNUSED_ void **state)
+{
+ struct json_object object;
+ int rc = 0;
+ struct tsocket_address *ip = NULL;
+
+ TALLOC_CTX *ctx = NULL;
+
+ will_return(__wrap_talloc_named_const, REAL_TALLOC);
+ ctx = talloc_new(NULL);
+
+ /*
+ * Add a null address
+ */
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_address(&object, "name", NULL);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Add a non null address, json_object_set_new failure
+ */
+ rc = tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 21, &ip);
+ assert_int_equal(0, rc);
+
+ will_return(__wrap_talloc_named_const, REAL_TALLOC);
+ will_return(__wrap_tsocket_address_string, false);
+ will_return(__wrap_json_string, false);
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_address(&object, "name", ip);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Add a non null address, with a talloc failure
+ */
+ rc = tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 21, &ip);
+ assert_int_equal(0, rc);
+
+ will_return(__wrap_talloc_named_const, NULL);
+ rc = json_add_address(&object, "name", ip);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Add a non null address, tsocket_address_string failure
+ */
+ rc = tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 21, &ip);
+ assert_int_equal(0, rc);
+
+ will_return(__wrap_talloc_named_const, REAL_TALLOC);
+ will_return(__wrap_tsocket_address_string, true);
+ rc = json_add_address(&object, "name", ip);
+
+ assert_false(json_is_invalid(&object));
+ assert_int_equal(JSON_ERROR, rc);
+
+ TALLOC_FREE(ctx);
+ json_free(&object);
+}
+
+static void test_json_add_sid(void **state)
+{
+ struct json_object object;
+ const char *SID = "S-1-5-21-2470180966-3899876309-2637894779";
+ struct dom_sid sid;
+ int rc;
+
+ /*
+ * Add a null SID
+ */
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_sid(&object, "null", NULL);
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Add a non null SID
+ */
+ assert_true(string_to_sid(&sid, SID));
+ will_return(__wrap_json_string, false);
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_sid(&object, "sid", &sid);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+}
+
+static void test_json_add_guid(void **state)
+{
+ struct json_object object;
+ const char *GUID = "3ab88633-1e57-4c1a-856c-d1bc4b15bbb1";
+ struct GUID guid;
+ NTSTATUS status;
+ int rc;
+
+ /*
+ * Add a null GUID
+ */
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_guid(&object, "null", NULL);
+ assert_int_equal(JSON_ERROR, rc);
+
+ /*
+ * Add a non null GUID
+ */
+ status = GUID_from_string(GUID, &guid);
+ assert_true(NT_STATUS_IS_OK(status));
+ will_return(__wrap_json_string, false);
+ will_return(__wrap_json_object_set_new, JANSSON_FAILURE);
+ rc = json_add_guid(&object, "guid", &guid);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+}
+
+static void test_json_to_string(_UNUSED_ void **state)
+{
+ struct json_object object;
+ char *s = NULL;
+ TALLOC_CTX *ctx = NULL;
+
+ will_return(__wrap_talloc_named_const, REAL_TALLOC);
+ ctx = talloc_new(NULL);
+
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+
+ /*
+ * json_dumps failure
+ */
+ will_return(__wrap_json_dumps, true);
+ s = json_to_string(ctx, &object);
+ assert_null(s);
+
+ /*
+ * talloc failure
+ */
+ will_return(__wrap_json_dumps, false);
+ will_return(__wrap_talloc_strdup, NULL);
+ s = json_to_string(ctx, &object);
+ assert_null(s);
+ TALLOC_FREE(ctx);
+ json_free(&object);
+}
+
+static void test_json_get_array(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_object stored_array;
+ struct json_object array;
+
+ int rc;
+
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+ assert_false(json_is_invalid(&object));
+
+ will_return(__wrap_json_array, false);
+ stored_array = json_new_array();
+ assert_false(json_is_invalid(&stored_array));
+
+ will_return(__wrap_json_object_set_new, CALL_ORIG);
+ rc = json_add_object(&object, "array", &stored_array);
+ assert_int_equal(0, rc);
+
+ /*
+ * json array failure
+ */
+ will_return(__wrap_json_array, true);
+ array = json_get_array(&object, "array");
+ assert_true(json_is_invalid(&array));
+
+ /*
+ * json array extend failure
+ */
+ will_return(__wrap_json_array, false);
+ will_return(__wrap_json_array_extend, true);
+ array = json_get_array(&object, "array");
+ assert_true(json_is_invalid(&array));
+
+ json_free(&stored_array);
+ json_free(&object);
+}
+
+static void test_json_get_object(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_object stored;
+ struct json_object retrieved;
+
+ int rc;
+
+ will_return(__wrap_json_object, false);
+ object = json_new_object();
+ assert_false(json_is_invalid(&object));
+
+ will_return(__wrap_json_object, false);
+ stored = json_new_object();
+ assert_false(json_is_invalid(&stored));
+
+ will_return(__wrap_json_object_set_new, CALL_ORIG);
+ rc = json_add_object(&object, "stored", &stored);
+ assert_int_equal(0, rc);
+
+ /*
+ * json object update failure
+ */
+ will_return(__wrap_json_object, false);
+ will_return(__wrap_json_object_update, true);
+ retrieved = json_get_object(&object, "stored");
+ assert_true(json_is_invalid(&retrieved));
+
+ json_free(&object);
+}
+
+int main(_UNUSED_ int argc, _UNUSED_ const char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_json_add_int),
+ cmocka_unit_test(test_json_add_bool),
+ cmocka_unit_test(test_json_add_string),
+ cmocka_unit_test(test_json_add_object),
+ cmocka_unit_test(test_json_add_to_array),
+ cmocka_unit_test(test_json_add_timestamp),
+ cmocka_unit_test(test_json_add_stringn),
+ cmocka_unit_test(test_json_add_version),
+ cmocka_unit_test(test_json_add_address),
+ cmocka_unit_test(test_json_add_sid),
+ cmocka_unit_test(test_json_add_guid),
+ cmocka_unit_test(test_json_to_string),
+ cmocka_unit_test(test_json_get_array),
+ cmocka_unit_test(test_json_get_object),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/lib/audit_logging/tests/audit_logging_test.c b/lib/audit_logging/tests/audit_logging_test.c
new file mode 100644
index 0000000..1f871c2
--- /dev/null
+++ b/lib/audit_logging/tests/audit_logging_test.c
@@ -0,0 +1,885 @@
+/*
+ * Unit tests for the audit_logging library.
+ *
+ * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * from cmocka.c:
+ * These headers or their equivalents should be included prior to
+ * including
+ * this header file.
+ *
+ * #include <stdarg.h>
+ * #include <stddef.h>
+ * #include <setjmp.h>
+ *
+ * This allows test applications to use custom definitions of C standard
+ * library functions and types.
+ *
+ */
+
+/*
+ * Note that the messaging routines (audit_message_send and get_event_server)
+ * are not tested by these unit tests. Currently they are for integration
+ * test support, and as such are exercised by the integration tests.
+ */
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <string.h>
+#include <time.h>
+#include <tevent.h>
+#include <config.h>
+#include <talloc.h>
+#include "lib/util/talloc_stack.h"
+
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "libcli/util/werror.h"
+#include "lib/param/loadparm.h"
+#include "libcli/security/dom_sid.h"
+#include "librpc/ndr/libndr.h"
+
+#include "lib/audit_logging/audit_logging.h"
+
+static void test_json_add_int(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ double n;
+ int rc = 0;
+
+ object = json_new_object();
+ rc = json_add_int(&object, "positive_one", 1);
+ assert_int_equal(0, rc);
+ rc = json_add_int(&object, "zero", 0);
+ assert_int_equal(0, rc);
+ rc = json_add_int(&object, "negative_one", -1);
+ assert_int_equal(0, rc);
+
+ assert_int_equal(3, json_object_size(object.root));
+
+ value = json_object_get(object.root, "positive_one");
+ assert_true(json_is_integer(value));
+ n = json_number_value(value);
+ assert_true(n == 1.0);
+
+ value = json_object_get(object.root, "zero");
+ assert_true(json_is_integer(value));
+ n = json_number_value(value);
+ assert_true(n == 0.0);
+
+ value = json_object_get(object.root, "negative_one");
+ assert_true(json_is_integer(value));
+ n = json_number_value(value);
+ assert_true(n == -1.0);
+
+ object.valid = false;
+ rc = json_add_int(&object, "should fail 1", 0xf1);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ rc = json_add_int(&object, "should fail 2", 0xf2);
+ assert_int_equal(JSON_ERROR, rc);
+}
+
+static void test_json_add_bool(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ int rc = 0;
+
+ object = json_new_object();
+ rc = json_add_bool(&object, "true", true);
+ assert_int_equal(0, rc);
+ rc = json_add_bool(&object, "false", false);
+ assert_int_equal(0, rc);
+
+ assert_int_equal(2, json_object_size(object.root));
+
+ value = json_object_get(object.root, "true");
+ assert_true(json_is_boolean(value));
+ assert_true(value == json_true());
+
+ value = json_object_get(object.root, "false");
+ assert_true(json_is_boolean(value));
+ assert_true(value == json_false());
+
+ object.valid = false;
+ rc = json_add_bool(&object, "should fail 1", true);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ rc = json_add_bool(&object, "should fail 2", false);
+ assert_int_equal(JSON_ERROR, rc);
+}
+
+static void test_json_add_string(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ const char *s = NULL;
+ int rc = 0;
+
+ object = json_new_object();
+ rc = json_add_string(&object, "null", NULL);
+ assert_int_equal(0, rc);
+ rc = json_add_string(&object, "empty", "");
+ assert_int_equal(0, rc);
+ rc = json_add_string(&object, "name", "value");
+ assert_int_equal(0, rc);
+
+ assert_int_equal(3, json_object_size(object.root));
+
+ value = json_object_get(object.root, "null");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "empty");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("", s);
+
+ value = json_object_get(object.root, "name");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("value", s);
+
+ object.valid = false;
+ rc = json_add_string(&object, "should fail 1", "A value");
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ rc = json_add_string(&object, "should fail 2", "Another value");
+ assert_int_equal(JSON_ERROR, rc);
+}
+
+static void test_json_add_object(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_object other;
+ struct json_object after;
+ struct json_object invalid = json_empty_object;
+ struct json_t *value = NULL;
+ int rc = 0;
+
+ object = json_new_object();
+ other = json_new_object();
+ rc = json_add_object(&object, "null", NULL);
+ assert_int_equal(0, rc);
+ rc = json_add_object(&object, "other", &other);
+ assert_int_equal(0, rc);
+
+ assert_int_equal(2, json_object_size(object.root));
+
+ value = json_object_get(object.root, "null");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "other");
+ assert_true(json_is_object(value));
+ assert_ptr_equal(other.root, value);
+
+ rc = json_add_object(&object, "invalid", &invalid);
+ assert_int_equal(JSON_ERROR, rc);
+
+ object.valid = false;
+ after = json_new_object();
+ rc = json_add_object(&object, "after", &after);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ rc = json_add_object(&object, "after", &after);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&after);
+}
+
+static void test_json_add_to_array(_UNUSED_ void **state)
+{
+ struct json_object array;
+ struct json_object o1;
+ struct json_object o2;
+ struct json_object o3;
+ struct json_object after;
+ struct json_object invalid = json_empty_object;
+ struct json_t *value = NULL;
+ int rc = 0;
+
+ array = json_new_array();
+ assert_true(json_is_array(array.root));
+
+ o1 = json_new_object();
+ o2 = json_new_object();
+ o3 = json_new_object();
+
+ rc = json_add_object(&array, NULL, &o3);
+ assert_int_equal(0, rc);
+ rc = json_add_object(&array, "", &o2);
+ assert_int_equal(0, rc);
+ rc = json_add_object(&array, "will-be-ignored", &o1);
+ assert_int_equal(0, rc);
+ rc = json_add_object(&array, NULL, NULL);
+ assert_int_equal(0, rc);
+
+ assert_int_equal(4, json_array_size(array.root));
+
+ value = json_array_get(array.root, 0);
+ assert_ptr_equal(o3.root, value);
+
+ value = json_array_get(array.root, 1);
+ assert_ptr_equal(o2.root, value);
+
+ value = json_array_get(array.root, 2);
+ assert_ptr_equal(o1.root, value);
+
+ value = json_array_get(array.root, 3);
+ assert_true(json_is_null(value));
+
+ rc = json_add_object(&array, "invalid", &invalid);
+ assert_int_equal(JSON_ERROR, rc);
+
+ array.valid = false;
+ after = json_new_object();
+ rc = json_add_object(&array, "after", &after);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&array);
+
+ rc = json_add_object(&array, "after", &after);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&after);
+}
+
+static void test_json_add_timestamp(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_t *ts = NULL;
+ const char *t = NULL;
+ int rc;
+ int usec, tz;
+ char c[2];
+ struct tm tm;
+ time_t before;
+ time_t after;
+ time_t actual;
+ struct timeval tv;
+ int ret;
+
+ object = json_new_object();
+
+ ret = gettimeofday(&tv, NULL);
+ assert_int_equal(0, ret);
+ before = tv.tv_sec;
+
+ rc = json_add_timestamp(&object);
+ assert_int_equal(0, rc);
+
+ ret = gettimeofday(&tv, NULL);
+ assert_int_equal(0, ret);
+ after = tv.tv_sec;
+
+ ts = json_object_get(object.root, "timestamp");
+ assert_true(json_is_string(ts));
+
+ /*
+ * Convert the returned ISO 8601 timestamp into a time_t
+ * Note for convenience we ignore the value of the microsecond
+ * part of the time stamp.
+ */
+ t = json_string_value(ts);
+ rc = sscanf(
+ t,
+ "%4d-%2d-%2dT%2d:%2d:%2d.%6d%1c%4d",
+ &tm.tm_year,
+ &tm.tm_mon,
+ &tm.tm_mday,
+ &tm.tm_hour,
+ &tm.tm_min,
+ &tm.tm_sec,
+ &usec,
+ c,
+ &tz);
+ assert_int_equal(9, rc);
+ tm.tm_year = tm.tm_year - 1900;
+ tm.tm_mon = tm.tm_mon - 1;
+ tm.tm_isdst = -1;
+ actual = mktime(&tm);
+
+ /*
+ * The timestamp should be before <= actual <= after
+ */
+ assert_true(difftime(actual, before) >= 0);
+ assert_true(difftime(after, actual) >= 0);
+
+ object.valid = false;
+ rc = json_add_timestamp(&object);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ rc = json_add_timestamp(&object);
+ assert_int_equal(JSON_ERROR, rc);
+}
+
+static void test_json_add_stringn(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ const char *s = NULL;
+ int rc = 0;
+
+ object = json_new_object();
+ rc = json_add_stringn(&object, "null", NULL, 10);
+ assert_int_equal(0, rc);
+ rc = json_add_stringn(&object, "null-zero-len", NULL, 0);
+ assert_int_equal(0, rc);
+ rc = json_add_stringn(&object, "empty", "", 1);
+ assert_int_equal(0, rc);
+ rc = json_add_stringn(&object, "empty-zero-len", "", 0);
+ assert_int_equal(0, rc);
+ rc = json_add_stringn(&object, "value-less-than-len", "123456", 7);
+ assert_int_equal(0, rc);
+ rc = json_add_stringn(&object, "value-greater-than-len", "abcd", 3);
+ assert_int_equal(0, rc);
+ rc = json_add_stringn(&object, "value-equal-len", "ZYX", 3);
+ assert_int_equal(0, rc);
+ rc = json_add_stringn(
+ &object, "value-len-is-zero", "this will be null", 0);
+ assert_int_equal(0, rc);
+
+ assert_int_equal(8, json_object_size(object.root));
+
+ value = json_object_get(object.root, "null");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "null-zero-len");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "empty");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("", s);
+
+ value = json_object_get(object.root, "empty-zero-len");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "value-greater-than-len");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("abc", s);
+ assert_int_equal(3, strlen(s));
+
+ value = json_object_get(object.root, "value-equal-len");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("ZYX", s);
+ assert_int_equal(3, strlen(s));
+
+ value = json_object_get(object.root, "value-len-is-zero");
+ assert_true(json_is_null(value));
+
+ object.valid = false;
+ rc = json_add_stringn(&object, "fail-01", "xxxxxxx", 1);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ rc = json_add_stringn(&object, "fail-02", "xxxxxxx", 1);
+ assert_int_equal(JSON_ERROR, rc);
+}
+
+static void test_json_add_version(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_t *version = NULL;
+ struct json_t *v = NULL;
+ double n;
+ int rc;
+
+ object = json_new_object();
+ rc = json_add_version(&object, 3, 1);
+ assert_int_equal(0, rc);
+
+ assert_int_equal(1, json_object_size(object.root));
+
+ version = json_object_get(object.root, "version");
+ assert_true(json_is_object(version));
+ assert_int_equal(2, json_object_size(version));
+
+ v = json_object_get(version, "major");
+ assert_true(json_is_integer(v));
+ n = json_number_value(v);
+ assert_true(n == 3.0);
+
+ v = json_object_get(version, "minor");
+ assert_true(json_is_integer(v));
+ n = json_number_value(v);
+ assert_true(n == 1.0);
+
+ object.valid = false;
+ rc = json_add_version(&object, 3, 1);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ rc = json_add_version(&object, 3, 1);
+ assert_int_equal(JSON_ERROR, rc);
+}
+
+static void test_json_add_address(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ struct tsocket_address *ip4 = NULL;
+ struct tsocket_address *ip6 = NULL;
+ struct tsocket_address *pipe = NULL;
+
+ struct tsocket_address *after = NULL;
+ const char *s = NULL;
+ int rc;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ object = json_new_object();
+
+ rc = json_add_address(&object, "null", NULL);
+ assert_int_equal(0, rc);
+
+ rc = tsocket_address_inet_from_strings(
+ ctx,
+ "ip",
+ "127.0.0.1",
+ 21,
+ &ip4);
+ assert_int_equal(0, rc);
+ rc = json_add_address(&object, "ip4", ip4);
+ assert_int_equal(0, rc);
+
+ rc = tsocket_address_inet_from_strings(
+ ctx,
+ "ip",
+ "2001:db8:0:0:1:0:0:1",
+ 42,
+ &ip6);
+ assert_int_equal(0, rc);
+ rc = json_add_address(&object, "ip6", ip6);
+ assert_int_equal(0, rc);
+
+ rc = tsocket_address_unix_from_path(ctx, "/samba/pipe", &pipe);
+ assert_int_equal(0, rc);
+ rc = json_add_address(&object, "pipe", pipe);
+ assert_int_equal(0, rc);
+
+ assert_int_equal(4, json_object_size(object.root));
+
+ value = json_object_get(object.root, "null");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "ip4");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("ipv4:127.0.0.1:21", s);
+
+ value = json_object_get(object.root, "ip6");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("ipv6:2001:db8::1:0:0:1:42", s);
+
+ value = json_object_get(object.root, "pipe");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("unix:/samba/pipe", s);
+
+ object.valid = false;
+ rc = tsocket_address_inet_from_strings(
+ ctx, "ip", "127.0.0.11", 23, &after);
+ assert_int_equal(0, rc);
+ rc = json_add_address(&object, "invalid_object", after);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ rc = json_add_address(&object, "freed object", after);
+ assert_int_equal(JSON_ERROR, rc);
+
+ TALLOC_FREE(ctx);
+}
+
+static void test_json_add_sid(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ const char *SID = "S-1-5-21-2470180966-3899876309-2637894779";
+ struct dom_sid sid;
+ const char *s = NULL;
+ int rc;
+
+ object = json_new_object();
+
+ rc = json_add_sid(&object, "null", NULL);
+ assert_int_equal(0, rc);
+
+ assert_true(string_to_sid(&sid, SID));
+ rc = json_add_sid(&object, "sid", &sid);
+ assert_int_equal(0, rc);
+
+ assert_int_equal(2, json_object_size(object.root));
+
+ value = json_object_get(object.root, "null");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "sid");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal(SID, s);
+
+ object.valid = false;
+ rc = json_add_sid(&object, "invalid_object", &sid);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ rc = json_add_sid(&object, "freed_object", &sid);
+ assert_int_equal(JSON_ERROR, rc);
+}
+
+static void test_json_add_guid(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ const char *GUID = "3ab88633-1e57-4c1a-856c-d1bc4b15bbb1";
+ struct GUID guid;
+ const char *s = NULL;
+ NTSTATUS status;
+ int rc;
+
+ object = json_new_object();
+
+ rc = json_add_guid(&object, "null", NULL);
+ assert_int_equal(0, rc);
+
+ status = GUID_from_string(GUID, &guid);
+ assert_true(NT_STATUS_IS_OK(status));
+ rc = json_add_guid(&object, "guid", &guid);
+ assert_int_equal(0, rc);
+
+ assert_int_equal(2, json_object_size(object.root));
+
+ value = json_object_get(object.root, "null");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "guid");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal(GUID, s);
+
+ object.valid = false;
+ rc = json_add_guid(&object, "invalid_object", &guid);
+ assert_int_equal(JSON_ERROR, rc);
+
+ json_free(&object);
+
+ rc = json_add_guid(&object, "freed_object", &guid);
+ assert_int_equal(JSON_ERROR, rc);
+}
+
+static void test_json_to_string(_UNUSED_ void **state)
+{
+ struct json_object object;
+ char *s = NULL;
+ int rc;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ object = json_new_object();
+
+ s = json_to_string(ctx, &object);
+ assert_string_equal("{}", s);
+ TALLOC_FREE(s);
+
+ rc = json_add_string(&object, "name", "value");
+ assert_int_equal(0, rc);
+ s = json_to_string(ctx, &object);
+ assert_string_equal("{\"name\": \"value\"}", s);
+ TALLOC_FREE(s);
+
+ object.valid = false;
+ s = json_to_string(ctx, &object);
+ assert_null(s);
+
+ json_free(&object);
+
+ object.valid = true;
+ object.root = NULL;
+
+ s = json_to_string(ctx, &object);
+ assert_null(s);
+ TALLOC_FREE(ctx);
+}
+
+static void test_json_get_array(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_object array;
+ struct json_object stored_array = json_new_array();
+ json_t *value = NULL;
+ json_t *o = NULL;
+ struct json_object o1;
+ struct json_object o2;
+ int rc;
+
+ object = json_new_object();
+
+ array = json_get_array(&object, "not-there");
+ assert_true(array.valid);
+ assert_non_null(array.root);
+ assert_true(json_is_array(array.root));
+ json_free(&array);
+
+ o1 = json_new_object();
+ rc = json_add_string(&o1, "value", "value-one");
+ assert_int_equal(0, rc);
+ rc = json_add_object(&stored_array, NULL, &o1);
+ assert_int_equal(0, rc);
+ rc = json_add_object(&object, "stored_array", &stored_array);
+ assert_int_equal(0, rc);
+
+ array = json_get_array(&object, "stored_array");
+ assert_true(array.valid);
+ assert_non_null(array.root);
+ assert_true(json_is_array(array.root));
+
+ assert_int_equal(1, json_array_size(array.root));
+
+ o = json_array_get(array.root, 0);
+ assert_non_null(o);
+ assert_true(json_is_object(o));
+
+ value = json_object_get(o, "value");
+ assert_non_null(value);
+ assert_true(json_is_string(value));
+
+ assert_string_equal("value-one", json_string_value(value));
+ json_free(&array);
+
+ /*
+ * Now update the array and add it back to the object
+ */
+ array = json_get_array(&object, "stored_array");
+ assert_true(json_is_array(array.root));
+ o2 = json_new_object();
+ rc = json_add_string(&o2, "value", "value-two");
+ assert_int_equal(0, rc);
+ assert_true(o2.valid);
+ rc = json_add_object(&array, NULL, &o2);
+ assert_int_equal(0, rc);
+ assert_true(json_is_array(array.root));
+ rc = json_add_object(&object, "stored_array", &array);
+ assert_int_equal(0, rc);
+ assert_true(json_is_array(array.root));
+
+ array = json_get_array(&object, "stored_array");
+ assert_non_null(array.root);
+ assert_true(json_is_array(array.root));
+ assert_true(array.valid);
+ assert_true(json_is_array(array.root));
+
+ assert_int_equal(2, json_array_size(array.root));
+
+ o = json_array_get(array.root, 0);
+ assert_non_null(o);
+ assert_true(json_is_object(o));
+
+ assert_non_null(value);
+ assert_true(json_is_string(value));
+
+ assert_string_equal("value-one", json_string_value(value));
+
+ o = json_array_get(array.root, 1);
+ assert_non_null(o);
+ assert_true(json_is_object(o));
+
+ value = json_object_get(o, "value");
+ assert_non_null(value);
+ assert_true(json_is_string(value));
+
+ assert_string_equal("value-two", json_string_value(value));
+
+ json_free(&array);
+ json_free(&object);
+
+ array = json_get_array(&object, "stored_array");
+ assert_false(array.valid);
+ json_free(&array);
+}
+
+static void test_json_get_object(_UNUSED_ void **state)
+{
+ struct json_object object;
+ struct json_object o1;
+ struct json_object o2;
+ struct json_object o3;
+ json_t *value = NULL;
+ int rc;
+
+ object = json_new_object();
+
+ o1 = json_get_object(&object, "not-there");
+ assert_true(o1.valid);
+ assert_non_null(o1.root);
+ assert_true(json_is_object(o1.root));
+ json_free(&o1);
+
+ o1 = json_new_object();
+ rc = json_add_string(&o1, "value", "value-one");
+ assert_int_equal(0, rc);
+ rc = json_add_object(&object, "stored_object", &o1);
+ assert_int_equal(0, rc);
+
+ o2 = json_get_object(&object, "stored_object");
+ assert_true(o2.valid);
+ assert_non_null(o2.root);
+ assert_true(json_is_object(o2.root));
+
+ value = json_object_get(o2.root, "value");
+ assert_non_null(value);
+ assert_true(json_is_string(value));
+
+ assert_string_equal("value-one", json_string_value(value));
+
+ rc = json_add_string(&o2, "value", "value-two");
+ assert_int_equal(0, rc);
+ rc = json_add_object(&object, "stored_object", &o2);
+ assert_int_equal(0, rc);
+
+ o3 = json_get_object(&object, "stored_object");
+ assert_true(o3.valid);
+ assert_non_null(o3.root);
+ assert_true(json_is_object(o3.root));
+
+ value = json_object_get(o3.root, "value");
+ assert_non_null(value);
+ assert_true(json_is_string(value));
+
+ assert_string_equal("value-two", json_string_value(value));
+
+ json_free(&o3);
+ json_free(&object);
+
+ o3 = json_get_object(&object, "stored_object");
+ assert_false(o3.valid);
+ json_free(&o3);
+}
+
+static void test_audit_get_timestamp(_UNUSED_ void **state)
+{
+ const char *t = NULL;
+ char *c;
+ struct tm tm;
+ time_t before;
+ time_t after;
+ time_t actual;
+ struct timeval tv;
+ int ret;
+ char *env_tz = NULL;
+ char *orig_tz = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ /*
+ * Explicitly set the time zone to UTC to make the test easier
+ */
+ env_tz = getenv("TZ");
+ if (env_tz != NULL) {
+ orig_tz = talloc_strdup(ctx, env_tz);
+ }
+ setenv("TZ", "UTC", 1);
+
+ ret = gettimeofday(&tv, NULL);
+ assert_int_equal(0, ret);
+ before = tv.tv_sec;
+
+ t = audit_get_timestamp(ctx);
+
+ ret = gettimeofday(&tv, NULL);
+ assert_int_equal(0, ret);
+ after = tv.tv_sec;
+
+ c = strptime(t, "%a, %d %b %Y %H:%M:%S", &tm);
+
+ /*
+ * Restore the time zone if we changed it
+ */
+ if (orig_tz != NULL) {
+ setenv("TZ", orig_tz, 1);
+ TALLOC_FREE(orig_tz);
+ }
+
+ assert_non_null(c);
+ tm.tm_isdst = -1;
+ if (c != NULL && *c == '.') {
+ char *e;
+ strtod(c, &e);
+ c = e;
+ }
+ if (c != NULL && *c == ' ') {
+ assert_string_equal(" UTC", c);
+ c += 4;
+ }
+ assert_int_equal(0, strlen(c));
+
+ actual = mktime(&tm);
+
+ /*
+ * The timestamp should be before <= actual <= after
+ */
+ assert_true(difftime(actual, before) >= 0);
+ assert_true(difftime(after, actual) >= 0);
+
+ TALLOC_FREE(ctx);
+}
+
+int main(_UNUSED_ int argc, _UNUSED_ const char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_json_add_int),
+ cmocka_unit_test(test_json_add_bool),
+ cmocka_unit_test(test_json_add_string),
+ cmocka_unit_test(test_json_add_object),
+ cmocka_unit_test(test_json_add_to_array),
+ cmocka_unit_test(test_json_add_timestamp),
+ cmocka_unit_test(test_json_add_stringn),
+ cmocka_unit_test(test_json_add_version),
+ cmocka_unit_test(test_json_add_address),
+ cmocka_unit_test(test_json_add_sid),
+ cmocka_unit_test(test_json_add_guid),
+ cmocka_unit_test(test_json_to_string),
+ cmocka_unit_test(test_json_get_array),
+ cmocka_unit_test(test_json_get_object),
+ cmocka_unit_test(test_audit_get_timestamp),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/lib/audit_logging/wscript_build b/lib/audit_logging/wscript_build
new file mode 100644
index 0000000..125c94b
--- /dev/null
+++ b/lib/audit_logging/wscript_build
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM(
+ 'audit_logging',
+ deps='''MESSAGING_SEND
+ jansson
+ samba-debug
+ LIBTSOCKET''',
+ source='audit_logging.c'
+)
+
+if bld.AD_DC_BUILD_IS_ENABLED():
+ bld.SAMBA_BINARY(
+ 'audit_logging_test',
+ source='tests/audit_logging_test.c',
+ deps='''
+ audit_logging
+ jansson
+ cmocka
+ talloc
+ samba-util
+ LIBTSOCKET
+ ''',
+ for_selftest=True
+ )
+
+if bld.AD_DC_BUILD_IS_ENABLED():
+ bld.SAMBA_BINARY(
+ 'audit_logging_error_test',
+ source='tests/audit_logging_error_test.c',
+ deps='''
+ audit_logging
+ jansson
+ cmocka
+ talloc
+ samba-util
+ LIBTSOCKET
+ ''',
+ for_selftest=True,
+ ldflags='''
+ -Wl,--wrap,json_object_set_new
+ -Wl,--wrap,json_object_update
+ -Wl,--wrap,json_array_append_new
+ -Wl,--wrap,json_array_extend
+ -Wl,--wrap,json_object
+ -Wl,--wrap,json_string
+ -Wl,--wrap,json_integer
+ -Wl,--wrap,json_array
+ -Wl,--wrap,json_dumps
+ -Wl,--wrap,gettimeofday
+ -Wl,--wrap,localtime
+ -Wl,--wrap,talloc_named_const
+ -Wl,--wrap,talloc_strdup
+ -Wl,--wrap,tsocket_address_string
+ '''
+ )