"Fossies" - the Fresh Open Source Software Archive 
Member "sudo-1.9.11p3/plugins/audit_json/audit_json.c" (12 Jun 2022, 19932 Bytes) of package /linux/misc/sudo-1.9.11p3.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "audit_json.c" see the
Fossies "Dox" file reference documentation.
1 /*
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 2020-2021 Todd C. Miller <Todd.Miller@sudo.ws>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*
20 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
21 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
22 */
23
24 #include <config.h>
25
26 #include <sys/stat.h>
27 #include <sys/wait.h>
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #ifdef HAVE_STDBOOL_H
32 # include <stdbool.h>
33 #else
34 # include "compat/stdbool.h"
35 #endif /* HAVE_STDBOOL_H */
36 #include <string.h>
37 #include <signal.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <time.h>
42
43 #include "pathnames.h"
44 #include "sudo_compat.h"
45 #include "sudo_conf.h"
46 #include "sudo_debug.h"
47 #include "sudo_dso.h"
48 #include "sudo_fatal.h"
49 #include "sudo_gettext.h"
50 #include "sudo_json.h"
51 #include "sudo_plugin.h"
52 #include "sudo_util.h"
53
54 static int audit_debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER;
55 static sudo_conv_t audit_conv;
56 static sudo_printf_t audit_printf;
57
58 static struct audit_state {
59 int submit_optind;
60 char uuid_str[37];
61 bool accepted;
62 FILE *log_fp;
63 char *logfile;
64 char * const * settings;
65 char * const * user_info;
66 char * const * submit_argv;
67 char * const * submit_envp;
68 } state = { -1 };
69
70 /* Filter out entries in settings[] that are not really options. */
71 char * const settings_filter[] = {
72 "debug_flags",
73 "max_groups",
74 "network_addrs",
75 "plugin_dir",
76 "plugin_path",
77 "progname",
78 NULL
79 };
80
81 static int
82 audit_json_open(unsigned int version, sudo_conv_t conversation,
83 sudo_printf_t plugin_printf, char * const settings[],
84 char * const user_info[], int submit_optind, char * const submit_argv[],
85 char * const submit_envp[], char * const plugin_options[],
86 const char **errstr)
87 {
88 struct sudo_conf_debug_file_list debug_files =
89 TAILQ_HEAD_INITIALIZER(debug_files);
90 struct sudo_debug_file *debug_file;
91 const char *cp, *plugin_path = NULL;
92 unsigned char uuid[16];
93 char * const *cur;
94 mode_t oldmask;
95 int fd, ret = -1;
96 debug_decl_vars(audit_json_open, SUDO_DEBUG_PLUGIN);
97
98 audit_conv = conversation;
99 audit_printf = plugin_printf;
100
101 /*
102 * Stash initial values.
103 */
104 state.submit_optind = submit_optind;
105 state.settings = settings;
106 state.user_info = user_info;
107 state.submit_argv = submit_argv;
108 state.submit_envp = submit_envp;
109
110 /* Initialize the debug subsystem. */
111 for (cur = settings; (cp = *cur) != NULL; cur++) {
112 if (strncmp(cp, "debug_flags=", sizeof("debug_flags=") - 1) == 0) {
113 cp += sizeof("debug_flags=") - 1;
114 if (sudo_debug_parse_flags(&debug_files, cp) == -1)
115 goto oom;
116 continue;
117 }
118 if (strncmp(cp, "plugin_path=", sizeof("plugin_path=") - 1) == 0) {
119 plugin_path = cp + sizeof("plugin_path=") - 1;
120 continue;
121 }
122 }
123 if (plugin_path != NULL && !TAILQ_EMPTY(&debug_files)) {
124 audit_debug_instance =
125 sudo_debug_register(plugin_path, NULL, NULL, &debug_files, -1);
126 if (audit_debug_instance == SUDO_DEBUG_INSTANCE_ERROR) {
127 *errstr = U_("unable to initialize debugging");
128 goto bad;
129 }
130 sudo_debug_enter(__func__, __FILE__, __LINE__, sudo_debug_subsys);
131 }
132
133 /* Create a UUID for this command for use with audit records. */
134 sudo_uuid_create(uuid);
135 if (sudo_uuid_to_string(uuid, state.uuid_str, sizeof(state.uuid_str)) == NULL) {
136 *errstr = U_("unable to generate UUID");
137 goto bad;
138 }
139
140 /* Parse plugin_options to check for logfile option. */
141 if (plugin_options != NULL) {
142 for (cur = plugin_options; (cp = *cur) != NULL; cur++) {
143 if (strncmp(cp, "logfile=", sizeof("logfile=") - 1) == 0) {
144 state.logfile = strdup(cp + sizeof("logfile=") - 1);
145 if (state.logfile == NULL)
146 goto oom;
147 }
148 }
149 }
150 if (state.logfile == NULL) {
151 if (asprintf(&state.logfile, "%s/sudo_audit.json", _PATH_SUDO_LOGDIR) == -1)
152 goto oom;
153 }
154
155 /* open log file */
156 /* TODO: support pipe */
157 oldmask = umask(S_IRWXG|S_IRWXO);
158 fd = open(state.logfile, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
159 (void)umask(oldmask);
160 if (fd == -1 || (state.log_fp = fdopen(fd, "w")) == NULL) {
161 *errstr = U_("unable to open audit system");
162 if (fd != -1)
163 close(fd);
164 goto bad;
165 }
166
167 ret = 1;
168 goto done;
169
170 oom:
171 sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
172 *errstr = U_("unable to allocate memory");
173
174 bad:
175 if (state.log_fp != NULL) {
176 fclose(state.log_fp);
177 state.log_fp = NULL;
178 }
179
180 done:
181 while ((debug_file = TAILQ_FIRST(&debug_files))) {
182 TAILQ_REMOVE(&debug_files, debug_file, entries);
183 free(debug_file->debug_file);
184 free(debug_file->debug_flags);
185 free(debug_file);
186 }
187
188 debug_return_int(ret);
189 }
190
191 static bool
192 add_key_value(struct json_container *json, const char *str)
193 {
194 struct json_value json_value;
195 const char *cp, *errstr;
196 char name[256];
197 size_t len;
198 debug_decl(add_key_value, SUDO_DEBUG_PLUGIN);
199
200 if ((cp = strchr(str, '=')) == NULL) {
201 sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
202 "ignoring bad command info string \"%s\"", str);
203 debug_return_bool(false);
204 }
205 len = (size_t)(cp - str);
206 cp++;
207
208 /* Variable name currently limited to 256 chars */
209 if (len >= sizeof(name)) {
210 sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
211 "ignoring long command info name \"%.*s\"", (int)len, str);
212 debug_return_bool(false);
213 }
214 memcpy(name, str, len);
215 name[len] = '\0';
216
217 /* Check for bool or number. */
218 json_value.type = JSON_NULL;
219 switch (cp[0]) {
220 case '0':
221 if (cp[1] == '\0') {
222 /* Only treat a plain "0" as number 0. */
223 json_value.u.number = 0;
224 json_value.type = JSON_NUMBER;
225 }
226 break;
227 case '+': case '-':
228 if (cp[1] == '0') {
229 /* Encode octal numbers as strings. */
230 break;
231 }
232 FALLTHROUGH;
233 case '1': case '2': case '3': case '4': case '5':
234 case '6': case '7': case '8': case '9':
235 json_value.u.number = sudo_strtonum(cp, INT_MIN, INT_MAX, &errstr);
236 if (errstr == NULL)
237 json_value.type = JSON_NUMBER;
238 break;
239 case 't':
240 if (strcmp(cp, "true") == 0) {
241 json_value.type = JSON_BOOL;
242 json_value.u.boolean = true;
243 }
244 break;
245 case 'f':
246 if (strcmp(cp, "false") == 0) {
247 json_value.type = JSON_BOOL;
248 json_value.u.boolean = false;
249 }
250 break;
251 }
252
253 /* Default to string type. */
254 if (json_value.type == JSON_NULL) {
255 json_value.type = JSON_STRING;
256 json_value.u.string = cp;
257 }
258
259 debug_return_bool(sudo_json_add_value(json, name, &json_value));
260 }
261
262 static bool
263 add_array(struct json_container *json, const char *name, char * const * array)
264 {
265 const char *cp;
266 struct json_value json_value;
267 debug_decl(add_array, SUDO_DEBUG_PLUGIN);
268
269 if (!sudo_json_open_array(json, name))
270 debug_return_bool(false);
271 while ((cp = *array) != NULL) {
272 json_value.type = JSON_STRING;
273 json_value.u.string = cp;
274 if (!sudo_json_add_value(json, name, &json_value))
275 debug_return_bool(false);
276 array++;
277 }
278 if (!sudo_json_close_array(json))
279 debug_return_bool(false);
280
281 debug_return_bool(true);
282 }
283
284 static bool
285 filter_key_value(const char *kv, char * const * filter)
286 {
287 char * const *cur;
288 const char *cp;
289 size_t namelen;
290
291 if (filter != NULL) {
292 namelen = strcspn(kv, "=");
293 for (cur = filter; (cp = *cur) != NULL; cur++) {
294 if (strncmp(kv, cp, namelen) == 0 && cp[namelen] == '\0')
295 return true;
296 }
297 }
298 return false;
299 }
300
301 static bool
302 add_key_value_object(struct json_container *json, const char *name,
303 char * const * array, char * const * filter)
304 {
305 char * const *cur;
306 const char *cp;
307 bool empty = false;
308 debug_decl(add_key_value_object, SUDO_DEBUG_PLUGIN);
309
310 if (filter != NULL) {
311 /* Avoid printing an empty object if everything is filtered. */
312 empty = true;
313 for (cur = array; (cp = *cur) != NULL; cur++) {
314 if (!filter_key_value(cp, filter)) {
315 empty = false;
316 break;
317 }
318 }
319 }
320 if (!empty) {
321 if (!sudo_json_open_object(json, name))
322 goto bad;
323 for (cur = array; (cp = *cur) != NULL; cur++) {
324 if (filter_key_value(cp, filter))
325 continue;
326 if (!add_key_value(json, cp))
327 goto bad;
328 }
329 if (!sudo_json_close_object(json))
330 goto bad;
331 }
332
333 debug_return_bool(true);
334 bad:
335 debug_return_bool(false);
336 }
337
338 static bool
339 add_timestamp(struct json_container *json, struct timespec *ts)
340 {
341 struct json_value json_value;
342 time_t secs = ts->tv_sec;
343 char timebuf[1024];
344 struct tm gmt;
345 int len;
346 debug_decl(add_timestamp, SUDO_DEBUG_PLUGIN);
347
348 if (gmtime_r(&secs, &gmt) == NULL)
349 debug_return_bool(false);
350
351 sudo_json_open_object(json, "timestamp");
352
353 json_value.type = JSON_NUMBER;
354 json_value.u.number = ts->tv_sec;
355 sudo_json_add_value(json, "seconds", &json_value);
356
357 json_value.type = JSON_NUMBER;
358 json_value.u.number = ts->tv_nsec;
359 sudo_json_add_value(json, "nanoseconds", &json_value);
360
361 timebuf[sizeof(timebuf) - 1] = '\0';
362 len = strftime(timebuf, sizeof(timebuf), "%Y%m%d%H%M%SZ", &gmt);
363 if (len != 0 && timebuf[sizeof(timebuf) - 1] == '\0'){
364 json_value.type = JSON_STRING;
365 json_value.u.string = timebuf;
366 sudo_json_add_value(json, "iso8601", &json_value);
367 }
368
369 timebuf[sizeof(timebuf) - 1] = '\0';
370 len = strftime(timebuf, sizeof(timebuf), "%a %b %e %H:%M:%S %Z %Y", &gmt);
371 if (len != 0 && timebuf[sizeof(timebuf) - 1] == '\0'){
372 json_value.type = JSON_STRING;
373 json_value.u.string = timebuf;
374 sudo_json_add_value(json, "localtime", &json_value);
375 }
376
377 sudo_json_close_object(json);
378
379 debug_return_bool(true);
380 }
381
382 static int
383 audit_write_json(struct json_container *json)
384 {
385 struct stat sb;
386 int ret = -1;
387 debug_decl(audit_write_json, SUDO_DEBUG_PLUGIN);
388
389 if (!sudo_lock_file(fileno(state.log_fp), SUDO_LOCK)) {
390 sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
391 "unable to lock %s", state.logfile);
392 goto done;
393 }
394
395 /* Note: assumes file ends in "\n}\n" */
396 if (fstat(fileno(state.log_fp), &sb) == -1) {
397 sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
398 "unable to stat %s", state.logfile);
399 goto done;
400 }
401 if (sb.st_size == 0) {
402 /* New file */
403 putc('{', state.log_fp);
404 } else if (fseeko(state.log_fp, -3, SEEK_END) == 0) {
405 /* Continue file, overwrite the final "\n}\n" */
406 putc(',', state.log_fp);
407 } else {
408 sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
409 "unable to seek %s", state.logfile);
410 goto done;
411 }
412
413 fputs(sudo_json_get_buf(json), state.log_fp);
414 fputs("\n}\n", state.log_fp);
415 fflush(state.log_fp);
416 (void)sudo_lock_file(fileno(state.log_fp), SUDO_UNLOCK);
417
418 /* TODO: undo partial record on error */
419 if (!ferror(state.log_fp))
420 ret = true;
421
422 done:
423 debug_return_int(ret);
424 }
425
426 static int
427 audit_write_exit_record(int exit_status, int error)
428 {
429 struct json_container json;
430 struct json_value json_value;
431 struct timespec now;
432 int ret = -1;
433 debug_decl(audit_write_exit_record, SUDO_DEBUG_PLUGIN);
434
435 if (sudo_gettime_real(&now) == -1) {
436 sudo_warn("%s", U_("unable to read the clock"));
437 goto done;
438 }
439
440 if (!sudo_json_init(&json, 4, false, false))
441 goto oom;
442 if (!sudo_json_open_object(&json, "exit"))
443 goto oom;
444
445 /* Write UUID */
446 json_value.type = JSON_STRING;
447 json_value.u.string = state.uuid_str;
448 if (!sudo_json_add_value(&json, "uuid", &json_value))
449 goto oom;
450
451 /* Write time stamp */
452 if (!add_timestamp(&json, &now))
453 goto oom;
454
455 if (error != 0) {
456 /* Error executing command */
457 json_value.type = JSON_STRING;
458 json_value.u.string = strerror(error);
459 if (!sudo_json_add_value(&json, "error", &json_value))
460 goto oom;
461 } else {
462 if (WIFEXITED(exit_status)) {
463 /* Command exited normally. */
464 json_value.type = JSON_NUMBER;
465 json_value.u.number = WEXITSTATUS(exit_status);
466 if (!sudo_json_add_value(&json, "exit_value", &json_value))
467 goto oom;
468 } else if (WIFSIGNALED(exit_status)) {
469 /* Command killed by signal. */
470 char signame[SIG2STR_MAX];
471 int signo = WTERMSIG(exit_status);
472 if (signo <= 0 || sig2str(signo, signame) == -1) {
473 json_value.type = JSON_NUMBER;
474 json_value.u.number = signo;
475 if (!sudo_json_add_value(&json, "signal", &json_value))
476 goto oom;
477 } else {
478 json_value.type = JSON_STRING;
479 json_value.u.string = signame; // -V507
480 if (!sudo_json_add_value(&json, "signal", &json_value))
481 goto oom;
482 }
483 /* Core dump? */
484 json_value.type = JSON_BOOL;
485 json_value.u.boolean = WCOREDUMP(exit_status);
486 if (!sudo_json_add_value(&json, "dumped_core", &json_value))
487 goto oom;
488 /* Exit value */
489 json_value.type = JSON_NUMBER;
490 json_value.u.number = WTERMSIG(exit_status) | 128;
491 if (!sudo_json_add_value(&json, "exit_value", &json_value))
492 goto oom;
493 }
494 }
495
496 if (!sudo_json_close_object(&json))
497 goto oom;
498
499 ret = audit_write_json(&json);
500 sudo_json_free(&json);
501 done:
502 debug_return_int(ret);
503 oom:
504 sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
505 sudo_json_free(&json);
506 debug_return_int(-1);
507 }
508
509 static int
510 audit_write_record(const char *audit_str, const char *plugin_name,
511 unsigned int plugin_type, const char *reason, char * const command_info[],
512 char * const run_argv[], char * const run_envp[])
513 {
514 struct json_container json;
515 struct json_value json_value;
516 struct timespec now;
517 int ret = -1;
518 debug_decl(audit_write_record, SUDO_DEBUG_PLUGIN);
519
520 if (sudo_gettime_real(&now) == -1) {
521 sudo_warn("%s", U_("unable to read the clock"));
522 goto done;
523 }
524
525 if (!sudo_json_init(&json, 4, false, false))
526 goto oom;
527 if (!sudo_json_open_object(&json, audit_str))
528 goto oom;
529
530 json_value.type = JSON_STRING;
531 json_value.u.string = plugin_name;
532 if (!sudo_json_add_value(&json, "plugin_name", &json_value))
533 goto oom;
534
535 switch (plugin_type) {
536 case SUDO_FRONT_END:
537 json_value.u.string = "front-end";
538 break;
539 case SUDO_POLICY_PLUGIN:
540 json_value.u.string = "policy";
541 break;
542 case SUDO_IO_PLUGIN:
543 json_value.u.string = "io";
544 break;
545 case SUDO_APPROVAL_PLUGIN:
546 json_value.u.string = "approval";
547 break;
548 case SUDO_AUDIT_PLUGIN:
549 json_value.u.string = "audit";
550 break;
551 default:
552 json_value.u.string = "unknown";
553 break;
554 }
555 json_value.type = JSON_STRING;
556 if (!sudo_json_add_value(&json, "plugin_type", &json_value))
557 goto oom;
558
559 /* error and reject audit events usually contain a reason. */
560 if (reason != NULL) {
561 json_value.type = JSON_STRING;
562 json_value.u.string = reason;
563 if (!sudo_json_add_value(&json, "reason", &json_value))
564 goto oom;
565 }
566
567 json_value.type = JSON_STRING;
568 json_value.u.string = state.uuid_str;
569 if (!sudo_json_add_value(&json, "uuid", &json_value))
570 goto oom;
571
572 if (!add_timestamp(&json, &now))
573 goto oom;
574
575 /* Write key=value objects. */
576 if (state.settings != NULL) {
577 if (!add_key_value_object(&json, "options", state.settings, settings_filter))
578 goto oom;
579 } else {
580 sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
581 "missing settings list");
582 }
583 if (state.user_info != NULL) {
584 if (!add_key_value_object(&json, "user_info", state.user_info, NULL))
585 goto oom;
586 } else {
587 sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
588 "missing user_info list");
589 }
590 if (command_info != NULL) {
591 if (!add_key_value_object(&json, "command_info", command_info, NULL))
592 goto oom;
593 }
594
595 /* Write submit_optind before submit_argv */
596 json_value.type = JSON_NUMBER;
597 json_value.u.number = state.submit_optind;
598 if (!sudo_json_add_value(&json, "submit_optind", &json_value))
599 goto oom;
600
601 if (state.submit_argv != NULL) {
602 if (!add_array(&json, "submit_argv", state.submit_argv))
603 goto oom;
604 } else {
605 sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
606 "missing submit_argv array");
607 }
608 if (state.submit_envp != NULL) {
609 if (!add_array(&json, "submit_envp", state.submit_envp))
610 goto oom;
611 } else {
612 sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
613 "missing submit_envp array");
614 }
615 if (run_argv != NULL) {
616 if (!add_array(&json, "run_argv", run_argv))
617 goto oom;
618 }
619 if (run_envp != NULL) {
620 if (!add_array(&json, "run_envp", run_envp))
621 goto oom;
622 }
623
624 if (!sudo_json_close_object(&json))
625 goto oom;
626
627 ret = audit_write_json(&json);
628 sudo_json_free(&json);
629
630 done:
631 debug_return_int(ret);
632 oom:
633 sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
634 sudo_json_free(&json);
635 debug_return_int(-1);
636 }
637
638 static int
639 audit_json_accept(const char *plugin_name, unsigned int plugin_type,
640 char * const command_info[], char * const run_argv[],
641 char * const run_envp[], const char **errstr)
642 {
643 int ret;
644 debug_decl(audit_json_accept, SUDO_DEBUG_PLUGIN);
645
646 /* Ignore the extra accept event from the sudo front-end. */
647 if (plugin_type == SUDO_FRONT_END)
648 debug_return_int(true);
649
650 state.accepted = true;
651
652 ret = audit_write_record("accept", plugin_name, plugin_type, NULL,
653 command_info, run_argv, run_envp);
654
655 debug_return_int(ret);
656 }
657
658 static int
659 audit_json_reject(const char *plugin_name, unsigned int plugin_type,
660 const char *reason, char * const command_info[], const char **errstr)
661 {
662 int ret;
663 debug_decl(audit_json_reject, SUDO_DEBUG_PLUGIN);
664
665 ret = audit_write_record("reject", plugin_name, plugin_type,
666 reason, command_info, NULL, NULL);
667
668 debug_return_int(ret);
669 }
670
671 static int
672 audit_json_error(const char *plugin_name, unsigned int plugin_type,
673 const char *reason, char * const command_info[], const char **errstr)
674 {
675 int ret;
676 debug_decl(audit_json_error, SUDO_DEBUG_PLUGIN);
677
678 ret = audit_write_record("error", plugin_name, plugin_type,
679 reason, command_info, NULL, NULL);
680
681 debug_return_int(ret);
682 }
683
684 static void
685 audit_json_close(int status_type, int status)
686 {
687 debug_decl(audit_json_close, SUDO_DEBUG_PLUGIN);
688
689 switch (status_type) {
690 case SUDO_PLUGIN_NO_STATUS:
691 break;
692 case SUDO_PLUGIN_WAIT_STATUS:
693 audit_write_exit_record(status, 0);
694 break;
695 case SUDO_PLUGIN_EXEC_ERROR:
696 audit_write_exit_record(0, status);
697 break;
698 case SUDO_PLUGIN_SUDO_ERROR:
699 audit_write_record("error", "sudo", 0, strerror(status),
700 NULL, NULL, NULL);
701 break;
702 default:
703 sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
704 "unexpected status type %d, value %d", status_type, status);
705 break;
706 }
707
708 free(state.logfile);
709 if (state.log_fp != NULL)
710 fclose(state.log_fp);
711
712 debug_return;
713 }
714
715 static int
716 audit_json_show_version(int verbose)
717 {
718 debug_decl(audit_json_show_version, SUDO_DEBUG_PLUGIN);
719
720 audit_printf(SUDO_CONV_INFO_MSG, "JSON audit plugin version %s\n",
721 PACKAGE_VERSION);
722
723 debug_return_int(true);
724 }
725
726 sudo_dso_public struct audit_plugin audit_json = {
727 SUDO_AUDIT_PLUGIN,
728 SUDO_API_VERSION,
729 audit_json_open,
730 audit_json_close,
731 audit_json_accept,
732 audit_json_reject,
733 audit_json_error,
734 audit_json_show_version,
735 NULL, /* register_hooks */
736 NULL /* deregister_hooks */
737 };