Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/logsrvd/logsrvd_local.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2019-2025 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/types.h>
23
#include <sys/socket.h>
24
#include <sys/uio.h>
25
#include <netinet/in.h>
26
#include <netinet/tcp.h>
27
#include <arpa/inet.h>
28
29
#include <errno.h>
30
#include <fcntl.h>
31
#include <limits.h>
32
#ifdef HAVE_STDBOOL_H
33
# include <stdbool.h>
34
#else
35
# include <compat/stdbool.h>
36
#endif /* HAVE_STDBOOL_H */
37
#if defined(HAVE_STDINT_H)
38
# include <stdint.h>
39
#elif defined(HAVE_INTTYPES_H)
40
# include <inttypes.h>
41
#endif
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include <time.h>
46
#include <unistd.h>
47
48
#include <sudo_compat.h>
49
#include <sudo_conf.h>
50
#include <sudo_debug.h>
51
#include <sudo_event.h>
52
#include <sudo_eventlog.h>
53
#include <sudo_fatal.h>
54
#include <sudo_gettext.h>
55
#include <sudo_json.h>
56
#include <sudo_iolog.h>
57
#include <sudo_rand.h>
58
#include <sudo_util.h>
59
60
#include <logsrvd.h>
61
62
struct logsrvd_info_closure {
63
InfoMessage **info_msgs;
64
size_t infolen;
65
};
66
67
static bool
68
logsrvd_json_log_cb(struct json_container *jsonc, void *v)
69
{
70
struct logsrvd_info_closure *closure = v;
71
struct json_value json_value;
72
size_t idx;
73
debug_decl(logsrvd_json_log_cb, SUDO_DEBUG_UTIL);
74
75
for (idx = 0; idx < closure->infolen; idx++) {
76
InfoMessage *info = closure->info_msgs[idx];
77
78
switch (info->value_case) {
79
case INFO_MESSAGE__VALUE_NUMVAL:
80
json_value.type = JSON_NUMBER;
81
json_value.u.number = info->u.numval;
82
if (!sudo_json_add_value(jsonc, info->key, &json_value))
83
goto bad;
84
break;
85
case INFO_MESSAGE__VALUE_STRVAL:
86
if (info->u.strval == NULL) {
87
sudo_warnx(U_("%s: protocol error: NULL value found in %s"),
88
"local", info->key);
89
break;
90
}
91
json_value.type = JSON_STRING;
92
json_value.u.string = info->u.strval;
93
if (!sudo_json_add_value(jsonc, info->key, &json_value))
94
goto bad;
95
break;
96
case INFO_MESSAGE__VALUE_STRLISTVAL: {
97
InfoMessage__StringList *strlist = info->u.strlistval;
98
size_t n;
99
100
if (strlist == NULL) {
101
sudo_warnx(U_("%s: protocol error: NULL value found in %s"),
102
"local", info->key);
103
break;
104
}
105
if (!sudo_json_open_array(jsonc, info->key))
106
goto bad;
107
for (n = 0; n < strlist->n_strings; n++) {
108
if (strlist->strings[n] == NULL) {
109
sudo_warnx(U_("%s: protocol error: NULL value found in %s"),
110
"local", info->key);
111
break;
112
}
113
json_value.type = JSON_STRING;
114
json_value.u.string = strlist->strings[n];
115
if (!sudo_json_add_value(jsonc, NULL, &json_value))
116
goto bad;
117
}
118
if (!sudo_json_close_array(jsonc))
119
goto bad;
120
break;
121
}
122
case INFO_MESSAGE__VALUE_NUMLISTVAL: {
123
InfoMessage__NumberList *numlist = info->u.numlistval;
124
size_t n;
125
126
if (numlist == NULL) {
127
sudo_warnx(U_("%s: protocol error: NULL value found in %s"),
128
"local", info->key);
129
break;
130
}
131
if (!sudo_json_open_array(jsonc, info->key))
132
goto bad;
133
for (n = 0; n < numlist->n_numbers; n++) {
134
json_value.type = JSON_NUMBER;
135
json_value.u.number = numlist->numbers[n];
136
if (!sudo_json_add_value(jsonc, NULL, &json_value))
137
goto bad;
138
}
139
if (!sudo_json_close_array(jsonc))
140
goto bad;
141
break;
142
}
143
default:
144
sudo_warnx(U_("unexpected value_case %d in %s from %s"),
145
info->value_case, "InfoMessage", "local");
146
break;
147
}
148
}
149
debug_return_bool(true);
150
bad:
151
debug_return_bool(false);
152
}
153
154
/*
155
* Parse and store an AcceptMessage locally.
156
*/
157
bool
158
store_accept_local(const AcceptMessage *msg, const uint8_t *buf, size_t len,
159
struct connection_closure *closure)
160
{
161
struct logsrvd_info_closure info = { msg->info_msgs, msg->n_info_msgs };
162
bool new_session = closure->evlog == NULL;
163
struct eventlog *evlog = NULL;
164
bool ret = false;
165
debug_decl(store_accept_local, SUDO_DEBUG_UTIL);
166
167
/* Store sudo-style event and I/O logs. */
168
evlog = evlog_new(msg->submit_time, msg->info_msgs, msg->n_info_msgs,
169
closure);
170
if (evlog == NULL) {
171
closure->errstr = _("error parsing AcceptMessage");
172
goto done;
173
}
174
175
/* Additional setup for the initial command in the session. */
176
if (new_session) {
177
closure->evlog = evlog;
178
179
/* Create I/O log info file and parent directories. */
180
if (msg->expect_iobufs) {
181
if (!iolog_init(msg, closure)) {
182
closure->errstr = _("error creating I/O log");
183
goto done;
184
}
185
closure->log_io = true;
186
}
187
} else if (closure->log_io) {
188
/* Sub-command from an existing session, set iolog and offset. */
189
free(evlog->iolog_path);
190
evlog->iolog_path = strdup(closure->evlog->iolog_path);
191
if (evlog->iolog_path == NULL) {
192
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
193
closure->errstr = _("unable to allocate memory");
194
goto done;
195
}
196
if (closure->evlog->iolog_file != NULL) {
197
evlog->iolog_file = evlog->iolog_path +
198
(closure->evlog->iolog_file - closure->evlog->iolog_path);
199
}
200
sudo_timespecsub(&evlog->event_time, &closure->evlog->event_time,
201
&evlog->iolog_offset);
202
}
203
204
if (!eventlog_accept(evlog, 0, logsrvd_json_log_cb, &info)) {
205
closure->errstr = _("error logging accept event");
206
goto done;
207
}
208
209
if (new_session && closure->log_io) {
210
/*
211
* Send log ID to client for restarting connections.
212
* Note that iolog_base has no trailing '/'.
213
*/
214
const char *relative_path = closure->evlog->iolog_path +
215
strlen(logsrvd_conf_iolog_base()) + 1;
216
if (!fmt_log_id_message(closure->uuid, relative_path, closure))
217
goto done;
218
if (sudo_ev_add(closure->evbase, closure->write_ev,
219
logsrvd_conf_server_timeout(), false) == -1) {
220
sudo_warnx("%s", U_("unable to add event to queue"));
221
goto done;
222
}
223
}
224
225
ret = true;
226
227
done:
228
if (closure->evlog != evlog)
229
eventlog_free(evlog);
230
231
debug_return_bool(ret);
232
}
233
234
/*
235
* Parse and store a RejectMessage locally.
236
*/
237
bool
238
store_reject_local(const RejectMessage *msg, const uint8_t *buf, size_t len,
239
struct connection_closure *closure)
240
{
241
struct logsrvd_info_closure info = { msg->info_msgs, msg->n_info_msgs };
242
struct eventlog *evlog = NULL;
243
bool ret = false;
244
debug_decl(store_reject_local, SUDO_DEBUG_UTIL);
245
246
evlog = evlog_new(msg->submit_time, msg->info_msgs, msg->n_info_msgs,
247
closure);
248
if (evlog == NULL) {
249
closure->errstr = _("error parsing RejectMessage");
250
goto done;
251
}
252
253
if (closure->evlog == NULL) {
254
/* Initial command in session. */
255
closure->evlog = evlog;
256
} else if (closure->log_io) {
257
/* Sub-command from an existing session, set iolog and offset. */
258
free(evlog->iolog_path);
259
evlog->iolog_path = strdup(closure->evlog->iolog_path);
260
if (evlog->iolog_path == NULL) {
261
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
262
closure->errstr = _("unable to allocate memory");
263
goto done;
264
}
265
if (closure->evlog->iolog_file != NULL) {
266
evlog->iolog_file = evlog->iolog_path +
267
(closure->evlog->iolog_file - closure->evlog->iolog_path);
268
}
269
sudo_timespecsub(&evlog->event_time, &closure->evlog->event_time,
270
&evlog->iolog_offset);
271
}
272
273
if (!eventlog_reject(evlog, 0, msg->reason, logsrvd_json_log_cb, &info)) {
274
closure->errstr = _("error logging reject event");
275
goto done;
276
}
277
278
ret = true;
279
280
done:
281
if (closure->evlog != evlog)
282
eventlog_free(evlog);
283
284
debug_return_bool(ret);
285
}
286
287
static bool
288
store_exit_info_json(int dfd, const struct eventlog *evlog)
289
{
290
struct json_container jsonc = { 0 };
291
struct json_value json_value;
292
struct iovec iov[3];
293
bool ret = false;
294
int fd = -1;
295
off_t pos;
296
debug_decl(store_exit_info_json, SUDO_DEBUG_UTIL);
297
298
if (!sudo_json_init(&jsonc, 4, false, false, false))
299
goto done;
300
301
fd = iolog_openat(dfd, "log.json", O_RDWR);
302
if (fd == -1) {
303
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
304
"unable to open to %s/log.json", evlog->iolog_path);
305
if (errno == ENOENT) {
306
/* Ignore missing log.json file. */
307
ret = true;
308
}
309
goto done;
310
}
311
312
if (sudo_timespecisset(&evlog->run_time)) {
313
if (!sudo_json_open_object(&jsonc, "run_time"))
314
goto done;
315
316
json_value.type = JSON_NUMBER;
317
json_value.u.number = evlog->run_time.tv_sec;
318
if (!sudo_json_add_value(&jsonc, "seconds", &json_value))
319
goto done;
320
321
json_value.type = JSON_NUMBER;
322
json_value.u.number = evlog->run_time.tv_nsec;
323
if (!sudo_json_add_value(&jsonc, "nanoseconds", &json_value))
324
goto done;
325
326
if (!sudo_json_close_object(&jsonc))
327
goto done;
328
}
329
330
if (evlog->signal_name != NULL) {
331
json_value.type = JSON_STRING;
332
json_value.u.string = evlog->signal_name;
333
if (!sudo_json_add_value(&jsonc, "signal", &json_value))
334
goto done;
335
336
json_value.type = JSON_BOOL;
337
json_value.u.boolean = evlog->dumped_core;
338
if (!sudo_json_add_value(&jsonc, "dumped_core", &json_value))
339
goto done;
340
}
341
342
json_value.type = JSON_NUMBER;
343
json_value.u.number = evlog->exit_value;
344
if (!sudo_json_add_value(&jsonc, "exit_value", &json_value))
345
goto done;
346
347
/* Back up to overwrite the final "\n}\n" */
348
pos = lseek(fd, -3, SEEK_END);
349
if (pos == -1) {
350
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
351
"unable to rewind %s/log.json 3 bytes", evlog->iolog_path);
352
goto done;
353
}
354
355
/* Append the exit data and close the object. */
356
iov[0].iov_base = (char *)",";
357
iov[0].iov_len = 1;
358
iov[1].iov_base = sudo_json_get_buf(&jsonc);
359
iov[1].iov_len = sudo_json_get_len(&jsonc);
360
iov[2].iov_base = (char *)"\n}\n";
361
iov[2].iov_len = 3;
362
if (writev(fd, iov, 3) < 0) {
363
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
364
"unable to write %s/log.json", evlog->iolog_path);
365
/* Back up and try to restore to original state. */
366
if (lseek(fd, pos, SEEK_SET) != -1) {
367
ignore_result(write(fd, "\n}\n", 3));
368
}
369
goto done;
370
}
371
372
ret = true;
373
374
done:
375
if (fd != -1)
376
close(fd);
377
sudo_json_free(&jsonc);
378
debug_return_bool(ret);
379
}
380
381
bool
382
store_exit_local(const ExitMessage *msg, const uint8_t *buf, size_t len,
383
struct connection_closure *closure)
384
{
385
struct eventlog *evlog = closure->evlog;
386
int flags = 0;
387
debug_decl(store_exit_local, SUDO_DEBUG_UTIL);
388
389
/* TODO: store the "error" string if present. */
390
if (msg->run_time != NULL) {
391
evlog->run_time.tv_sec = (time_t)msg->run_time->tv_sec;
392
evlog->run_time.tv_nsec = (long)msg->run_time->tv_nsec;
393
}
394
evlog->exit_value = msg->exit_value;
395
if (msg->signal[0] != '\0') {
396
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
397
"command was killed by SIG%s%s", msg->signal,
398
msg->dumped_core ? " (core dumped)" : "");
399
free(evlog->signal_name);
400
evlog->signal_name = strdup(msg->signal);
401
if (evlog->signal_name == NULL) {
402
closure->errstr = _("unable to allocate memory");
403
debug_return_bool(false);
404
}
405
evlog->dumped_core = msg->dumped_core;
406
} else {
407
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
408
"command exited with %d", msg->exit_value);
409
}
410
if (logsrvd_conf_log_exit()) {
411
if (!eventlog_exit(evlog, flags)) {
412
closure->errstr = _("error logging exit event");
413
debug_return_bool(false);
414
}
415
}
416
417
if (closure->log_io) {
418
mode_t mode;
419
420
/* Store the run time and exit status in log.json. */
421
if (!store_exit_info_json(closure->iolog_dir_fd, evlog)) {
422
closure->errstr = _("error logging exit event");
423
debug_return_bool(false);
424
}
425
426
/* Clear write bits from I/O timing file to indicate completion. */
427
mode = logsrvd_conf_iolog_mode();
428
CLR(mode, S_IWUSR|S_IWGRP|S_IWOTH);
429
if (fchmodat(closure->iolog_dir_fd, "timing", mode, 0) == -1) {
430
sudo_warn("chmod 0%o %s/%s", (unsigned int)mode, "timing",
431
logsrvd_conf_iolog_dir());
432
}
433
}
434
435
debug_return_bool(true);
436
}
437
438
/*
439
* Decode a base64 log ID by and split it into a UUID and a path.
440
* The path must not contain ".." elements.
441
*/
442
static char *
443
decode_log_id(const char *b64_log_id, unsigned char uuid[restrict static 16])
444
{
445
unsigned char log_id_buf[PATH_MAX + 16];
446
char *path, *ret;
447
size_t len;
448
debug_decl(decode_log_id, SUDO_DEBUG_UTIL);
449
450
/* The log ID is base64-encoded; path has no NUL so leave room for one. */
451
len = sudo_base64_decode(b64_log_id, log_id_buf, sizeof(log_id_buf) - 1);
452
if (len == (size_t)-1)
453
debug_return_str(NULL);
454
log_id_buf[len] = '\0';
455
456
/* A log ID consists of a 16-byte UUID followed by a path. */
457
if (len <= 16)
458
debug_return_str(NULL);
459
memcpy(uuid, log_id_buf, 16);
460
if (memchr(&log_id_buf[16], '\0', len - 16) != NULL) {
461
sudo_warnx("%s", U_("RestartMessage log_id path has embedded NUL"));
462
debug_return_str(NULL);
463
}
464
path = (char *)&log_id_buf[16];
465
if (contains_dot_dot(path)) {
466
sudo_warnx("%s", U_("RestartMessage log_id path traversal attack"));
467
debug_return_str(NULL);
468
}
469
470
/* The log_id path is relative to iolog_base. */
471
if (asprintf(&ret, "%s/%s", logsrvd_conf_iolog_base(), path) == -1) {
472
ret = NULL;
473
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
474
}
475
debug_return_str(ret);
476
}
477
478
/*
479
* Verify the specified uuid against the uuid file in the I/O log directory.
480
*/
481
static bool
482
verify_iolog_uuid(int dfd, const unsigned char uuid[restrict static 16])
483
{
484
unsigned char uuid2[16];
485
char uuid2_str[37];
486
bool ret = false;
487
struct stat sb;
488
int fd;
489
debug_decl(verify_iolog_uuid, SUDO_DEBUG_UTIL);
490
491
/* Read in the I/O log uuid (string form), which must be 36-bytes. */
492
fd = openat(dfd, "uuid", O_RDONLY);
493
if (fd == -1)
494
goto done;
495
if (fstat(fd, &sb) == -1 || sb.st_size != 36)
496
goto done;
497
if (read(fd, uuid2_str, sizeof(uuid2_str)) != 36)
498
goto done;
499
uuid2_str[36] = '\0';
500
501
/* Convert I/O log uuid to binary and compare. */
502
if (sudo_uuid_from_string(uuid2_str, uuid2) != 0)
503
goto done;
504
if (memcmp(uuid, uuid2, sizeof(uuid2)) != 0)
505
goto done;
506
507
ret = true;
508
509
done:
510
if (fd != -1) {
511
/* Invalid uuid, not file error. */
512
errno = EINVAL;
513
close(fd);
514
}
515
debug_return_bool(ret);
516
}
517
518
bool
519
store_restart_local(const RestartMessage *msg, const uint8_t *buf, size_t len,
520
struct connection_closure *closure)
521
{
522
struct timespec target;
523
struct stat sb;
524
int iofd;
525
debug_decl(store_restart_local, SUDO_DEBUG_UTIL);
526
527
target.tv_sec = (time_t)msg->resume_point->tv_sec;
528
target.tv_nsec = (long)msg->resume_point->tv_nsec;
529
530
/* We must allocate closure->evlog for iolog_path. */
531
closure->evlog = calloc(1, sizeof(*closure->evlog));
532
if (closure->evlog == NULL) {
533
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
534
closure->errstr = _("unable to allocate memory");
535
goto bad;
536
}
537
538
/* Parse log_id into a uuid and a path. */
539
closure->evlog->iolog_path = decode_log_id(msg->log_id, closure->uuid);
540
if (closure->evlog->iolog_path == NULL) {
541
sudo_warnx(U_("%s: %s"), __func__, U_("unable to parse log_id"));
542
closure->errstr = _("unable to parse log_id");
543
goto bad;
544
}
545
546
/* We use iolog_dir_fd in calls to openat(2) */
547
closure->iolog_dir_fd = iolog_openat(AT_FDCWD,
548
closure->evlog->iolog_path, O_RDONLY|O_DIRECTORY);
549
if (closure->iolog_dir_fd == -1) {
550
sudo_warn("%s", closure->evlog->iolog_path);
551
goto bad;
552
}
553
554
/* Verify the log ID uuid against the actual I/O log uuid file. */
555
if (!verify_iolog_uuid(closure->iolog_dir_fd, closure->uuid)) {
556
sudo_warn("%s/uuid", closure->evlog->iolog_path);
557
goto bad;
558
}
559
560
/* If the timing file write bit is clear, log is already complete. */
561
if (fstatat(closure->iolog_dir_fd, "timing", &sb, 0) == -1) {
562
sudo_warn("%s/timing", closure->evlog->iolog_path);
563
goto bad;
564
}
565
if (!ISSET(sb.st_mode, S_IWUSR)) {
566
sudo_warn(U_("%s: %s"), closure->evlog->iolog_path,
567
U_("log is already complete, cannot be restarted"));
568
closure->errstr = _("log is already complete, cannot be restarted");
569
goto bad;
570
}
571
572
/* Open existing I/O log files. */
573
if (!iolog_open_all(closure->iolog_dir_fd, closure->evlog->iolog_path,
574
closure->iolog_files, "r+"))
575
goto bad;
576
577
/* Compressed logs don't support random access, so rewrite them. */
578
for (iofd = 0; iofd < IOFD_MAX; iofd++) {
579
if (closure->iolog_files[iofd].compressed)
580
debug_return_bool(iolog_rewrite(&target, closure));
581
}
582
583
/* Parse timing file until we reach the target point. */
584
if (!iolog_seekto(closure->iolog_dir_fd, closure->evlog->iolog_path,
585
closure->iolog_files, &closure->elapsed_time, &target))
586
goto bad;
587
588
/* Must seek or flush before switching from read -> write. */
589
if (iolog_seek(&closure->iolog_files[IOFD_TIMING], 0, SEEK_CUR) == -1) {
590
sudo_warn("%s/timing", closure->evlog->iolog_path);
591
goto bad;
592
}
593
594
/* Ready to log I/O buffers. */
595
debug_return_bool(true);
596
bad:
597
if (closure->errstr == NULL)
598
closure->errstr = _("unable to restart log");
599
debug_return_bool(false);
600
}
601
602
bool
603
store_alert_local(const AlertMessage *msg, const uint8_t *buf, size_t len,
604
struct connection_closure *closure)
605
{
606
struct eventlog *evlog = NULL;
607
struct timespec alert_time;
608
bool ret = false;
609
debug_decl(store_alert_local, SUDO_DEBUG_UTIL);
610
611
if (msg->info_msgs != NULL && msg->n_info_msgs != 0) {
612
evlog = evlog_new(NULL, msg->info_msgs, msg->n_info_msgs, closure);
613
if (evlog == NULL) {
614
closure->errstr = _("error parsing AlertMessage");
615
goto done;
616
}
617
if (closure->evlog == NULL)
618
closure->evlog = evlog;
619
}
620
alert_time.tv_sec = (time_t)msg->alert_time->tv_sec;
621
alert_time.tv_nsec = (long)msg->alert_time->tv_nsec;
622
623
if (!eventlog_alert(evlog, 0, &alert_time, msg->reason, NULL)) {
624
closure->errstr = _("error logging alert event");
625
goto done;
626
}
627
628
ret = true;
629
630
done:
631
if (closure->evlog != evlog)
632
eventlog_free(evlog);
633
634
debug_return_bool(ret);
635
}
636
637
bool
638
store_iobuf_local(int iofd, const IoBuffer *iobuf, const uint8_t *buf,
639
size_t buflen, struct connection_closure *closure)
640
{
641
const struct eventlog *evlog = closure->evlog;
642
struct ProtobufCBinaryData data = iobuf->data;
643
char tbuf[1024], *newbuf = NULL;
644
const char *errstr;
645
int len;
646
debug_decl(store_iobuf_local, SUDO_DEBUG_UTIL);
647
648
/* Open log file as needed. */
649
if (!closure->iolog_files[iofd].enabled) {
650
if (!iolog_create(iofd, closure))
651
goto bad;
652
}
653
654
/* Format timing data. */
655
/* FIXME - assumes IOFD_* matches IO_EVENT_* */
656
len = snprintf(tbuf, sizeof(tbuf), "%d %lld.%09d %zu\n",
657
iofd, (long long)iobuf->delay->tv_sec, (int)iobuf->delay->tv_nsec,
658
data.len);
659
if (len < 0 || len >= ssizeof(tbuf)) {
660
sudo_warnx(U_("unable to format timing buffer, length %d"), len);
661
goto bad;
662
}
663
664
if (!logsrvd_conf_iolog_log_passwords()) {
665
if (!iolog_pwfilt_run(logsrvd_conf_iolog_passprompt_regex(), iofd,
666
(char *)data.data, data.len, &newbuf))
667
goto bad;
668
if (newbuf != NULL)
669
data.data = (uint8_t *)newbuf;
670
}
671
672
/* Write to specified I/O log file. */
673
if (iolog_write(&closure->iolog_files[iofd], data.data, data.len,
674
&errstr) == -1) {
675
sudo_warnx(U_("%s/%s: %s"), evlog->iolog_path, iolog_fd_to_name(iofd),
676
errstr);
677
goto bad;
678
}
679
680
/* Write timing data. */
681
if (iolog_write(&closure->iolog_files[IOFD_TIMING], tbuf,
682
(size_t)len, &errstr) == -1) {
683
sudo_warnx(U_("%s/%s: %s"), evlog->iolog_path,
684
iolog_fd_to_name(IOFD_TIMING), errstr);
685
goto bad;
686
}
687
688
update_elapsed_time(iobuf->delay, &closure->elapsed_time);
689
690
free(newbuf);
691
debug_return_bool(true);
692
bad:
693
free(newbuf);
694
if (closure->errstr == NULL)
695
closure->errstr = _("error writing IoBuffer");
696
debug_return_bool(false);
697
}
698
699
bool
700
store_winsize_local(const ChangeWindowSize *msg, const uint8_t *buf,
701
size_t buflen, struct connection_closure *closure)
702
{
703
const char *errstr;
704
char tbuf[1024];
705
int len;
706
debug_decl(store_winsize_local, SUDO_DEBUG_UTIL);
707
708
/* Format timing data including new window size. */
709
len = snprintf(tbuf, sizeof(tbuf), "%d %lld.%09d %d %d\n", IO_EVENT_WINSIZE,
710
(long long)msg->delay->tv_sec, (int)msg->delay->tv_nsec,
711
msg->rows, msg->cols);
712
if (len < 0 || len >= ssizeof(tbuf)) {
713
sudo_warnx(U_("unable to format timing buffer, length %d"), len);
714
goto bad;
715
}
716
717
/* Write timing data. */
718
if (iolog_write(&closure->iolog_files[IOFD_TIMING], tbuf,
719
(size_t)len, &errstr) == -1) {
720
sudo_warnx(U_("%s/%s: %s"), closure->evlog->iolog_path,
721
iolog_fd_to_name(IOFD_TIMING), errstr);
722
goto bad;
723
}
724
725
update_elapsed_time(msg->delay, &closure->elapsed_time);
726
727
debug_return_bool(true);
728
bad:
729
if (closure->errstr == NULL)
730
closure->errstr = _("error writing ChangeWindowSize");
731
debug_return_bool(false);
732
}
733
734
bool
735
store_suspend_local(const CommandSuspend *msg, const uint8_t *buf,
736
size_t buflen, struct connection_closure *closure)
737
{
738
const char *errstr;
739
char tbuf[1024];
740
int len;
741
debug_decl(store_suspend_local, SUDO_DEBUG_UTIL);
742
743
/* Format timing data including suspend signal. */
744
len = snprintf(tbuf, sizeof(tbuf), "%d %lld.%09d %s\n", IO_EVENT_SUSPEND,
745
(long long)msg->delay->tv_sec, (int)msg->delay->tv_nsec,
746
msg->signal);
747
if (len < 0 || len >= ssizeof(tbuf)) {
748
sudo_warnx(U_("unable to format timing buffer, length %d"), len);
749
goto bad;
750
}
751
752
/* Write timing data. */
753
if (iolog_write(&closure->iolog_files[IOFD_TIMING], tbuf,
754
(size_t)len, &errstr) == -1) {
755
sudo_warnx(U_("%s/%s: %s"), closure->evlog->iolog_path,
756
iolog_fd_to_name(IOFD_TIMING), errstr);
757
goto bad;
758
}
759
760
update_elapsed_time(msg->delay, &closure->elapsed_time);
761
762
debug_return_bool(true);
763
bad:
764
if (closure->errstr == NULL)
765
closure->errstr = _("error writing CommandSuspend");
766
debug_return_bool(false);
767
}
768
769
struct client_message_switch cms_local = {
770
store_accept_local,
771
store_reject_local,
772
store_exit_local,
773
store_restart_local,
774
store_alert_local,
775
store_iobuf_local,
776
store_suspend_local,
777
store_winsize_local
778
};
779
780