2
0
mirror of https://github.com/sudo-project/sudo.git synced 2025-08-22 01:49:11 +00:00

Journal messages to disk when store_first is set in the relay section.

Instead of forwarding messages immediately, they are journaled
locally in wire format.
This will be used to implement relay store-and-forward mode.
This commit is contained in:
Todd C. Miller 2021-04-23 16:55:30 -06:00
parent 6f5b353e87
commit c2d3070fa1
8 changed files with 549 additions and 104 deletions

View File

@ -349,6 +349,7 @@ logsrvd/logsrv_util.h
logsrvd/logsrvd.c
logsrvd/logsrvd.h
logsrvd/logsrvd_conf.c
logsrvd/logsrvd_journal.c
logsrvd/logsrvd_relay.c
logsrvd/regress/corpus/seed/logsrvd_conf/logsrvd.conf.1
logsrvd/regress/corpus/seed/logsrvd_conf/logsrvd.conf.2

View File

@ -120,7 +120,7 @@ SHELL = @SHELL@
PROGS = sudo_logsrvd sudo_sendlog
LOGSRVD_OBJS = logsrv_util.o iolog_writer.o logsrvd.o logsrvd_conf.o \
logsrvd_relay.o tls_client.o tls_init.o
logsrvd_journal.o logsrvd_relay.o tls_client.o tls_init.o
SENDLOG_OBJS = logsrv_util.o sendlog.o tls_client.o tls_init.o
@ -383,27 +383,45 @@ logsrvd_conf.i: $(srcdir)/logsrvd_conf.c $(incdir)/compat/getaddrinfo.h \
$(CC) -E -o $@ $(CPPFLAGS) $<
logsrvd_conf.plog: logsrvd_conf.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/logsrvd_conf.c --i-file $< --output-file $@
logsrvd_relay.o: $(srcdir)/logsrvd_relay.c $(incdir)/compat/getopt.h \
$(incdir)/compat/stdbool.h $(incdir)/log_server.pb-c.h \
logsrvd_journal.o: $(srcdir)/logsrvd_journal.c $(incdir)/compat/stdbool.h \
$(incdir)/log_server.pb-c.h \
$(incdir)/protobuf-c/protobuf-c.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_event.h $(incdir)/sudo_eventlog.h \
$(incdir)/sudo_gettext.h $(incdir)/sudo_iolog.h \
$(incdir)/sudo_json.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_eventlog.h $(incdir)/sudo_gettext.h \
$(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/logsrv_util.h \
$(srcdir)/logsrvd.h $(srcdir)/tls_common.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
$(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/logsrvd_journal.c
logsrvd_journal.i: $(srcdir)/logsrvd_journal.c $(incdir)/compat/stdbool.h \
$(incdir)/log_server.pb-c.h \
$(incdir)/protobuf-c/protobuf-c.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_eventlog.h $(incdir)/sudo_gettext.h \
$(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/logsrv_util.h \
$(srcdir)/logsrvd.h $(srcdir)/tls_common.h \
$(top_builddir)/config.h
$(CC) -E -o $@ $(CPPFLAGS) $<
logsrvd_journal.plog: logsrvd_journal.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/logsrvd_journal.c --i-file $< --output-file $@
logsrvd_relay.o: $(srcdir)/logsrvd_relay.c $(incdir)/compat/stdbool.h \
$(incdir)/log_server.pb-c.h $(incdir)/protobuf-c/protobuf-c.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_event.h $(incdir)/sudo_eventlog.h \
$(incdir)/sudo_gettext.h $(incdir)/sudo_iolog.h \
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
$(srcdir)/logsrv_util.h $(srcdir)/logsrvd.h \
$(srcdir)/tls_common.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/logsrvd_relay.c
logsrvd_relay.i: $(srcdir)/logsrvd_relay.c $(incdir)/compat/getopt.h \
$(incdir)/compat/stdbool.h $(incdir)/log_server.pb-c.h \
$(incdir)/protobuf-c/protobuf-c.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
logsrvd_relay.i: $(srcdir)/logsrvd_relay.c $(incdir)/compat/stdbool.h \
$(incdir)/log_server.pb-c.h $(incdir)/protobuf-c/protobuf-c.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_event.h $(incdir)/sudo_eventlog.h \
$(incdir)/sudo_gettext.h $(incdir)/sudo_iolog.h \
$(incdir)/sudo_json.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/logsrv_util.h \
$(srcdir)/logsrvd.h $(srcdir)/tls_common.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
$(srcdir)/logsrv_util.h $(srcdir)/logsrvd.h \
$(srcdir)/tls_common.h $(top_builddir)/config.h
$(CC) -E -o $@ $(CPPFLAGS) $<
logsrvd_relay.plog: logsrvd_relay.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/logsrvd_relay.c --i-file $< --output-file $@

View File

@ -982,7 +982,7 @@ bad:
* Add given delta to elapsed time.
* We cannot use timespecadd here since delta is not struct timespec.
*/
static void
void
update_elapsed_time(TimeSpec *delta, struct timespec *elapsed)
{
debug_decl(update_elapsed_time, SUDO_DEBUG_UTIL);

View File

@ -132,6 +132,9 @@ connection_closure_free(struct connection_closure *closure)
free(buf->data);
free(buf);
}
free(closure->journal_path);
if (closure->journal != NULL)
fclose(closure->journal);
free(closure);
if (shutting_down && TAILQ_EMPTY(&connections))
@ -313,6 +316,7 @@ static bool
handle_accept(AcceptMessage *msg, uint8_t *buf, size_t len,
struct connection_closure *closure)
{
char *log_id = NULL;
struct logsrvd_info_closure info = { msg->info_msgs, msg->n_info_msgs };
debug_decl(handle_accept, SUDO_DEBUG_UTIL);
@ -338,6 +342,18 @@ handle_accept(AcceptMessage *msg, uint8_t *buf, size_t len,
debug_return_bool(relay_accept(msg, buf, len, closure));
}
if (logsrvd_conf_relay_store_first()) {
/* Store message in a journal for later relaying. */
if (!journal_open(closure))
debug_return_bool(false);
if (!journal_write(buf, len, closure))
debug_return_bool(false);
if (msg->expect_iobufs) {
closure->log_io = true;
log_id = closure->journal_path;
}
} else {
/* Store sudo-style event and I/O logs. */
closure->evlog = evlog_new(msg->submit_time, msg->info_msgs,
msg->n_info_msgs, closure);
if (closure->evlog == NULL) {
@ -352,16 +368,18 @@ handle_accept(AcceptMessage *msg, uint8_t *buf, size_t len,
debug_return_bool(false);
}
closure->log_io = true;
log_id = closure->evlog->iolog_path;
}
if (!eventlog_accept(closure->evlog, 0, logsrvd_json_log_cb, &info)) {
closure->errstr = _("error logging accept event");
debug_return_bool(false);
}
}
if (msg->expect_iobufs) {
if (log_id != NULL) {
/* Send log ID to client for restarting connections. */
if (!fmt_log_id_message(closure->evlog->iolog_path, closure))
if (!fmt_log_id_message(log_id, closure))
debug_return_bool(false);
if (sudo_ev_add(closure->evbase, closure->write_ev,
logsrvd_conf_server_timeout(), false) == -1) {
@ -407,6 +425,13 @@ handle_reject(RejectMessage *msg, uint8_t *buf, size_t len,
debug_return_bool(relay_reject(msg, buf, len, closure));
}
if (logsrvd_conf_relay_store_first()) {
/* Store message in a journal for later relaying. */
if (!journal_open(closure))
debug_return_bool(false);
if (!journal_write(buf, len, closure))
debug_return_bool(false);
} else {
closure->evlog = evlog_new(msg->submit_time, msg->info_msgs,
msg->n_info_msgs, closure);
if (closure->evlog == NULL) {
@ -419,6 +444,7 @@ handle_reject(RejectMessage *msg, uint8_t *buf, size_t len,
closure->errstr = _("error logging reject event");
debug_return_bool(false);
}
}
closure->state = FINISHED;
debug_return_bool(true);
@ -456,6 +482,15 @@ handle_exit(ExitMessage *msg, uint8_t *buf, size_t len,
"command exited with %d", msg->exit_value);
}
if (logsrvd_conf_relay_store_first()) {
/* Store message in a journal for later relaying. */
if (!journal_write(buf, len, closure))
debug_return_bool(false);
if (!journal_finish(closure))
debug_return_bool(false);
/* XXX - schedule relay of journal file */
}
if (closure->log_io) {
/* No more data, command exited. */
closure->state = EXITED;
@ -465,6 +500,7 @@ handle_exit(ExitMessage *msg, uint8_t *buf, size_t len,
__func__, (long long)closure->elapsed_time.tv_sec,
closure->elapsed_time.tv_nsec);
if (closure->iolog_dir_fd != -1) {
/* Clear write bits from I/O timing file to indicate completion. */
mode = logsrvd_conf_iolog_mode();
CLR(mode, S_IWUSR|S_IWGRP|S_IWOTH);
@ -472,6 +508,7 @@ handle_exit(ExitMessage *msg, uint8_t *buf, size_t len,
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to fchmodat timing file");
}
}
/* Schedule the final commit point event immediately. */
if (sudo_ev_add(closure->evbase, closure->commit_ev, &tv, false) == -1) {
@ -491,6 +528,7 @@ static bool
handle_restart(RestartMessage *msg, uint8_t *buf, size_t len,
struct connection_closure *closure)
{
bool restarted;
debug_decl(handle_restart, SUDO_DEBUG_UTIL);
if (closure->state != INITIAL) {
@ -507,7 +545,12 @@ handle_restart(RestartMessage *msg, uint8_t *buf, size_t len,
debug_return_bool(relay_restart(msg, buf, len, closure));
}
if (!iolog_restart(msg, closure)) {
if (logsrvd_conf_relay_store_first()) {
restarted = journal_restart(msg, closure);
} else {
restarted = iolog_restart(msg, closure);
}
if (!restarted) {
sudo_debug_printf(SUDO_DEBUG_WARN, "%s: unable to restart I/O log", __func__);
/* XXX - structured error message so client can send from beginning */
if (!fmt_error_message(closure->errstr, closure))
@ -549,6 +592,11 @@ handle_alert(AlertMessage *msg, uint8_t *buf, size_t len,
debug_return_bool(relay_alert(msg, buf, len, closure));
}
if (logsrvd_conf_relay_store_first()) {
/* Store message in a journal for later relaying. */
if (!journal_write(buf, len, closure))
debug_return_bool(false);
} else {
if (msg->info_msgs != NULL && msg->n_info_msgs != 0) {
closure->evlog = evlog_new(NULL, msg->info_msgs,
msg->n_info_msgs, closure);
@ -564,6 +612,7 @@ handle_alert(AlertMessage *msg, uint8_t *buf, size_t len,
closure->errstr = _("error logging alert event");
debug_return_bool(false);
}
}
debug_return_bool(true);
}
@ -594,6 +643,12 @@ handle_iobuf(int iofd, IoBuffer *iobuf, uint8_t *buf, size_t len,
debug_return_bool(relay_iobuf(iobuf, buf, len, closure));
}
if (logsrvd_conf_relay_store_first()) {
/* Store message in a journal for later relaying. */
if (!journal_write(buf, len, closure))
debug_return_bool(false);
update_elapsed_time(iobuf->delay, &closure->elapsed_time);
} else {
/* Store IoBuffer in log. */
if (store_iobuf(iofd, iobuf, closure) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
@ -611,6 +666,7 @@ handle_iobuf(int iofd, IoBuffer *iobuf, uint8_t *buf, size_t len,
debug_return_bool(false);
}
}
}
/* Schedule a commit point in 10 sec if one is not already pending. */
if (!ISSET(closure->commit_ev->flags, SUDO_EVQ_INSERTED)) {
@ -652,6 +708,11 @@ handle_winsize(ChangeWindowSize *msg, uint8_t *buf, size_t len,
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: received ChangeWindowSize",
__func__);
if (logsrvd_conf_relay_store_first()) {
/* Store message in a journal for later relaying. */
if (!journal_write(buf, len, closure))
debug_return_bool(false);
} else {
/* Store new window size in log. */
if (store_winsize(msg, closure) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
@ -659,6 +720,7 @@ handle_winsize(ChangeWindowSize *msg, uint8_t *buf, size_t len,
closure->errstr = _("error writing ChangeWindowSize");
debug_return_bool(false);
}
}
debug_return_bool(true);
}
@ -690,6 +752,11 @@ handle_suspend(CommandSuspend *msg, uint8_t *buf, size_t len,
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: received CommandSuspend",
__func__);
if (logsrvd_conf_relay_store_first()) {
/* Store message in a journal for later relaying. */
if (!journal_write(buf, len, closure))
debug_return_bool(false);
} else {
/* Store suspend signal in log. */
if (store_suspend(msg, closure) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
@ -697,12 +764,14 @@ handle_suspend(CommandSuspend *msg, uint8_t *buf, size_t len,
closure->errstr = _("error writing CommandSuspend");
debug_return_bool(false);
}
}
debug_return_bool(true);
}
static bool
handle_client_hello(ClientHello *msg, struct connection_closure *closure)
handle_client_hello(ClientHello *msg, uint8_t *buf, size_t len,
struct connection_closure *closure)
{
debug_decl(handle_client_hello, SUDO_DEBUG_UTIL);
@ -775,7 +844,7 @@ handle_client_message(uint8_t *buf, size_t len,
ret = handle_suspend(msg->u.suspend_event, buf, len, closure);
break;
case CLIENT_MESSAGE__TYPE_HELLO_MSG:
ret = handle_client_hello(msg->u.hello_msg, closure);
ret = handle_client_hello(msg->u.hello_msg, buf, len, closure);
break;
default:
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
@ -1344,7 +1413,7 @@ tls_handshake_cb(int fd, int what, void *v)
SSL_get_cipher(closure->ssl));
/* Start the actual protocol now that the TLS handshake is complete. */
if (logsrvd_conf_relay_address() != NULL) {
if (!TAILQ_EMPTY(logsrvd_conf_relay_address()) && !logsrvd_conf_relay_store_first()) {
if (!connect_relay(closure))
goto bad;
} else {
@ -1484,7 +1553,7 @@ new_connection(int sock, bool tls, const struct sockaddr *sa,
#endif
/* If no TLS handshake, start the protocol immediately. */
if (!tls) {
if (logsrvd_conf_relay_address() != NULL) {
if (!TAILQ_EMPTY(logsrvd_conf_relay_address()) && !logsrvd_conf_relay_store_first()) {
if (!connect_relay(closure))
goto bad;
} else {

View File

@ -96,6 +96,8 @@ struct connection_closure {
SSL *ssl;
#endif
const char *errstr;
FILE *journal;
char *journal_path;
struct iolog_file iolog_files[IOFD_MAX];
int iolog_dir_fd;
int sock;
@ -152,6 +154,7 @@ int store_iobuf(int iofd, IoBuffer *msg, struct connection_closure *closure);
int store_suspend(CommandSuspend *msg, struct connection_closure *closure);
int store_winsize(ChangeWindowSize *msg, struct connection_closure *closure);
void iolog_close_all(struct connection_closure *closure);
void update_elapsed_time(TimeSpec *delta, struct timespec *elapsed);
/* logsrvd.c */
bool start_protocol(struct connection_closure *closure);
@ -186,6 +189,12 @@ void address_list_addref(struct server_address_list *);
void address_list_delref(struct server_address_list *);
void logsrvd_conf_cleanup(void);
/* logsrvd_journal.c */
bool journal_open(struct connection_closure *closure);
bool journal_finish(struct connection_closure *closure);
bool journal_write(uint8_t *buf, size_t len, struct connection_closure *closure);
bool journal_restart(RestartMessage *msg, struct connection_closure *closure);
/* logsrvd_relay.c */
void relay_closure_free(struct relay_closure *relay_closure);
bool connect_relay(struct connection_closure *closure);

View File

@ -1399,6 +1399,10 @@ logsrvd_conf_apply(struct logsrvd_config *config)
}
#endif /* HAVE_OPENSSL */
/* Clear store_first if not relaying. */
if (TAILQ_EMPTY(&config->relay.relays.addrs))
config->relay.store_first = false;
/* Open event log if specified. */
switch (config->eventlog.log_type) {
case EVLOG_SYSLOG:

352
logsrvd/logsrvd_journal.c Normal file
View File

@ -0,0 +1,352 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2021 Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif /* HAVE_STDBOOL_H */
#if defined(HAVE_STDINT_H)
# include <stdint.h>
#elif defined(HAVE_INTTYPES_H)
# include <inttypes.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "sudo_compat.h"
#include "sudo_conf.h"
#include "sudo_debug.h"
#include "sudo_gettext.h"
#include "sudo_eventlog.h"
#include "sudo_iolog.h"
#include "sudo_util.h"
#include "log_server.pb-c.h"
#include "logsrvd.h"
/*
* Helper function to set closure->journal and closure->journal_path.
*/
static bool
journal_fdopen(int fd, const char *journal_path,
struct connection_closure *closure)
{
debug_decl(journal_fdopen, SUDO_DEBUG_UTIL);
closure->journal_path = strdup(journal_path);
if (closure->journal_path == NULL) {
closure->errstr = _("unable to allocate memory");
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to allocate memory");
debug_return_bool(false);
}
/* Defer fdopen() until last--it cannot be undone. */
if ((closure->journal = fdopen(fd, "r+")) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to fdopen journal file %s", journal_path);
closure->errstr = _("unable to allocate memory");
debug_return_bool(false);
}
debug_return_bool(true);
}
/*
* Create a temporary file in the relay dir and store it in the closure.
*/
bool
journal_open(struct connection_closure *closure)
{
char journal_path[PATH_MAX];
int fd, len;
debug_decl(journal_open, SUDO_DEBUG_UTIL);
len = snprintf(journal_path, sizeof(journal_path), "%s/relay.XXXXXXXX",
logsrvd_conf_relay_dir());
if (len >= ssizeof(journal_path)) {
errno = ENAMETOOLONG;
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"%s/relay.XXXXXXXX", logsrvd_conf_relay_dir());
debug_return_bool(false);
}
/* TODO: use same escapes as iolog_path? */
if (!sudo_mkdir_parents(journal_path, ROOT_UID, ROOT_GID,
S_IRWXU|S_IXGRP|S_IXOTH, false)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to create parent dir for %s", journal_path);
closure->errstr = _("unable to create journal file");
debug_return_bool(false);
}
if ((fd = mkstemp(journal_path)) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to create journal file %s", journal_path);
closure->errstr = _("unable to create journal file");
debug_return_bool(false);
}
if (!journal_fdopen(fd, journal_path, closure)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to fdopen journal file %s", journal_path);
close(fd);
debug_return_bool(false);
}
debug_return_bool(true);
}
/*
* Flush any buffered data and rewind journal to the beginning.
* The actual open file is closed in connection_closure_free().
*/
bool
journal_finish(struct connection_closure *closure)
{
bool ret;
debug_decl(journal_finish, SUDO_DEBUG_UTIL);
ret = fflush(closure->journal) == 0;
if (!ret)
closure->errstr = _("unable to write journal file");
rewind(closure->journal);
debug_return_bool(ret);
}
/*
* Seek ahead in the journal to the specified target time.
* Returns true if we reached the target time exactly, else false.
*/
static bool
journal_seek(struct timespec *target, struct connection_closure *closure)
{
ClientMessage *msg = NULL;
struct timespec elapsed_time = { 0, 0 };
size_t nread, bufsize = 0;
uint8_t *buf = NULL;
uint32_t msg_len;
bool ret = false;
debug_decl(journal_seek, SUDO_DEBUG_UTIL);
for (;;) {
TimeSpec *delay = NULL;
/* Read message size (uint32_t in network byte order). */
nread = fread(&msg_len, sizeof(msg_len), 1, closure->journal);
if (nread != 1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to read message length from %s", closure->journal_path);
if (feof(closure->journal))
closure->errstr = _("unexpected EOF reading journal file");
else
closure->errstr = _("error reading journal file");
break;
}
msg_len = ntohl(msg_len);
if (msg_len > MESSAGE_SIZE_MAX) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"%s: client message too large %u > %u",
closure->journal_path, msg_len, MESSAGE_SIZE_MAX);
closure->errstr = _("client message too large");
break;
}
if (msg_len > bufsize) {
bufsize = sudo_pow2_roundup(msg_len);
free(buf);
if ((buf = malloc(bufsize)) == NULL) {
closure->errstr = _("unable to allocate memory");
break;
}
}
/* Read actual message now that we know the size. */
nread = fread(buf, msg_len, 1, closure->journal);
if (nread != 1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to read message from %s", closure->journal_path);
if (feof(closure->journal))
closure->errstr = _("unexpected EOF reading journal file");
else
closure->errstr = _("error reading journal file");
break;
}
client_message__free_unpacked(msg, NULL);
msg = client_message__unpack(NULL, msg_len, buf);
if (msg == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to unpack ClientMessage size %u", msg_len);
closure->errstr = _("invalid journal file, unable to restart");
break;
}
switch (msg->type_case) {
case CLIENT_MESSAGE__TYPE_HELLO_MSG:
case CLIENT_MESSAGE__TYPE_ACCEPT_MSG:
case CLIENT_MESSAGE__TYPE_REJECT_MSG:
case CLIENT_MESSAGE__TYPE_EXIT_MSG:
case CLIENT_MESSAGE__TYPE_RESTART_MSG:
case CLIENT_MESSAGE__TYPE_ALERT_MSG:
/* No associated delay. */
break;
case CLIENT_MESSAGE__TYPE_TTYIN_BUF:
delay = msg->u.ttyin_buf->delay;
break;
case CLIENT_MESSAGE__TYPE_TTYOUT_BUF:
delay = msg->u.ttyout_buf->delay;
break;
case CLIENT_MESSAGE__TYPE_STDIN_BUF:
delay = msg->u.stdin_buf->delay;
break;
case CLIENT_MESSAGE__TYPE_STDOUT_BUF:
delay = msg->u.stdout_buf->delay;
break;
case CLIENT_MESSAGE__TYPE_STDERR_BUF:
delay = msg->u.stderr_buf->delay;
break;
case CLIENT_MESSAGE__TYPE_WINSIZE_EVENT:
delay = msg->u.winsize_event->delay;
break;
case CLIENT_MESSAGE__TYPE_SUSPEND_EVENT:
delay = msg->u.suspend_event->delay;
break;
default:
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unexpected type_case value %d", msg->type_case);
break;
}
if (delay != NULL) {
elapsed_time.tv_sec += delay->tv_sec;
elapsed_time.tv_nsec += delay->tv_nsec;
if (elapsed_time.tv_nsec >= 1000000000) {
elapsed_time.tv_sec++;
elapsed_time.tv_nsec -= 1000000000;
}
}
if (timespeccmp(&elapsed_time, target, >=)) {
if (sudo_timespeccmp(&elapsed_time, target, ==)) {
ret = true;
break;
}
/* Mismatch between resume point and stored log. */
closure->errstr = _("invalid journal file, unable to restart");
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to find resume point [%lld, %ld] in %s",
(long long)target->tv_sec, target->tv_nsec,
closure->journal_path);
break;
}
}
client_message__free_unpacked(msg, NULL);
free(buf);
debug_return_bool(ret);
}
/*
* Restart an existing journal.
* Seeks to the resume_point in RestartMessage before continuing.
* Returns true if we reached the target time exactly, else false.
*/
bool
journal_restart(RestartMessage *msg, struct connection_closure *closure)
{
struct timespec target;
int fd, len;
char *cp, journal_path[PATH_MAX];
debug_decl(journal_restart, SUDO_DEBUG_UTIL);
/* Strip off leading hostname from log_id. */
if ((cp = strchr(msg->log_id, '/')) != NULL) {
if (cp != msg->log_id)
cp++;
} else {
cp = msg->log_id;
}
len = snprintf(journal_path, sizeof(journal_path), "%s/%s",
logsrvd_conf_relay_dir(), cp);
if (len >= ssizeof(journal_path)) {
errno = ENAMETOOLONG;
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"%s/%s", logsrvd_conf_relay_dir(), cp);
closure->errstr = _("unable to create journal file");
debug_return_bool(false);
}
if ((fd = open(journal_path, O_RDWR)) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to open journal file %s", journal_path);
closure->errstr = _("unable to create journal file");
debug_return_bool(false);
}
if (!journal_fdopen(fd, journal_path, closure)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to fdopen journal file %s", journal_path);
close(fd);
debug_return_bool(false);
}
/* Seek forward to resume point. */
target.tv_sec = msg->resume_point->tv_sec;
target.tv_nsec = msg->resume_point->tv_nsec;
if (!journal_seek(&target, closure)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to seek to [%lld, %ld] in journal file %s",
(long long)target.tv_sec, target.tv_nsec, journal_path);
debug_return_bool(false);
}
debug_return_bool(true);
}
bool
journal_write(uint8_t *buf, size_t len, struct connection_closure *closure)
{
uint32_t msg_len;
debug_decl(journal_write, SUDO_DEBUG_UTIL);
/* 32-bit message length in network byte order. */
msg_len = htonl((uint32_t)len);
if (fwrite(&msg_len, 1, sizeof(msg_len), closure->journal) != sizeof(msg_len)) {
closure->errstr = _("unable to write journal file");
debug_return_bool(false);
}
/* message payload */
if (fwrite(buf, 1, len, closure->journal) != len) {
closure->errstr = _("unable to write journal file");
debug_return_bool(false);
}
debug_return_bool(true);
}

View File

@ -43,11 +43,6 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifdef HAVE_GETOPT_LONG
# include <getopt.h>
# else
# include "compat/getopt.h"
#endif /* HAVE_GETOPT_LONG */
#if defined(HAVE_OPENSSL)
# include <openssl/ssl.h>
@ -56,14 +51,11 @@
#define NEED_INET_NTOP /* to expose sudo_inet_ntop in sudo_compat.h */
#include "pathnames.h"
#include "sudo_compat.h"
#include "sudo_conf.h"
#include "sudo_debug.h"
#include "sudo_event.h"
#include "sudo_eventlog.h"
#include "sudo_gettext.h"
#include "sudo_json.h"
#include "sudo_iolog.h"
#include "sudo_queue.h"
#include "sudo_util.h"