diff options
author | Todd C. Miller <Todd.Miller@sudo.ws> | 2020-02-17 16:10:55 -0700 |
---|---|---|
committer | Todd C. Miller <Todd.Miller@sudo.ws> | 2020-02-17 16:10:55 -0700 |
commit | c535f32f9cc3a0763dc68766e1a1dee51240a6e8 (patch) | |
tree | 67da63c5e03fa632f813ba0c163d6b94f2321d26 /plugins/audit_json | |
parent | 44bc820d484a029a57a9f4b7624e9c2df3913b99 (diff) | |
download | sudo-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.c | 220 |
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 |