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:
parent
6f5b353e87
commit
c2d3070fa1
1
MANIFEST
1
MANIFEST
@ -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
|
||||
|
@ -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 \
|
||||
$(incdir)/protobuf-c/protobuf-c.h $(incdir)/sudo_compat.h \
|
||||
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.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_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_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_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) -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 $@
|
||||
|
@ -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);
|
||||
|
@ -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,30 +342,44 @@ handle_accept(AcceptMessage *msg, uint8_t *buf, size_t len,
|
||||
debug_return_bool(relay_accept(msg, buf, len, closure));
|
||||
}
|
||||
|
||||
closure->evlog = evlog_new(msg->submit_time, msg->info_msgs,
|
||||
msg->n_info_msgs, closure);
|
||||
if (closure->evlog == NULL) {
|
||||
closure->errstr = _("error parsing AcceptMessage");
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
||||
/* Create I/O log info file and parent directories. */
|
||||
if (msg->expect_iobufs) {
|
||||
if (!iolog_init(msg, closure)) {
|
||||
closure->errstr = _("error creating I/O log");
|
||||
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) {
|
||||
closure->errstr = _("error parsing AcceptMessage");
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
||||
/* Create I/O log info file and parent directories. */
|
||||
if (msg->expect_iobufs) {
|
||||
if (!iolog_init(msg, closure)) {
|
||||
closure->errstr = _("error creating I/O log");
|
||||
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);
|
||||
}
|
||||
closure->log_io = true;
|
||||
}
|
||||
|
||||
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,17 +425,25 @@ handle_reject(RejectMessage *msg, uint8_t *buf, size_t len,
|
||||
debug_return_bool(relay_reject(msg, buf, len, closure));
|
||||
}
|
||||
|
||||
closure->evlog = evlog_new(msg->submit_time, msg->info_msgs,
|
||||
msg->n_info_msgs, closure);
|
||||
if (closure->evlog == NULL) {
|
||||
closure->errstr = _("error parsing RejectMessage");
|
||||
debug_return_bool(false);
|
||||
}
|
||||
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) {
|
||||
closure->errstr = _("error parsing RejectMessage");
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
||||
if (!eventlog_reject(closure->evlog, 0, msg->reason,
|
||||
logsrvd_json_log_cb, &info)) {
|
||||
closure->errstr = _("error logging reject event");
|
||||
debug_return_bool(false);
|
||||
if (!eventlog_reject(closure->evlog, 0, msg->reason,
|
||||
logsrvd_json_log_cb, &info)) {
|
||||
closure->errstr = _("error logging reject event");
|
||||
debug_return_bool(false);
|
||||
}
|
||||
}
|
||||
|
||||
closure->state = FINISHED;
|
||||
@ -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,12 +500,14 @@ handle_exit(ExitMessage *msg, uint8_t *buf, size_t len,
|
||||
__func__, (long long)closure->elapsed_time.tv_sec,
|
||||
closure->elapsed_time.tv_nsec);
|
||||
|
||||
/* Clear write bits from I/O timing file to indicate completion. */
|
||||
mode = logsrvd_conf_iolog_mode();
|
||||
CLR(mode, S_IWUSR|S_IWGRP|S_IWOTH);
|
||||
if (fchmodat(closure->iolog_dir_fd, "timing", mode, 0) == -1) {
|
||||
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
||||
"unable to fchmodat timing file");
|
||||
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);
|
||||
if (fchmodat(closure->iolog_dir_fd, "timing", mode, 0) == -1) {
|
||||
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
||||
"unable to fchmodat timing file");
|
||||
}
|
||||
}
|
||||
|
||||
/* Schedule the final commit point event immediately. */
|
||||
@ -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,22 +592,28 @@ handle_alert(AlertMessage *msg, uint8_t *buf, size_t len,
|
||||
debug_return_bool(relay_alert(msg, buf, len, closure));
|
||||
}
|
||||
|
||||
if (msg->info_msgs != NULL && msg->n_info_msgs != 0) {
|
||||
closure->evlog = evlog_new(NULL, msg->info_msgs,
|
||||
msg->n_info_msgs, closure);
|
||||
if (closure->evlog == NULL) {
|
||||
closure->errstr = _("error parsing AlertMessage");
|
||||
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);
|
||||
if (closure->evlog == NULL) {
|
||||
closure->errstr = _("error parsing AlertMessage");
|
||||
debug_return_bool(false);
|
||||
}
|
||||
}
|
||||
|
||||
alert_time.tv_sec = msg->alert_time->tv_sec;
|
||||
alert_time.tv_nsec = msg->alert_time->tv_nsec;
|
||||
if (!eventlog_alert(closure->evlog, 0, &alert_time, msg->reason, NULL)) {
|
||||
closure->errstr = _("error logging alert event");
|
||||
debug_return_bool(false);
|
||||
}
|
||||
}
|
||||
|
||||
alert_time.tv_sec = msg->alert_time->tv_sec;
|
||||
alert_time.tv_nsec = msg->alert_time->tv_nsec;
|
||||
if (!eventlog_alert(closure->evlog, 0, &alert_time, msg->reason, NULL)) {
|
||||
closure->errstr = _("error logging alert event");
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
||||
debug_return_bool(true);
|
||||
}
|
||||
|
||||
@ -594,22 +643,29 @@ handle_iobuf(int iofd, IoBuffer *iobuf, uint8_t *buf, size_t len,
|
||||
debug_return_bool(relay_iobuf(iobuf, buf, len, closure));
|
||||
}
|
||||
|
||||
/* Store IoBuffer in log. */
|
||||
if (store_iobuf(iofd, iobuf, closure) == -1) {
|
||||
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
||||
"failed to store IoBuffer");
|
||||
closure->errstr = _("error writing IoBuffer");
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
||||
/* Random drop is a debugging tool to test client restart. */
|
||||
if (random_drop > 0.0) {
|
||||
double randval = arc4random() / (double)UINT32_MAX;
|
||||
if (randval < random_drop) {
|
||||
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
|
||||
"randomly dropping connection (%f < %f)", randval, random_drop);
|
||||
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,
|
||||
"failed to store IoBuffer");
|
||||
closure->errstr = _("error writing IoBuffer");
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
||||
/* Random drop is a debugging tool to test client restart. */
|
||||
if (random_drop > 0.0) {
|
||||
double randval = arc4random() / (double)UINT32_MAX;
|
||||
if (randval < random_drop) {
|
||||
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
|
||||
"randomly dropping connection (%f < %f)", randval, random_drop);
|
||||
debug_return_bool(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Schedule a commit point in 10 sec if one is not already pending. */
|
||||
@ -652,12 +708,18 @@ handle_winsize(ChangeWindowSize *msg, uint8_t *buf, size_t len,
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: received ChangeWindowSize",
|
||||
__func__);
|
||||
|
||||
/* Store new window size in log. */
|
||||
if (store_winsize(msg, closure) == -1) {
|
||||
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
||||
"failed to store ChangeWindowSize");
|
||||
closure->errstr = _("error writing ChangeWindowSize");
|
||||
debug_return_bool(false);
|
||||
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,
|
||||
"failed to store ChangeWindowSize");
|
||||
closure->errstr = _("error writing ChangeWindowSize");
|
||||
debug_return_bool(false);
|
||||
}
|
||||
}
|
||||
|
||||
debug_return_bool(true);
|
||||
@ -690,19 +752,26 @@ handle_suspend(CommandSuspend *msg, uint8_t *buf, size_t len,
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: received CommandSuspend",
|
||||
__func__);
|
||||
|
||||
/* Store suspend signal in log. */
|
||||
if (store_suspend(msg, closure) == -1) {
|
||||
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
||||
"failed to store CommandSuspend");
|
||||
closure->errstr = _("error writing CommandSuspend");
|
||||
debug_return_bool(false);
|
||||
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,
|
||||
"failed to store CommandSuspend");
|
||||
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 {
|
||||
|
@ -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);
|
||||
|
@ -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
352
logsrvd/logsrvd_journal.c
Normal 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);
|
||||
}
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user