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