Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/lib/eventlog/eventlog.c
3848 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 1994-1996, 1998-2023 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
* Sponsored in part by the Defense Advanced Research Projects
19
* Agency (DARPA) and Air Force Research Laboratory, Air Force
20
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
21
*/
22
23
#include <config.h>
24
25
#include <sys/types.h>
26
#include <sys/socket.h>
27
#include <sys/stat.h>
28
#include <sys/wait.h>
29
#include <netinet/in.h>
30
31
#include <ctype.h>
32
#include <errno.h>
33
#include <fcntl.h>
34
#include <grp.h>
35
#include <locale.h>
36
#include <pwd.h>
37
#include <signal.h>
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <syslog.h>
42
#include <time.h>
43
#include <unistd.h>
44
45
#include <pathnames.h>
46
#include <sudo_compat.h>
47
#include <sudo_debug.h>
48
#include <sudo_eventlog.h>
49
#include <sudo_lbuf.h>
50
#include <sudo_fatal.h>
51
#include <sudo_gettext.h>
52
#include <sudo_json.h>
53
#include <sudo_queue.h>
54
#include <sudo_util.h>
55
56
#define IS_SESSID(s) ( \
57
isalnum((unsigned char)(s)[0]) && isalnum((unsigned char)(s)[1]) && \
58
(s)[2] == '/' && \
59
isalnum((unsigned char)(s)[3]) && isalnum((unsigned char)(s)[4]) && \
60
(s)[5] == '/' && \
61
isalnum((unsigned char)(s)[6]) && isalnum((unsigned char)(s)[7]) && \
62
(s)[8] == '\0')
63
64
struct eventlog_args {
65
const char *reason;
66
const char *errstr;
67
const struct timespec *event_time;
68
eventlog_json_callback_t json_info_cb;
69
void *json_info;
70
};
71
72
/*
73
* Allocate and fill in a new logline.
74
*/
75
static bool
76
new_logline(int event_type, int flags, struct eventlog_args *args,
77
const struct eventlog *evlog, struct sudo_lbuf *lbuf)
78
{
79
const struct eventlog_config *evl_conf = eventlog_getconf();
80
const char *iolog_file;
81
const char *tty, *tsid = NULL;
82
char exit_str[STRLEN_MAX_SIGNED(int) + 1];
83
char sessid[7], offsetstr[64] = "";
84
size_t i;
85
debug_decl(new_logline, SUDO_DEBUG_UTIL);
86
87
if (ISSET(flags, EVLOG_RAW) || evlog == NULL) {
88
if (args->reason != NULL) {
89
if (args->errstr != NULL) {
90
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "%s: %s",
91
args->reason, args->errstr);
92
} else {
93
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "%s", args->reason);
94
}
95
if (sudo_lbuf_error(lbuf))
96
goto oom;
97
}
98
debug_return_bool(true);
99
}
100
101
/* A TSID may be a sudoers-style session ID or a free-form string. */
102
iolog_file = evlog->iolog_file;
103
if (iolog_file != NULL) {
104
if (IS_SESSID(iolog_file)) {
105
sessid[0] = iolog_file[0];
106
sessid[1] = iolog_file[1];
107
sessid[2] = iolog_file[3];
108
sessid[3] = iolog_file[4];
109
sessid[4] = iolog_file[6];
110
sessid[5] = iolog_file[7];
111
sessid[6] = '\0';
112
tsid = sessid;
113
} else {
114
tsid = iolog_file;
115
}
116
if (sudo_timespecisset(&evlog->iolog_offset)) {
117
/* Only write up to two significant digits for the decimal part. */
118
if (evlog->iolog_offset.tv_nsec > 10000000) {
119
(void)snprintf(offsetstr, sizeof(offsetstr), "@%lld.%02ld",
120
(long long)evlog->iolog_offset.tv_sec,
121
evlog->iolog_offset.tv_nsec / 10000000);
122
} else if (evlog->iolog_offset.tv_sec != 0) {
123
(void)snprintf(offsetstr, sizeof(offsetstr), "@%lld",
124
(long long)evlog->iolog_offset.tv_sec);
125
}
126
}
127
}
128
129
/* Sudo-format logs use the short form of the ttyname. */
130
if ((tty = evlog->ttyname) != NULL) {
131
if (strncmp(tty, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
132
tty += sizeof(_PATH_DEV) - 1;
133
}
134
135
/*
136
* Format the log line as an lbuf, escaping control characters in
137
* octal form (#0nn). Error checking (ENOMEM) is done at the end.
138
*/
139
if (args->reason != NULL) {
140
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "%s%s", args->reason,
141
args->errstr ? " : " : " ; ");
142
}
143
if (args->errstr != NULL) {
144
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "%s ; ", args->errstr);
145
}
146
if (evlog->submithost != NULL && !evl_conf->omit_hostname) {
147
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "HOST=%s ; ",
148
evlog->submithost);
149
}
150
if (tty != NULL) {
151
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "TTY=%s ; ", tty);
152
}
153
if (evlog->runchroot != NULL) {
154
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "CHROOT=%s ; ",
155
evlog->runchroot);
156
}
157
if (evlog->runcwd != NULL || evlog->cwd != NULL) {
158
if (ISSET(flags, EVLOG_CWD)) {
159
/* For sudoreplay -l output format. */
160
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "CWD=%s ; ",
161
evlog->runcwd ? evlog->runcwd : evlog->cwd);
162
} else if (evlog->runcwd != NULL) {
163
/* For backwards compatibility with sudo log format. */
164
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "PWD=%s ; ",
165
evlog->runcwd);
166
}
167
}
168
if (evlog->runuser != NULL) {
169
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "USER=%s ; ",
170
evlog->runuser);
171
}
172
if (evlog->rungroup != NULL) {
173
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "GROUP=%s ; ",
174
evlog->rungroup);
175
}
176
if (tsid != NULL) {
177
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "TSID=%s%s ; ", tsid,
178
offsetstr);
179
}
180
if (evlog->env_add != NULL && evlog->env_add[0] != NULL) {
181
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "ENV=%s",
182
evlog->env_add[0]);
183
for (i = 1; evlog->env_add[i] != NULL; i++) {
184
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, " %s",
185
evlog->env_add[i]);
186
}
187
sudo_lbuf_append(lbuf, " ; ");
188
}
189
if (evlog->command != NULL && evlog->runargv != NULL) {
190
/* Command plus argv. */
191
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL|LBUF_ESC_BLANK,
192
"COMMAND=%s", evlog->command);
193
if (evlog->runargv[0] != NULL) {
194
for (i = 1; evlog->runargv[i] != NULL; i++) {
195
sudo_lbuf_append(lbuf, " ");
196
if (strchr(evlog->runargv[i], ' ') != NULL) {
197
/* Wrap args containing spaces in single quotes. */
198
sudo_lbuf_append(lbuf, "'");
199
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL|LBUF_ESC_QUOTE,
200
"%s", evlog->runargv[i]);
201
sudo_lbuf_append(lbuf, "'");
202
} else {
203
/* Escape quotes here too for consistency. */
204
sudo_lbuf_append_esc(lbuf,
205
LBUF_ESC_CNTRL|LBUF_ESC_BLANK|LBUF_ESC_QUOTE,
206
"%s", evlog->runargv[i]);
207
}
208
}
209
}
210
if (event_type == EVLOG_EXIT) {
211
if (evlog->signal_name != NULL) {
212
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, " ; SIGNAL=%s",
213
evlog->signal_name);
214
}
215
if (evlog->exit_value != -1) {
216
(void)snprintf(exit_str, sizeof(exit_str), "%d",
217
evlog->exit_value);
218
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, " ; EXIT=%s",
219
exit_str);
220
}
221
}
222
} else if (evlog->command != NULL) {
223
/* Just the command, no argv. */
224
sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "COMMAND=%s",
225
evlog->command);
226
}
227
228
if (!sudo_lbuf_error(lbuf))
229
debug_return_bool(true);
230
oom:
231
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
232
debug_return_bool(false);
233
}
234
235
bool
236
eventlog_store_sudo(int event_type, const struct eventlog *evlog,
237
struct sudo_lbuf *lbuf)
238
{
239
struct eventlog_args args = { NULL };
240
241
return new_logline(event_type, EVLOG_CWD, &args, evlog, lbuf);
242
}
243
244
static void
245
closefrom_nodebug(int lowfd)
246
{
247
unsigned char *debug_fds;
248
int fd, startfd;
249
debug_decl(closefrom_nodebug, SUDO_DEBUG_UTIL);
250
251
startfd = sudo_debug_get_fds(&debug_fds) + 1;
252
if (lowfd > startfd)
253
startfd = lowfd;
254
255
/* Close fds higher than the debug fds. */
256
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
257
"closing fds >= %d", startfd);
258
closefrom(startfd);
259
260
/* Close fds [lowfd, startfd) that are not in debug_fds. */
261
for (fd = lowfd; fd < startfd; fd++) {
262
if (fd < 0 || sudo_isset(debug_fds, fd))
263
continue;
264
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
265
"closing fd %d", fd);
266
#ifdef __APPLE__
267
/* Avoid potential libdispatch crash when we close its fds. */
268
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
269
#else
270
(void) close(fd);
271
#endif
272
}
273
debug_return;
274
}
275
276
#define MAX_MAILFLAGS 63
277
278
sudo_noreturn static void
279
exec_mailer(int pipein) // -V1082
280
{
281
const struct eventlog_config *evl_conf = eventlog_getconf();
282
char *last, *mflags, *p, *argv[MAX_MAILFLAGS + 1];
283
const char *mpath = evl_conf->mailerpath;
284
gid_t mailgid = evl_conf->mailgid;
285
size_t i;
286
const char * const root_envp[] = {
287
"HOME=/",
288
"PATH=/usr/bin:/bin:/usr/sbin:/sbin",
289
"LOGNAME=root",
290
"USER=root",
291
# ifdef _AIX
292
"LOGIN=root",
293
# endif
294
NULL
295
};
296
debug_decl(exec_mailer, SUDO_DEBUG_UTIL);
297
298
/* Set stdin to read side of the pipe. */
299
if (dup3(pipein, STDIN_FILENO, 0) == -1) {
300
syslog(LOG_ERR, _("unable to dup stdin: %m")); // -V618
301
sudo_debug_printf(SUDO_DEBUG_ERROR,
302
"unable to dup stdin: %s", strerror(errno));
303
goto bad;
304
}
305
306
/* Build up an argv based on the mailer path and flags */
307
if ((mflags = strdup(evl_conf->mailerflags)) == NULL) {
308
syslog(LOG_ERR, "%s", _("unable to allocate memory"));
309
goto bad;
310
}
311
argv[0] = sudo_basename(mpath);
312
313
i = 1;
314
for (p = strtok_r(mflags, " \t", &last); p != NULL;
315
p = strtok_r(NULL, " \t", &last)) {
316
if (i < MAX_MAILFLAGS)
317
argv[i++] = p;
318
}
319
argv[i] = NULL;
320
321
/*
322
* Depending on the config, either run the mailer as root
323
* (so user cannot kill it) or as the user (for the paranoid).
324
*/
325
if (setuid(ROOT_UID) != 0) {
326
sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to change uid to %u",
327
ROOT_UID);
328
goto bad;
329
}
330
if (setgid(mailgid) != 0) {
331
sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to change gid to %u",
332
(unsigned int)mailgid);
333
goto bad;
334
}
335
if (setgroups(1, &mailgid) != 0) {
336
sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to set groups to %u",
337
(unsigned int)mailgid);
338
goto bad;
339
}
340
if (evl_conf->mailuid != ROOT_UID) {
341
if (setuid(evl_conf->mailuid) != 0) {
342
sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to change uid to %u",
343
(unsigned int)evl_conf->mailuid);
344
goto bad;
345
}
346
}
347
sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
348
if (evl_conf->mailuid == ROOT_UID)
349
execve(mpath, argv, (char **)root_envp);
350
else
351
execv(mpath, argv);
352
syslog(LOG_ERR, _("unable to execute %s: %m"), mpath); // -V618
353
sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to execute %s: %s",
354
mpath, strerror(errno));
355
_exit(127);
356
bad:
357
sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
358
_exit(127);
359
}
360
361
/* Send a message to the mailto user */
362
static bool
363
send_mail(const struct eventlog *evlog, const char *message)
364
{
365
const struct eventlog_config *evl_conf = eventlog_getconf();
366
const char *cp, *timefmt = evl_conf->time_fmt;
367
struct sigaction sa;
368
char timebuf[1024];
369
sigset_t chldmask;
370
struct tm tm;
371
time_t now;
372
FILE *mail;
373
int fd, pfd[2], status;
374
size_t len;
375
pid_t pid, rv;
376
struct stat sb;
377
#if defined(HAVE_NL_LANGINFO) && defined(CODESET)
378
char *locale;
379
#endif
380
debug_decl(send_mail, SUDO_DEBUG_UTIL);
381
382
/* If mailer is disabled just return. */
383
if (evl_conf->mailerpath == NULL || evl_conf->mailto == NULL)
384
debug_return_bool(true);
385
386
/* Make sure the mailer exists and is a regular file. */
387
if (stat(evl_conf->mailerpath, &sb) != 0 || !S_ISREG(sb.st_mode))
388
debug_return_bool(false);
389
390
time(&now);
391
if (localtime_r(&now, &tm) == NULL)
392
debug_return_bool(false);
393
394
/* Block SIGCHLD for the duration since we call waitpid() below. */
395
sigemptyset(&chldmask);
396
sigaddset(&chldmask, SIGCHLD);
397
(void)sigprocmask(SIG_BLOCK, &chldmask, NULL);
398
399
/* Fork and return, child will daemonize. */
400
switch (pid = sudo_debug_fork()) {
401
case -1:
402
/* Error. */
403
sudo_warn("%s", U_("unable to fork"));
404
405
/* Unblock SIGCHLD and return. */
406
(void)sigprocmask(SIG_UNBLOCK, &chldmask, NULL);
407
debug_return_bool(false);
408
case 0:
409
/* Child. */
410
switch (fork()) {
411
case -1:
412
/* Error. */
413
syslog(LOG_ERR, _("unable to fork: %m")); // -V618
414
sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to fork: %s",
415
strerror(errno));
416
sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
417
_exit(EXIT_FAILURE);
418
/* NOTREACHED */
419
case 0:
420
/* Grandchild continues below. */
421
sudo_debug_enter(__func__, __FILE__, __LINE__, sudo_debug_subsys);
422
break;
423
default:
424
/* Parent will wait for us. */
425
_exit(EXIT_SUCCESS);
426
/* NOTREACHED */
427
}
428
break;
429
default:
430
/* Parent. */
431
for (;;) {
432
rv = waitpid(pid, &status, 0);
433
if (rv == -1 && errno != EINTR)
434
break;
435
if (rv != -1 && !WIFSTOPPED(status))
436
break;
437
}
438
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
439
"child (%d) exit value %d", (int)rv, status);
440
441
/* Unblock SIGCHLD and return. */
442
(void)sigprocmask(SIG_UNBLOCK, &chldmask, NULL);
443
debug_return_bool(true);
444
}
445
446
/* Reset SIGCHLD to default and unblock it. */
447
memset(&sa, 0, sizeof(sa));
448
sigemptyset(&sa.sa_mask);
449
sa.sa_flags = SA_RESTART;
450
sa.sa_handler = SIG_DFL;
451
(void)sigaction(SIGCHLD, &sa, NULL);
452
(void)sigprocmask(SIG_UNBLOCK, &chldmask, NULL);
453
454
/* Daemonize - disassociate from session/tty. */
455
if (setsid() == -1)
456
sudo_warn("setsid");
457
if (chdir("/") == -1)
458
sudo_warn("chdir(/)");
459
fd = open(_PATH_DEVNULL, O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
460
if (fd != -1) {
461
(void) dup2(fd, STDIN_FILENO);
462
(void) dup2(fd, STDOUT_FILENO);
463
(void) dup2(fd, STDERR_FILENO);
464
}
465
466
/* Close non-debug fds so we don't leak anything. */
467
closefrom_nodebug(STDERR_FILENO + 1);
468
469
if (pipe2(pfd, O_CLOEXEC) == -1) {
470
syslog(LOG_ERR, _("unable to open pipe: %m")); // -V618
471
sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to open pipe: %s",
472
strerror(errno));
473
sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
474
_exit(EXIT_FAILURE);
475
}
476
477
switch (pid = sudo_debug_fork()) {
478
case -1:
479
/* Error. */
480
syslog(LOG_ERR, _("unable to fork: %m")); // -V618
481
sudo_debug_printf(
482
SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
483
"unable to fork");
484
sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
485
_exit(EXIT_FAILURE);
486
/* NOTREACHED */
487
case 0:
488
/* Child. */
489
exec_mailer(pfd[0]);
490
/* NOTREACHED */
491
}
492
493
(void) close(pfd[0]);
494
if ((mail = fdopen(pfd[1], "w")) == NULL) {
495
syslog(LOG_ERR, "fdopen: %m");
496
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
497
"unable to fdopen pipe");
498
sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
499
_exit(EXIT_FAILURE);
500
}
501
502
/* Pipes are all setup, send message. */
503
(void) fprintf(mail, "To: %s\nFrom: %s\nAuto-Submitted: %s\nSubject: ",
504
evl_conf->mailto,
505
evl_conf->mailfrom ? evl_conf->mailfrom :
506
(evlog ? evlog->submituser : "root"),
507
"auto-generated");
508
for (cp = _(evl_conf->mailsub); *cp; cp++) {
509
/* Expand escapes in the subject */
510
if (*cp == '%' && *(cp+1) != '%') {
511
switch (*(++cp)) {
512
case 'h':
513
if (evlog != NULL)
514
(void) fputs(evlog->submithost, mail);
515
break;
516
case 'u':
517
if (evlog != NULL)
518
(void) fputs(evlog->submituser, mail);
519
break;
520
default:
521
cp--;
522
break;
523
}
524
} else
525
(void) fputc(*cp, mail);
526
}
527
528
#if defined(HAVE_NL_LANGINFO) && defined(CODESET)
529
locale = setlocale(LC_ALL, NULL);
530
if (locale[0] != 'C' || locale[1] != '\0')
531
(void) fprintf(mail, "\nContent-Type: text/plain; charset=\"%s\"\nContent-Transfer-Encoding: 8bit", nl_langinfo(CODESET));
532
#endif /* HAVE_NL_LANGINFO && CODESET */
533
534
timebuf[sizeof(timebuf) - 1] = '\0';
535
len = strftime(timebuf, sizeof(timebuf), timefmt, &tm);
536
if (len == 0 || timebuf[sizeof(timebuf) - 1] != '\0') {
537
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_ERROR,
538
"strftime() failed to format time: %s", timefmt);
539
/* Fall back to default time format string. */
540
timebuf[sizeof(timebuf) - 1] = '\0';
541
len = strftime(timebuf, sizeof(timebuf), "%h %e %T", &tm);
542
if (len == 0 || timebuf[sizeof(timebuf) - 1] != '\0') {
543
timebuf[0] = '\0'; /* give up */
544
}
545
}
546
if (evlog != NULL) {
547
(void) fprintf(mail, "\n\n%s : %s : %s : ", evlog->submithost, timebuf,
548
evlog->submituser);
549
} else {
550
(void) fprintf(mail, "\n\n%s : ", timebuf);
551
}
552
fputs(message, mail);
553
fputs("\n\n", mail);
554
555
fclose(mail);
556
for (;;) {
557
rv = waitpid(pid, &status, 0);
558
if (rv == -1 && errno != EINTR)
559
break;
560
if (rv != -1 && !WIFSTOPPED(status))
561
break;
562
}
563
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
564
"child (%d) exit value %d", (int)rv, status);
565
sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
566
_exit(EXIT_SUCCESS);
567
}
568
569
static bool
570
json_add_timestamp(struct json_container *jsonc, const char *name,
571
const struct timespec *ts, bool format_timestamp)
572
{
573
struct json_value json_value;
574
size_t len;
575
debug_decl(json_add_timestamp, SUDO_DEBUG_PLUGIN);
576
577
if (!sudo_json_open_object(jsonc, name))
578
goto oom;
579
580
json_value.type = JSON_NUMBER;
581
json_value.u.number = ts->tv_sec;
582
if (!sudo_json_add_value(jsonc, "seconds", &json_value))
583
goto oom;
584
585
json_value.type = JSON_NUMBER;
586
json_value.u.number = ts->tv_nsec;
587
if (!sudo_json_add_value(jsonc, "nanoseconds", &json_value))
588
goto oom;
589
590
if (format_timestamp) {
591
const struct eventlog_config *evl_conf = eventlog_getconf();
592
const char *timefmt = evl_conf->time_fmt;
593
time_t secs = ts->tv_sec;
594
char timebuf[1024];
595
struct tm tm;
596
597
if (gmtime_r(&secs, &tm) != NULL) {
598
timebuf[sizeof(timebuf) - 1] = '\0';
599
len = strftime(timebuf, sizeof(timebuf), "%Y%m%d%H%M%SZ", &tm);
600
if (len != 0 && timebuf[sizeof(timebuf) - 1] == '\0') {
601
json_value.type = JSON_STRING;
602
json_value.u.string = timebuf; // -V507
603
if (!sudo_json_add_value(jsonc, "iso8601", &json_value))
604
goto oom;
605
}
606
}
607
608
if (localtime_r(&secs, &tm) != NULL) {
609
timebuf[sizeof(timebuf) - 1] = '\0';
610
len = strftime(timebuf, sizeof(timebuf), timefmt, &tm);
611
if (len != 0 && timebuf[sizeof(timebuf) - 1] == '\0') {
612
json_value.type = JSON_STRING;
613
json_value.u.string = timebuf; // -V507
614
if (!sudo_json_add_value(jsonc, "localtime", &json_value))
615
goto oom;
616
}
617
}
618
}
619
620
if (!sudo_json_close_object(jsonc))
621
goto oom;
622
623
debug_return_bool(true);
624
oom:
625
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
626
"%s: %s", __func__, "unable to allocate memory");
627
debug_return_bool(false);
628
}
629
630
/*
631
* Store the contents of struct eventlog as JSON.
632
* The event_time and iolog_path members are not stored, they should
633
* be stored and formatted by the caller.
634
*/
635
bool
636
eventlog_store_json(struct json_container *jsonc, const struct eventlog *evlog)
637
{
638
struct json_value json_value;
639
size_t i;
640
char *cp;
641
debug_decl(eventlog_store_json, SUDO_DEBUG_UTIL);
642
643
/* Required settings. */
644
if (evlog == NULL || evlog->submituser == NULL)
645
debug_return_bool(false);
646
647
/*
648
* The most important values are written first in case
649
* the log record gets truncated.
650
* Note: event_time and iolog_path are not stored here.
651
*/
652
653
json_value.type = JSON_STRING;
654
json_value.u.string = evlog->submituser;
655
if (!sudo_json_add_value(jsonc, "submituser", &json_value))
656
goto oom;
657
658
if (evlog->command != NULL) {
659
json_value.type = JSON_STRING;
660
json_value.u.string = evlog->command;
661
if (!sudo_json_add_value(jsonc, "command", &json_value))
662
goto oom;
663
}
664
665
if (evlog->runuser != NULL) {
666
json_value.type = JSON_STRING;
667
json_value.u.string = evlog->runuser;
668
if (!sudo_json_add_value(jsonc, "runuser", &json_value))
669
goto oom;
670
}
671
672
if (evlog->rungroup != NULL) {
673
json_value.type = JSON_STRING;
674
json_value.u.string = evlog->rungroup;
675
if (!sudo_json_add_value(jsonc, "rungroup", &json_value))
676
goto oom;
677
}
678
679
if (evlog->runchroot != NULL) {
680
json_value.type = JSON_STRING;
681
json_value.u.string = evlog->runchroot;
682
if (!sudo_json_add_value(jsonc, "runchroot", &json_value))
683
goto oom;
684
}
685
686
if (evlog->runcwd != NULL) {
687
json_value.type = JSON_STRING;
688
json_value.u.string = evlog->runcwd;
689
if (!sudo_json_add_value(jsonc, "runcwd", &json_value))
690
goto oom;
691
}
692
693
if (evlog->source != NULL) {
694
json_value.type = JSON_STRING;
695
json_value.u.string = evlog->source;
696
if (!sudo_json_add_value(jsonc, "source", &json_value))
697
goto oom;
698
}
699
700
if (evlog->ttyname != NULL) {
701
json_value.type = JSON_STRING;
702
json_value.u.string = evlog->ttyname;
703
if (!sudo_json_add_value(jsonc, "ttyname", &json_value))
704
goto oom;
705
}
706
707
if (evlog->submithost != NULL) {
708
json_value.type = JSON_STRING;
709
json_value.u.string = evlog->submithost;
710
if (!sudo_json_add_value(jsonc, "submithost", &json_value))
711
goto oom;
712
}
713
714
if (evlog->cwd != NULL) {
715
json_value.type = JSON_STRING;
716
json_value.u.string = evlog->cwd;
717
if (!sudo_json_add_value(jsonc, "submitcwd", &json_value))
718
goto oom;
719
}
720
721
if (evlog->rungroup!= NULL && evlog->rungid != (gid_t)-1) {
722
json_value.type = JSON_ID;
723
json_value.u.id = evlog->rungid;
724
if (!sudo_json_add_value(jsonc, "rungid", &json_value))
725
goto oom;
726
}
727
728
if (evlog->runuid != (uid_t)-1) {
729
json_value.type = JSON_ID;
730
json_value.u.id = evlog->runuid;
731
if (!sudo_json_add_value(jsonc, "runuid", &json_value))
732
goto oom;
733
}
734
735
json_value.type = JSON_NUMBER;
736
json_value.u.number = evlog->columns;
737
if (!sudo_json_add_value(jsonc, "columns", &json_value))
738
goto oom;
739
740
json_value.type = JSON_NUMBER;
741
json_value.u.number = evlog->lines;
742
if (!sudo_json_add_value(jsonc, "lines", &json_value))
743
goto oom;
744
745
if (evlog->runargv != NULL) {
746
if (!sudo_json_open_array(jsonc, "runargv"))
747
goto oom;
748
for (i = 0; (cp = evlog->runargv[i]) != NULL; i++) {
749
json_value.type = JSON_STRING;
750
json_value.u.string = cp;
751
if (!sudo_json_add_value(jsonc, NULL, &json_value))
752
goto oom;
753
}
754
if (!sudo_json_close_array(jsonc))
755
goto oom;
756
}
757
758
if (evlog->runenv != NULL) {
759
if (!sudo_json_open_array(jsonc, "runenv"))
760
goto oom;
761
for (i = 0; (cp = evlog->runenv[i]) != NULL; i++) {
762
json_value.type = JSON_STRING;
763
json_value.u.string = cp;
764
if (!sudo_json_add_value(jsonc, NULL, &json_value))
765
goto oom;
766
}
767
if (!sudo_json_close_array(jsonc))
768
goto oom;
769
}
770
771
if (evlog->submitenv != NULL) {
772
if (!sudo_json_open_array(jsonc, "submitenv"))
773
goto oom;
774
for (i = 0; (cp = evlog->submitenv[i]) != NULL; i++) {
775
json_value.type = JSON_STRING;
776
json_value.u.string = cp;
777
if (!sudo_json_add_value(jsonc, NULL, &json_value))
778
goto oom;
779
}
780
if (!sudo_json_close_array(jsonc))
781
goto oom;
782
}
783
784
debug_return_bool(true);
785
786
oom:
787
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
788
debug_return_bool(false);
789
}
790
791
static bool
792
default_json_cb(struct json_container *jsonc, void *v)
793
{
794
return eventlog_store_json(jsonc, v);
795
}
796
797
static char *
798
format_json(int event_type, struct eventlog_args *args,
799
const struct eventlog *evlog, bool compact)
800
{
801
eventlog_json_callback_t info_cb = args->json_info_cb;
802
void *info = args->json_info;
803
struct json_container jsonc = { 0 };
804
struct json_value json_value;
805
const char *time_str, *type_str;
806
struct timespec now;
807
debug_decl(format_json, SUDO_DEBUG_UTIL);
808
809
if (info_cb == NULL) {
810
info_cb = default_json_cb;
811
info = (void *)evlog;
812
}
813
814
if (sudo_gettime_real(&now) == -1) {
815
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
816
"unable to read the clock");
817
debug_return_str(NULL);
818
}
819
820
switch (event_type) {
821
case EVLOG_ACCEPT:
822
type_str = "accept";
823
time_str = "submit_time";
824
break;
825
case EVLOG_REJECT:
826
type_str = "reject";
827
time_str = "submit_time";
828
break;
829
case EVLOG_ALERT:
830
type_str = "alert";
831
time_str = "alert_time";
832
break;
833
case EVLOG_EXIT:
834
type_str = "exit";
835
time_str = "exit_time";
836
break;
837
default:
838
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
839
"unexpected event type %d", event_type);
840
debug_return_str(NULL);
841
}
842
843
if (!sudo_json_init(&jsonc, 4, compact, false, false))
844
goto bad;
845
if (!sudo_json_open_object(&jsonc, type_str))
846
goto bad;
847
848
if (evlog != NULL && evlog->uuid_str[0] != '\0') {
849
json_value.type = JSON_STRING;
850
json_value.u.string = evlog->uuid_str;
851
if (!sudo_json_add_value(&jsonc, "uuid", &json_value))
852
goto bad;
853
}
854
855
/* Reject and Alert events include a reason and optional error string. */
856
if (args->reason != NULL) {
857
char *ereason = NULL;
858
859
if (args->errstr != NULL) {
860
const int len = asprintf(&ereason, _("%s: %s"), args->reason,
861
args->errstr);
862
if (len == -1) {
863
sudo_warnx(U_("%s: %s"), __func__,
864
U_("unable to allocate memory"));
865
goto bad;
866
}
867
}
868
json_value.type = JSON_STRING;
869
json_value.u.string = ereason ? ereason : args->reason;
870
if (!sudo_json_add_value(&jsonc, "reason", &json_value)) {
871
free(ereason);
872
goto bad;
873
}
874
free(ereason);
875
}
876
877
/* Log event time on server (set earlier) */
878
if (!json_add_timestamp(&jsonc, "server_time", &now, true)) {
879
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
880
"unable format timestamp");
881
goto bad;
882
}
883
884
/* Log event time from client */
885
if (args->event_time != NULL) {
886
if (!json_add_timestamp(&jsonc, time_str, args->event_time, true)) {
887
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
888
"unable format timestamp");
889
goto bad;
890
}
891
}
892
893
/* Event log info may be missing for alert messages. */
894
if (evlog != NULL) {
895
if (evlog->peeraddr != NULL) {
896
json_value.type = JSON_STRING;
897
json_value.u.string = evlog->peeraddr;
898
if (!sudo_json_add_value(&jsonc, "peeraddr", &json_value))
899
goto bad;
900
}
901
902
if (evlog->iolog_path != NULL) {
903
json_value.type = JSON_STRING;
904
json_value.u.string = evlog->iolog_path;
905
if (!sudo_json_add_value(&jsonc, "iolog_path", &json_value))
906
goto bad;
907
908
if (sudo_timespecisset(&evlog->iolog_offset)) {
909
if (!json_add_timestamp(&jsonc, "iolog_offset", &evlog->iolog_offset, false)) {
910
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
911
"unable format timestamp");
912
goto bad;
913
}
914
}
915
}
916
917
if (event_type == EVLOG_EXIT) {
918
/* Exit events don't need evlog details if there is a UUID. */
919
if (evlog->uuid_str[0] != '\0') {
920
if (args->json_info == NULL)
921
info = NULL;
922
}
923
924
if (sudo_timespecisset(&evlog->run_time)) {
925
if (!json_add_timestamp(&jsonc, "run_time", &evlog->run_time,
926
false)) {
927
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
928
"unable format timestamp");
929
goto bad;
930
}
931
}
932
if (evlog->signal_name != NULL) {
933
json_value.type = JSON_STRING;
934
json_value.u.string = evlog->signal_name;
935
if (!sudo_json_add_value(&jsonc, "signal", &json_value))
936
goto bad;
937
938
json_value.type = JSON_BOOL;
939
json_value.u.boolean = evlog->dumped_core;
940
if (!sudo_json_add_value(&jsonc, "dumped_core", &json_value))
941
goto bad;
942
}
943
json_value.type = JSON_NUMBER;
944
json_value.u.number = evlog->exit_value;
945
if (!sudo_json_add_value(&jsonc, "exit_value", &json_value))
946
goto bad;
947
}
948
}
949
950
/* Write log info. */
951
if (info != NULL) {
952
if (!info_cb(&jsonc, info))
953
goto bad;
954
}
955
956
if (!sudo_json_close_object(&jsonc))
957
goto bad;
958
959
/* Caller is responsible for freeing the buffer. */
960
debug_return_str(sudo_json_get_buf(&jsonc));
961
962
bad:
963
sudo_json_free(&jsonc);
964
debug_return_str(NULL);
965
}
966
967
/*
968
* Log a message to syslog, prepending the username and splitting the
969
* message into parts if it is longer than syslog_maxlen.
970
*/
971
static bool
972
do_syslog_sudo(int pri, char *logline, const struct eventlog *evlog)
973
{
974
const struct eventlog_config *evl_conf = eventlog_getconf();
975
size_t len, maxlen;
976
char *p, *tmp, save;
977
const char *fmt;
978
debug_decl(do_syslog_sudo, SUDO_DEBUG_UTIL);
979
980
evl_conf->open_log(EVLOG_SYSLOG, NULL);
981
982
if (evlog == NULL) {
983
/* Not a command, just log it as-is. */
984
syslog(pri, "%s", logline);
985
goto done;
986
}
987
988
/*
989
* Log the full line, breaking into multiple syslog(3) calls if necessary
990
*/
991
fmt = _("%8s : %s");
992
maxlen = evl_conf->syslog_maxlen -
993
(strlen(fmt) - 5 + strlen(evlog->submituser));
994
for (p = logline; *p != '\0'; ) {
995
len = strlen(p);
996
if (len > maxlen) {
997
/*
998
* Break up the line into what will fit on one syslog(3) line
999
* Try to avoid breaking words into several lines if possible.
1000
*/
1001
tmp = memrchr(p, ' ', maxlen);
1002
if (tmp == NULL)
1003
tmp = p + maxlen;
1004
1005
/* NULL terminate line, but save the char to restore later */
1006
save = *tmp;
1007
*tmp = '\0';
1008
1009
syslog(pri, fmt, evlog->submituser, p);
1010
1011
*tmp = save; /* restore saved character */
1012
1013
/* Advance p and eliminate leading whitespace */
1014
for (p = tmp; *p == ' '; p++)
1015
continue;
1016
} else {
1017
syslog(pri, fmt, evlog->submituser, p);
1018
p += len;
1019
}
1020
fmt = _("%8s : (command continued) %s");
1021
maxlen = evl_conf->syslog_maxlen -
1022
(strlen(fmt) - 5 + strlen(evlog->submituser));
1023
}
1024
done:
1025
evl_conf->close_log(EVLOG_SYSLOG, NULL);
1026
1027
debug_return_bool(true);
1028
}
1029
1030
static bool
1031
do_syslog_json(int pri, int event_type, struct eventlog_args *args,
1032
const struct eventlog *evlog)
1033
{
1034
const struct eventlog_config *evl_conf = eventlog_getconf();
1035
char *json_str;
1036
debug_decl(do_syslog_json, SUDO_DEBUG_UTIL);
1037
1038
/* Format as a compact JSON message (no newlines) */
1039
json_str = format_json(event_type, args, evlog, true);
1040
if (json_str == NULL)
1041
debug_return_bool(false);
1042
1043
/* Syslog it in a sudo object with a @cee: prefix. */
1044
/* TODO: use evl_conf->syslog_maxlen to break up long messages. */
1045
evl_conf->open_log(EVLOG_SYSLOG, NULL);
1046
syslog(pri, "@cee:{\"sudo\":{%s}}", json_str);
1047
evl_conf->close_log(EVLOG_SYSLOG, NULL);
1048
free(json_str);
1049
debug_return_bool(true);
1050
}
1051
1052
/*
1053
* Log a message to syslog in either sudo or JSON format.
1054
*/
1055
static bool
1056
do_syslog(int event_type, int flags, struct eventlog_args *args,
1057
const struct eventlog *evlog)
1058
{
1059
const struct eventlog_config *evl_conf = eventlog_getconf();
1060
struct sudo_lbuf lbuf;
1061
bool ret = false;
1062
int pri;
1063
debug_decl(do_syslog, SUDO_DEBUG_UTIL);
1064
1065
sudo_lbuf_init(&lbuf, NULL, 0, NULL, 0);
1066
1067
/* Sudo format logs and mailed logs use the same log line format. */
1068
if (evl_conf->format == EVLOG_SUDO || ISSET(flags, EVLOG_MAIL)) {
1069
if (!new_logline(event_type, flags, args, evlog, &lbuf))
1070
goto done;
1071
1072
if (ISSET(flags, EVLOG_MAIL)) {
1073
if (!send_mail(evlog, lbuf.buf)) {
1074
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1075
"unable to mail log line");
1076
}
1077
if (ISSET(flags, EVLOG_MAIL_ONLY)) {
1078
ret = true;
1079
goto done;
1080
}
1081
}
1082
}
1083
1084
switch (event_type) {
1085
case EVLOG_ACCEPT:
1086
case EVLOG_EXIT:
1087
pri = evl_conf->syslog_acceptpri;
1088
break;
1089
case EVLOG_REJECT:
1090
pri = evl_conf->syslog_rejectpri;
1091
break;
1092
case EVLOG_ALERT:
1093
pri = evl_conf->syslog_alertpri;
1094
break;
1095
default:
1096
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1097
"unexpected event type %d", event_type);
1098
pri = -1;
1099
break;
1100
}
1101
if (pri == -1) {
1102
/* syslog disabled for this message type */
1103
ret = true;
1104
goto done;
1105
}
1106
1107
switch (evl_conf->format) {
1108
case EVLOG_SUDO:
1109
ret = do_syslog_sudo(pri, lbuf.buf, evlog);
1110
break;
1111
case EVLOG_JSON_COMPACT:
1112
case EVLOG_JSON_PRETTY:
1113
ret = do_syslog_json(pri, event_type, args, evlog);
1114
break;
1115
default:
1116
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1117
"unexpected eventlog format %d", evl_conf->format);
1118
break;
1119
}
1120
done:
1121
sudo_lbuf_destroy(&lbuf);
1122
debug_return_bool(ret);
1123
}
1124
1125
static bool
1126
do_logfile_sudo(const char *logline, const struct eventlog *evlog,
1127
const struct timespec *event_time)
1128
{
1129
const struct eventlog_config *evl_conf = eventlog_getconf();
1130
char *full_line, timebuf[8192], *timestr = NULL;
1131
const char *timefmt = evl_conf->time_fmt;
1132
const char *logfile = evl_conf->logpath;
1133
struct tm tm;
1134
bool ret = false;
1135
FILE *fp;
1136
int len;
1137
debug_decl(do_logfile_sudo, SUDO_DEBUG_UTIL);
1138
1139
if ((fp = evl_conf->open_log(EVLOG_FILE, logfile)) == NULL)
1140
debug_return_bool(false);
1141
1142
if (!sudo_lock_file(fileno(fp), SUDO_LOCK)) {
1143
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
1144
"unable to lock log file %s", logfile);
1145
goto done;
1146
}
1147
1148
if (event_time != NULL) {
1149
time_t tv_sec = event_time->tv_sec;
1150
if (localtime_r(&tv_sec, &tm) != NULL) {
1151
/* strftime() does not guarantee to NUL-terminate so we must check. */
1152
timebuf[sizeof(timebuf) - 1] = '\0';
1153
if (strftime(timebuf, sizeof(timebuf), timefmt, &tm) != 0 &&
1154
timebuf[sizeof(timebuf) - 1] == '\0') {
1155
timestr = timebuf;
1156
}
1157
}
1158
}
1159
if (evlog != NULL) {
1160
len = asprintf(&full_line, "%s : %s : %s",
1161
timestr ? timestr : "invalid date", evlog->submituser, logline);
1162
} else {
1163
len = asprintf(&full_line, "%s : %s",
1164
timestr ? timestr : "invalid date", logline);
1165
}
1166
if (len == -1) {
1167
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1168
goto done;
1169
}
1170
eventlog_writeln(fp, full_line, (size_t)len, evl_conf->file_maxlen);
1171
free(full_line);
1172
(void)fflush(fp);
1173
if (ferror(fp)) {
1174
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
1175
"unable to write log file %s", logfile);
1176
goto done;
1177
}
1178
ret = true;
1179
1180
done:
1181
(void)sudo_lock_file(fileno(fp), SUDO_UNLOCK);
1182
evl_conf->close_log(EVLOG_FILE, fp);
1183
debug_return_bool(ret);
1184
}
1185
1186
static bool
1187
do_logfile_json(enum eventlog_format format, int event_type,
1188
struct eventlog_args *args, const struct eventlog *evlog)
1189
{
1190
const struct eventlog_config *evl_conf = eventlog_getconf();
1191
const char *logfile = evl_conf->logpath;
1192
const bool compact = format == EVLOG_JSON_COMPACT;
1193
struct stat sb;
1194
char *json_str;
1195
int ret = false;
1196
FILE *fp;
1197
debug_decl(do_logfile_json, SUDO_DEBUG_UTIL);
1198
1199
if ((fp = evl_conf->open_log(EVLOG_FILE, logfile)) == NULL)
1200
debug_return_bool(false);
1201
1202
json_str = format_json(event_type, args, evlog, compact);
1203
if (json_str == NULL)
1204
goto done;
1205
1206
if (!sudo_lock_file(fileno(fp), SUDO_LOCK)) {
1207
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
1208
"unable to lock log file %s", logfile);
1209
goto done;
1210
}
1211
1212
if (!compact) {
1213
/* Note: assumes file ends in "\n}\n" */
1214
if (fstat(fileno(fp), &sb) == -1) {
1215
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
1216
"unable to stat %s", logfile);
1217
goto done;
1218
}
1219
if (sb.st_size == 0) {
1220
/* New file */
1221
putc('{', fp);
1222
} else if (fseeko(fp, -3, SEEK_END) == 0) {
1223
/* Continue file, overwrite the final "\n}\n" */
1224
putc(',', fp);
1225
} else {
1226
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
1227
"unable to seek %s", logfile);
1228
goto done;
1229
}
1230
fputs(json_str, fp);
1231
fputs("\n}\n", fp); /* close JSON */
1232
} else {
1233
/* Compact (minified) JSON records, one per line. */
1234
putc('{', fp);
1235
fputs(json_str, fp);
1236
fputs("}\n", fp);
1237
}
1238
fflush(fp);
1239
/* XXX - check for file error and recover */
1240
1241
ret = true;
1242
1243
done:
1244
free(json_str);
1245
(void)sudo_lock_file(fileno(fp), SUDO_UNLOCK);
1246
evl_conf->close_log(EVLOG_FILE, fp);
1247
debug_return_bool(ret);
1248
}
1249
1250
static bool
1251
do_logfile(int event_type, int flags, struct eventlog_args *args,
1252
const struct eventlog *evlog)
1253
{
1254
const struct eventlog_config *evl_conf = eventlog_getconf();
1255
struct sudo_lbuf lbuf;
1256
bool ret = false;
1257
debug_decl(do_logfile, SUDO_DEBUG_UTIL);
1258
1259
sudo_lbuf_init(&lbuf, NULL, 0, NULL, 0);
1260
1261
/* Sudo format logs and mailed logs use the same log line format. */
1262
if (evl_conf->format == EVLOG_SUDO || ISSET(flags, EVLOG_MAIL)) {
1263
if (!new_logline(event_type, flags, args, evlog, &lbuf))
1264
goto done;
1265
1266
if (ISSET(flags, EVLOG_MAIL)) {
1267
if (!send_mail(evlog, lbuf.buf)) {
1268
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1269
"unable to mail log line");
1270
}
1271
if (ISSET(flags, EVLOG_MAIL_ONLY)) {
1272
ret = true;
1273
goto done;
1274
}
1275
}
1276
}
1277
1278
switch (evl_conf->format) {
1279
case EVLOG_SUDO:
1280
ret = do_logfile_sudo(lbuf.buf ? lbuf.buf : args->reason, evlog,
1281
args->event_time);
1282
break;
1283
case EVLOG_JSON_COMPACT:
1284
case EVLOG_JSON_PRETTY:
1285
ret = do_logfile_json(evl_conf->format, event_type, args, evlog);
1286
break;
1287
default:
1288
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1289
"unexpected eventlog format %d", evl_conf->format);
1290
break;
1291
}
1292
1293
done:
1294
sudo_lbuf_destroy(&lbuf);
1295
debug_return_bool(ret);
1296
}
1297
1298
bool
1299
eventlog_accept(const struct eventlog *evlog, int flags,
1300
eventlog_json_callback_t info_cb, void *info)
1301
{
1302
const struct eventlog_config *evl_conf = eventlog_getconf();
1303
const int log_type = evl_conf->type;
1304
struct eventlog_args args = { NULL };
1305
bool ret = true;
1306
debug_decl(eventlog_accept, SUDO_DEBUG_UTIL);
1307
1308
args.event_time = &evlog->event_time;
1309
args.json_info_cb = info_cb;
1310
args.json_info = info;
1311
1312
if (ISSET(log_type, EVLOG_SYSLOG)) {
1313
if (!do_syslog(EVLOG_ACCEPT, flags, &args, evlog))
1314
ret = false;
1315
CLR(flags, EVLOG_MAIL);
1316
}
1317
if (ISSET(log_type, EVLOG_FILE)) {
1318
if (!do_logfile(EVLOG_ACCEPT, flags, &args, evlog))
1319
ret = false;
1320
}
1321
1322
debug_return_bool(ret);
1323
}
1324
1325
bool
1326
eventlog_reject(const struct eventlog *evlog, int flags, const char *reason,
1327
eventlog_json_callback_t info_cb, void *info)
1328
{
1329
const struct eventlog_config *evl_conf = eventlog_getconf();
1330
const int log_type = evl_conf->type;
1331
struct eventlog_args args = { NULL };
1332
bool ret = true;
1333
debug_decl(eventlog_reject, SUDO_DEBUG_UTIL);
1334
1335
args.reason = reason;
1336
args.event_time = &evlog->event_time;
1337
args.json_info_cb = info_cb;
1338
args.json_info = info;
1339
1340
if (ISSET(log_type, EVLOG_SYSLOG)) {
1341
if (!do_syslog(EVLOG_REJECT, flags, &args, evlog))
1342
ret = false;
1343
CLR(flags, EVLOG_MAIL);
1344
}
1345
if (ISSET(log_type, EVLOG_FILE)) {
1346
if (!do_logfile(EVLOG_REJECT, flags, &args, evlog))
1347
ret = false;
1348
}
1349
1350
debug_return_bool(ret);
1351
}
1352
1353
bool
1354
eventlog_alert(const struct eventlog *evlog, int flags,
1355
struct timespec *alert_time, const char *reason, const char *errstr)
1356
{
1357
const struct eventlog_config *evl_conf = eventlog_getconf();
1358
const int log_type = evl_conf->type;
1359
struct eventlog_args args = { NULL };
1360
bool ret = true;
1361
debug_decl(eventlog_alert, SUDO_DEBUG_UTIL);
1362
1363
args.reason = reason;
1364
args.errstr = errstr;
1365
args.event_time = alert_time;
1366
1367
if (ISSET(log_type, EVLOG_SYSLOG)) {
1368
if (!do_syslog(EVLOG_ALERT, flags, &args, evlog))
1369
ret = false;
1370
CLR(flags, EVLOG_MAIL);
1371
}
1372
if (ISSET(log_type, EVLOG_FILE)) {
1373
if (!do_logfile(EVLOG_ALERT, flags, &args, evlog))
1374
ret = false;
1375
}
1376
1377
debug_return_bool(ret);
1378
}
1379
1380
bool
1381
eventlog_mail(const struct eventlog *evlog, int flags,
1382
struct timespec *event_time, const char *reason, const char *errstr,
1383
char * const extra[])
1384
{
1385
struct eventlog_args args = { NULL };
1386
struct sudo_lbuf lbuf;
1387
bool ret = false;
1388
debug_decl(eventlog_mail, SUDO_DEBUG_UTIL);
1389
1390
args.reason = reason;
1391
args.errstr = errstr;
1392
args.event_time = event_time;
1393
1394
sudo_lbuf_init(&lbuf, NULL, 0, NULL, 0);
1395
if (!new_logline(EVLOG_ALERT, flags, &args, evlog, &lbuf))
1396
goto done;
1397
1398
if (extra != NULL) {
1399
/* Each extra message is written on its own line. */
1400
while (*extra != NULL) {
1401
sudo_lbuf_append(&lbuf, "\n");
1402
sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "%s", *extra);
1403
if (sudo_lbuf_error(&lbuf)) {
1404
sudo_debug_printf(
1405
SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
1406
"unable to format mail message");
1407
goto done;
1408
}
1409
extra++;
1410
}
1411
}
1412
1413
ret = send_mail(evlog, lbuf.buf);
1414
if (!ret) {
1415
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1416
"unable to mail log line");
1417
}
1418
1419
done:
1420
sudo_lbuf_destroy(&lbuf);
1421
debug_return_bool(ret);
1422
}
1423
1424
bool
1425
eventlog_exit(const struct eventlog *evlog, int flags)
1426
{
1427
const struct eventlog_config *evl_conf = eventlog_getconf();
1428
const int log_type = evl_conf->type;
1429
struct eventlog_args args = { NULL };
1430
struct timespec exit_time;
1431
bool ret = true;
1432
debug_decl(eventlog_exit, SUDO_DEBUG_UTIL);
1433
1434
/* We expect evlog->event_time to be the command start time. */
1435
if (sudo_timespecisset(&evlog->run_time)) {
1436
sudo_timespecadd(&evlog->event_time, &evlog->run_time, &exit_time);
1437
args.event_time = &exit_time;
1438
}
1439
1440
if (ISSET(log_type, EVLOG_SYSLOG)) {
1441
if (!do_syslog(EVLOG_EXIT, flags, &args, evlog))
1442
ret = false;
1443
CLR(flags, EVLOG_MAIL);
1444
}
1445
if (ISSET(log_type, EVLOG_FILE)) {
1446
if (!do_logfile(EVLOG_EXIT, flags, &args, evlog))
1447
ret = false;
1448
}
1449
1450
debug_return_bool(ret);
1451
}
1452
1453