summaryrefslogtreecommitdiff
path: root/plugins/audit_json
diff options
context:
space:
mode:
authorTodd C. Miller <Todd.Miller@sudo.ws>2020-02-17 16:10:55 -0700
committerTodd C. Miller <Todd.Miller@sudo.ws>2020-02-17 16:10:55 -0700
commitc535f32f9cc3a0763dc68766e1a1dee51240a6e8 (patch)
tree67da63c5e03fa632f813ba0c163d6b94f2321d26 /plugins/audit_json
parent44bc820d484a029a57a9f4b7624e9c2df3913b99 (diff)
downloadsudo-c535f32f9cc3a0763dc68766e1a1dee51240a6e8.tar.gz
Rework the JSON API to write to a memory buffer, not a stdio stream.
Diffstat (limited to 'plugins/audit_json')
-rw-r--r--plugins/audit_json/audit_json.c220
1 files changed, 123 insertions, 97 deletions
diff --git a/plugins/audit_json/audit_json.c b/plugins/audit_json/audit_json.c
index f58bb92e3..a803a3d02 100644
--- a/plugins/audit_json/audit_json.c
+++ b/plugins/audit_json/audit_json.c
@@ -198,13 +198,13 @@ done:
}
static bool
-print_key_value(struct json_container *json, const char *str)
+add_key_value(struct json_container *json, const char *str)
{
struct json_value json_value;
const char *cp, *errstr;
char name[256];
size_t len;
- debug_decl(print_key_value, SUDO_DEBUG_PLUGIN);
+ debug_decl(add_key_value, SUDO_DEBUG_PLUGIN);
if ((cp = strchr(str, '=')) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
@@ -252,22 +252,18 @@ print_key_value(struct json_container *json, const char *str)
json_value.u.string = cp;
}
- sudo_json_add_value(json, name, &json_value);
-
- debug_return_bool(true);
+ debug_return_bool(sudo_json_add_value(json, name, &json_value));
}
-static void
-print_array(struct json_container *json, const char *name, char * const * array)
+static bool
+add_array(struct json_container *json, const char *name, char * const * array)
{
struct json_value json_value;
- debug_decl(print_array, SUDO_DEBUG_PLUGIN);
+ debug_decl(add_array, SUDO_DEBUG_PLUGIN);
json_value.type = JSON_ARRAY;
json_value.u.array = array;
- sudo_json_add_value(json, name, &json_value);
-
- debug_return;
+ debug_return_bool(sudo_json_add_value(json, name, &json_value));
}
static bool
@@ -287,14 +283,14 @@ filter_key_value(const char *kv, char * const * filter)
return false;
}
-static void
-print_key_value_object(struct json_container *json, const char *name,
+static bool
+add_key_value_object(struct json_container *json, const char *name,
char * const * array, char * const * filter)
{
char * const *cur;
const char *cp;
bool empty = false;
- debug_decl(print_key_value_object, SUDO_DEBUG_PLUGIN);
+ debug_decl(add_key_value_object, SUDO_DEBUG_PLUGIN);
if (filter != NULL) {
/* Avoid printing an empty object if everything is filtered. */
@@ -307,26 +303,31 @@ print_key_value_object(struct json_container *json, const char *name,
}
}
if (!empty) {
- sudo_json_open_object(json, name);
+ if (!sudo_json_open_object(json, name))
+ goto bad;
for (cur = array; (cp = *cur) != NULL; cur++) {
if (filter_key_value(cp, filter))
continue;
- print_key_value(json, cp);
+ if (!add_key_value(json, cp))
+ goto bad;
}
- sudo_json_close_object(json);
+ if (!sudo_json_close_object(json))
+ goto bad;
}
- debug_return;
+ debug_return_bool(true);
+bad:
+ debug_return_bool(false);
}
static bool
-print_timestamp(struct json_container *json, struct timespec *ts)
+add_timestamp(struct json_container *json, struct timespec *ts)
{
struct json_value json_value;
time_t secs = ts->tv_sec;
char timebuf[1024];
struct tm *tm;
- debug_decl(print_timestamp, SUDO_DEBUG_PLUGIN);
+ debug_decl(add_timestamp, SUDO_DEBUG_PLUGIN);
if ((tm = gmtime(&secs)) == NULL)
debug_return_bool(false);
@@ -357,19 +358,11 @@ print_timestamp(struct json_container *json, struct timespec *ts)
}
static int
-audit_write_exit_record(int exit_status, int error)
+audit_write_json(struct json_container *json)
{
- struct json_container json;
- struct json_value json_value;
- struct timespec now;
struct stat sb;
int ret = -1;
- debug_decl(audit_write_exit_record, SUDO_DEBUG_PLUGIN);
-
- if (sudo_gettime_real(&now) == -1) {
- sudo_warn(U_("unable to read the clock"));
- goto done;
- }
+ debug_decl(audit_write_json, SUDO_DEBUG_PLUGIN);
if (!sudo_lock_file(fileno(state.log_fp), SUDO_LOCK)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
@@ -395,29 +388,61 @@ audit_write_exit_record(int exit_status, int error)
goto done;
}
- sudo_json_init(&json, state.log_fp, 4);
- sudo_json_open_object(&json, "exit");
+ fputs(sudo_json_get_buf(json), state.log_fp);
+ fputs("\n}\n", state.log_fp);
+ fflush(state.log_fp);
+ (void)sudo_lock_file(fileno(state.log_fp), SUDO_UNLOCK);
+
+ /* TODO: undo partial record on error */
+ if (!ferror(state.log_fp))
+ ret = true;
+
+done:
+ debug_return_int(ret);
+}
+
+static int
+audit_write_exit_record(int exit_status, int error)
+{
+ struct json_container json;
+ struct json_value json_value;
+ struct timespec now;
+ int ret = -1;
+ debug_decl(audit_write_exit_record, SUDO_DEBUG_PLUGIN);
+
+ if (sudo_gettime_real(&now) == -1) {
+ sudo_warn(U_("unable to read the clock"));
+ goto done;
+ }
+
+ if (!sudo_json_init(&json, 4, false, false))
+ goto oom;
+ if (!sudo_json_open_object(&json, "exit"))
+ goto oom;
/* Write UUID */
json_value.type = JSON_STRING;
json_value.u.string = state.uuid_str;
- sudo_json_add_value(&json, "uuid", &json_value);
+ if (!sudo_json_add_value(&json, "uuid", &json_value))
+ goto oom;
/* Write time stamp */
- if (!print_timestamp(&json, &now))
- sudo_warnx(U_("unable to format timestamp"));
+ if (!add_timestamp(&json, &now))
+ goto oom;
if (error != 0) {
/* Error executing command */
json_value.type = JSON_STRING;
json_value.u.string = strerror(error);
- sudo_json_add_value(&json, "error", &json_value);
+ if (!sudo_json_add_value(&json, "error", &json_value))
+ goto oom;
} else {
if (WIFEXITED(exit_status)) {
/* Command exited normally. */
json_value.type = JSON_NUMBER;
json_value.u.number = WEXITSTATUS(exit_status);
- sudo_json_add_value(&json, "exit_value", &json_value);
+ if (!sudo_json_add_value(&json, "exit_value", &json_value))
+ goto oom;
} else if (WIFSIGNALED(exit_status)) {
/* Command killed by signal. */
char signame[SIG2STR_MAX];
@@ -425,32 +450,38 @@ audit_write_exit_record(int exit_status, int error)
if (signo <= 0 || sig2str(signo, signame) == -1) {
json_value.type = JSON_NUMBER;
json_value.u.number = signo;
- sudo_json_add_value(&json, "signal", &json_value);
+ if (!sudo_json_add_value(&json, "signal", &json_value))
+ goto oom;
} else {
json_value.type = JSON_STRING;
json_value.u.string = signame;
- sudo_json_add_value(&json, "signal", &json_value);
+ if (!sudo_json_add_value(&json, "signal", &json_value))
+ goto oom;
}
/* Core dump? */
json_value.type = JSON_BOOL;
json_value.u.boolean = WCOREDUMP(exit_status);
- sudo_json_add_value(&json, "dumped_core", &json_value);
+ if (!sudo_json_add_value(&json, "dumped_core", &json_value))
+ goto oom;
/* Exit value */
json_value.type = JSON_NUMBER;
json_value.u.number = WTERMSIG(exit_status) | 128;
- sudo_json_add_value(&json, "exit_value", &json_value);
+ if (!sudo_json_add_value(&json, "exit_value", &json_value))
+ goto oom;
}
}
- sudo_json_close_object(&json); /* close record */
- fputs("\n}\n", state.log_fp); /* close JSON */
- fflush(state.log_fp);
-
- (void)sudo_lock_file(fileno(state.log_fp), SUDO_UNLOCK);
+ if (!sudo_json_close_object(&json))
+ goto oom;
- ret = true;
+ ret = audit_write_json(&json);
+ sudo_json_free(&json);
done:
debug_return_int(ret);
+oom:
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ sudo_json_free(&json);
+ debug_return_int(-1);
}
static int
@@ -461,7 +492,6 @@ audit_write_record(const char *audit_str, const char *plugin_name,
struct json_container json;
struct json_value json_value;
struct timespec now;
- struct stat sb;
int ret = -1;
debug_decl(audit_write_record, SUDO_DEBUG_PLUGIN);
@@ -470,36 +500,15 @@ audit_write_record(const char *audit_str, const char *plugin_name,
goto done;
}
- if (!sudo_lock_file(fileno(state.log_fp), SUDO_LOCK)) {
- sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
- "unable to lock %s", state.logfile);
- goto done;
- }
-
- /* Note: assumes file ends in "\n}\n" */
- if (fstat(fileno(state.log_fp), &sb) == -1) {
- sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
- "unable to stat %s", state.logfile);
- goto done;
- }
- if (sb.st_size == 0) {
- /* New file */
- putc('{', state.log_fp);
- } else if (fseeko(state.log_fp, -3, SEEK_END) == 0) {
- /* Continue file, overwrite the final "\n}\n" */
- putc(',', state.log_fp);
- } else {
- sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
- "unable to seek %s", state.logfile);
- goto done;
- }
-
- sudo_json_init(&json, state.log_fp, 4);
- sudo_json_open_object(&json, audit_str);
+ if (!sudo_json_init(&json, 4, false, false))
+ goto oom;
+ if (!sudo_json_open_object(&json, audit_str))
+ goto oom;
json_value.type = JSON_STRING;
json_value.u.string = plugin_name;
- sudo_json_add_value(&json, "plugin_name", &json_value);
+ if (!sudo_json_add_value(&json, "plugin_name", &json_value))
+ goto oom;
switch (plugin_type) {
case 0:
@@ -522,49 +531,66 @@ audit_write_record(const char *audit_str, const char *plugin_name,
break;
}
json_value.type = JSON_STRING;
- sudo_json_add_value(&json, "plugin_type", &json_value);
+ if (!sudo_json_add_value(&json, "plugin_type", &json_value))
+ goto oom;
/* error and reject audit events usually contain a reason. */
if (reason != NULL) {
json_value.type = JSON_STRING;
json_value.u.string = reason;
- sudo_json_add_value(&json, "reason", &json_value);
+ if (!sudo_json_add_value(&json, "reason", &json_value))
+ goto oom;
}
json_value.type = JSON_STRING;
json_value.u.string = state.uuid_str;
- sudo_json_add_value(&json, "uuid", &json_value);
+ if (!sudo_json_add_value(&json, "uuid", &json_value))
+ goto oom;
- if (!print_timestamp(&json, &now))
- sudo_warnx(U_("unable to format timestamp"));
+ if (!add_timestamp(&json, &now))
+ goto oom;
/* Write key=value objects. */
- print_key_value_object(&json, "options", state.settings, settings_filter);
- print_key_value_object(&json, "user_info", state.user_info, NULL);
- if (command_info != NULL)
- print_key_value_object(&json, "command_info", command_info, NULL);
+ if (!add_key_value_object(&json, "options", state.settings, settings_filter))
+ goto oom;
+ if (!add_key_value_object(&json, "user_info", state.user_info, NULL))
+ goto oom;
+ if (command_info != NULL) {
+ if (!add_key_value_object(&json, "command_info", command_info, NULL))
+ goto oom;
+ }
/* Write submit_optind before submit_argv */
json_value.type = JSON_NUMBER;
json_value.u.number = state.submit_optind;
- sudo_json_add_value(&json, "submit_optind", &json_value);
-
- print_array(&json, "submit_argv", state.submit_argv);
- print_array(&json, "submit_envp", state.submit_envp);
- if (run_argv != NULL)
- print_array(&json, "run_argv", run_argv);
- if (run_envp != NULL)
- print_array(&json, "run_envp", run_envp);
+ if (!sudo_json_add_value(&json, "submit_optind", &json_value))
+ goto oom;
+
+ if (!add_array(&json, "submit_argv", state.submit_argv))
+ goto oom;
+ if (!add_array(&json, "submit_envp", state.submit_envp))
+ goto oom;
+ if (run_argv != NULL) {
+ if (!add_array(&json, "run_argv", run_argv))
+ goto oom;
+ }
+ if (run_envp != NULL) {
+ if (!add_array(&json, "run_envp", run_envp))
+ goto oom;
+ }
- sudo_json_close_object(&json); /* close audit_str */
- fputs("\n}\n", state.log_fp); /* close JSON */
- fflush(state.log_fp);
+ if (!sudo_json_close_object(&json))
+ goto oom;
- (void)sudo_lock_file(fileno(state.log_fp), SUDO_UNLOCK);
+ ret = audit_write_json(&json);
+ sudo_json_free(&json);
- ret = true;
done:
debug_return_int(ret);
+oom:
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ sudo_json_free(&json);
+ debug_return_int(-1);
}
static int