2019-10-24 20:04:29 -06:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2019 Todd C. Miller <Todd.Miller@courtesan.com>
|
|
|
|
*
|
|
|
|
* 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 <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#ifdef HAVE_STDBOOL_H
|
|
|
|
# include <stdbool.h>
|
|
|
|
#else
|
|
|
|
# include "compat/stdbool.h"
|
|
|
|
#endif /* HAVE_STDBOOL_H */
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "log_server.pb-c.h"
|
|
|
|
#include "sudo_compat.h"
|
|
|
|
#include "sudo_queue.h"
|
|
|
|
#include "sudo_debug.h"
|
|
|
|
#include "sudo_util.h"
|
|
|
|
#include "sudo_fatal.h"
|
2019-10-24 20:04:31 -06:00
|
|
|
#include "sudo_iolog.h"
|
2019-10-24 20:04:29 -06:00
|
|
|
#include "logsrvd.h"
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
has_numval(InfoMessage *info)
|
|
|
|
{
|
|
|
|
return info->value_case == INFO_MESSAGE__VALUE_NUMVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
has_strval(InfoMessage *info)
|
|
|
|
{
|
|
|
|
return info->value_case == INFO_MESSAGE__VALUE_STRVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
has_strlistval(InfoMessage *info)
|
|
|
|
{
|
|
|
|
return info->value_case == INFO_MESSAGE__VALUE_STRLISTVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2019-10-24 20:04:29 -06:00
|
|
|
* Fill in I/O log details from an ExecMessage
|
2019-10-24 20:04:29 -06:00
|
|
|
* Only makes a shallow copy of strings and string lists.
|
|
|
|
*/
|
|
|
|
static bool
|
2019-10-24 20:04:29 -06:00
|
|
|
iolog_details_fill(struct iolog_details *details, ExecMessage *msg)
|
2019-10-24 20:04:29 -06:00
|
|
|
{
|
|
|
|
size_t idx;
|
|
|
|
bool ret = true;
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_decl(iolog_details_fill, SUDO_DEBUG_UTIL)
|
2019-10-24 20:04:29 -06:00
|
|
|
|
2019-10-24 20:04:29 -06:00
|
|
|
memset(details, 0, sizeof(*details));
|
2019-10-24 20:04:29 -06:00
|
|
|
|
|
|
|
/* Start time. */
|
2019-10-24 20:04:29 -06:00
|
|
|
details->start_time = msg->start_time->tv_sec;
|
2019-10-24 20:04:29 -06:00
|
|
|
|
|
|
|
/* Default values */
|
2019-10-24 20:04:29 -06:00
|
|
|
details->lines = 24;
|
|
|
|
details->columns = 80;
|
2019-10-24 20:04:29 -06:00
|
|
|
|
|
|
|
/* Pull out values by key from info array. */
|
|
|
|
for (idx = 0; idx < msg->n_info_msgs; idx++) {
|
|
|
|
InfoMessage *info = msg->info_msgs[idx];
|
|
|
|
const char *key = info->key;
|
|
|
|
switch (key[0]) {
|
|
|
|
case 'c':
|
|
|
|
if (strcmp(key, "columns") == 0) {
|
|
|
|
if (!has_numval(info)) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"columns specified but not a number");
|
2019-10-24 20:04:29 -06:00
|
|
|
} else if (info->numval <= 0 || info->numval > INT_MAX) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"columns (%" PRId64 ") out of range", info->numval);
|
2019-10-24 20:04:29 -06:00
|
|
|
} else {
|
2019-10-24 20:04:29 -06:00
|
|
|
details->columns = info->numval;
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strcmp(key, "command") == 0) {
|
|
|
|
if (has_strval(info)) {
|
2019-10-24 20:04:29 -06:00
|
|
|
details->command = info->strval;
|
2019-10-24 20:04:29 -06:00
|
|
|
} else {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"command specified but not a string");
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strcmp(key, "cwd") == 0) {
|
|
|
|
if (has_strval(info)) {
|
2019-10-24 20:04:29 -06:00
|
|
|
details->cwd = info->strval;
|
2019-10-24 20:04:29 -06:00
|
|
|
} else {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"cwd specified but not a string");
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
if (strcmp(key, "lines") == 0) {
|
|
|
|
if (!has_numval(info)) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"lines specified but not a number");
|
2019-10-24 20:04:29 -06:00
|
|
|
} else if (info->numval <= 0 || info->numval > INT_MAX) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"lines (%" PRId64 ") out of range", info->numval);
|
2019-10-24 20:04:29 -06:00
|
|
|
} else {
|
2019-10-24 20:04:29 -06:00
|
|
|
details->lines = info->numval;
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
if (strcmp(key, "runargv") == 0) {
|
|
|
|
if (has_strlistval(info)) {
|
2019-10-24 20:04:29 -06:00
|
|
|
details->argv = info->strlistval->strings;
|
|
|
|
details->argc = info->strlistval->n_strings;
|
2019-10-24 20:04:29 -06:00
|
|
|
} else {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"runargv specified but not a string list");
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strcmp(key, "rungroup") == 0) {
|
|
|
|
if (has_strval(info)) {
|
2019-10-24 20:04:29 -06:00
|
|
|
details->rungroup = info->strval;
|
2019-10-24 20:04:29 -06:00
|
|
|
} else {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"rungroup specified but not a string");
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strcmp(key, "runuser") == 0) {
|
|
|
|
if (has_strval(info)) {
|
2019-10-24 20:04:29 -06:00
|
|
|
details->runuser = info->strval;
|
2019-10-24 20:04:29 -06:00
|
|
|
} else {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"runuser specified but not a string");
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
if (strcmp(key, "submithost") == 0) {
|
|
|
|
if (has_strval(info)) {
|
2019-10-24 20:04:29 -06:00
|
|
|
details->submithost = info->strval;
|
2019-10-24 20:04:29 -06:00
|
|
|
} else {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"submithost specified but not a string");
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strcmp(key, "submituser") == 0) {
|
|
|
|
if (has_strval(info)) {
|
2019-10-24 20:04:29 -06:00
|
|
|
details->submituser = info->strval;
|
2019-10-24 20:04:29 -06:00
|
|
|
} else {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"submituser specified but not a string");
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
if (strcmp(key, "ttyname") == 0) {
|
|
|
|
if (has_strval(info)) {
|
2019-10-24 20:04:29 -06:00
|
|
|
details->ttyname = info->strval;
|
2019-10-24 20:04:29 -06:00
|
|
|
} else {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"ttyname specified but not a string");
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for required settings */
|
2019-10-24 20:04:29 -06:00
|
|
|
if (details->submituser == NULL) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"missing user in ExecMessage");
|
2019-10-24 20:04:29 -06:00
|
|
|
ret = false;
|
|
|
|
}
|
2019-10-24 20:04:29 -06:00
|
|
|
if (details->submithost == NULL) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"missing host in ExecMessage");
|
2019-10-24 20:04:29 -06:00
|
|
|
ret = false;
|
|
|
|
}
|
2019-10-24 20:04:29 -06:00
|
|
|
if (details->command == NULL) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"missing command in ExecMessage");
|
2019-10-24 20:04:29 -06:00
|
|
|
ret = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug_return_bool(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create I/O log path
|
2019-10-24 20:04:31 -06:00
|
|
|
* Sets iolog_dir and iolog_dir_fd in the closure
|
|
|
|
* XXX - use iolog_dir and iolog_file code from sudoers/iolog.c
|
2019-10-24 20:04:29 -06:00
|
|
|
*/
|
|
|
|
static bool
|
2019-10-24 20:04:29 -06:00
|
|
|
create_iolog_dir(struct iolog_details *details, struct connection_closure *closure)
|
2019-10-24 20:04:29 -06:00
|
|
|
{
|
|
|
|
char path[PATH_MAX];
|
|
|
|
int len;
|
|
|
|
debug_decl(create_iolog_dir, SUDO_DEBUG_UTIL)
|
|
|
|
|
|
|
|
/* Create IOLOG_DIR/host/user/XXXXXX directory */
|
|
|
|
if (mkdir(IOLOG_DIR, 0755) == -1 && errno != EEXIST) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"mkdir %s", path);
|
2019-10-24 20:04:29 -06:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
len = snprintf(path, sizeof(path), "%s/%s", IOLOG_DIR,
|
2019-10-24 20:04:29 -06:00
|
|
|
details->submithost);
|
2019-10-24 20:04:29 -06:00
|
|
|
if (len < 0 || len >= ssizeof(path)) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"failed to snprintf I/O log path");
|
2019-10-24 20:04:29 -06:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if (mkdir(path, 0755) == -1 && errno != EEXIST) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"mkdir %s", path);
|
2019-10-24 20:04:29 -06:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
len = snprintf(path, sizeof(path), "%s/%s/%s", IOLOG_DIR,
|
2019-10-24 20:04:29 -06:00
|
|
|
details->submithost, details->submituser);
|
2019-10-24 20:04:29 -06:00
|
|
|
if (len < 0 || len >= ssizeof(path)) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"failed to snprintf I/O log path");
|
2019-10-24 20:04:29 -06:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if (mkdir(path, 0755) == -1 && errno != EEXIST) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"mkdir %s", path);
|
2019-10-24 20:04:29 -06:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
len = snprintf(path, sizeof(path), "%s/%s/%s/XXXXXX", IOLOG_DIR,
|
2019-10-24 20:04:29 -06:00
|
|
|
details->submithost, details->submituser);
|
2019-10-24 20:04:29 -06:00
|
|
|
if (len < 0 || len >= ssizeof(path)) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"failed to snprintf I/O log path");
|
2019-10-24 20:04:29 -06:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if (mkdtemp(path) == NULL) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"mkdtemp %s", path);
|
2019-10-24 20:04:29 -06:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((closure->iolog_dir = strdup(path)) == NULL) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"strdup");
|
2019-10-24 20:04:29 -06:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2019-10-24 20:04:31 -06:00
|
|
|
#if 0
|
2019-10-24 20:04:29 -06:00
|
|
|
/* We use iolog_dir_fd in calls to openat(2) */
|
|
|
|
closure->iolog_dir_fd = open(closure->iolog_dir, O_RDONLY);
|
|
|
|
if (closure->iolog_dir_fd == -1) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
2019-10-24 20:04:30 -06:00
|
|
|
"%s", closure->iolog_dir);
|
2019-10-24 20:04:29 -06:00
|
|
|
goto bad;
|
|
|
|
}
|
2019-10-24 20:04:31 -06:00
|
|
|
#endif
|
2019-10-24 20:04:29 -06:00
|
|
|
|
|
|
|
debug_return_bool(true);
|
|
|
|
bad:
|
|
|
|
free(closure->iolog_dir);
|
|
|
|
debug_return_bool(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the sudo-style I/O log info file containing user and command info.
|
|
|
|
*/
|
|
|
|
static bool
|
2019-10-24 20:04:29 -06:00
|
|
|
iolog_details_write(struct iolog_details *details, struct connection_closure *closure)
|
2019-10-24 20:04:29 -06:00
|
|
|
{
|
|
|
|
int fd, i;
|
|
|
|
FILE *fp;
|
|
|
|
int error;
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_decl(iolog_details_write, SUDO_DEBUG_UTIL)
|
2019-10-24 20:04:29 -06:00
|
|
|
|
|
|
|
fd = openat(closure->iolog_dir_fd, "log", O_CREAT|O_EXCL|O_WRONLY, 0600);
|
|
|
|
if (fd == -1 || (fp = fdopen(fd, "w")) == NULL) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"unable to open %s", closure->iolog_dir);
|
2019-10-24 20:04:29 -06:00
|
|
|
if (fd != -1)
|
|
|
|
close(fd);
|
|
|
|
debug_return_bool(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(fp, "%lld:%s:%s:%s:%s:%d:%d\n%s\n",
|
2019-10-24 20:04:29 -06:00
|
|
|
(long long)details->start_time, details->submituser,
|
|
|
|
details->runuser ? details->runuser : RUNAS_DEFAULT,
|
|
|
|
details->rungroup ? details->rungroup : "",
|
|
|
|
details->ttyname ? details->ttyname : "unknown",
|
|
|
|
details->lines, details->columns,
|
|
|
|
details->cwd ? details->cwd : "unknown");
|
|
|
|
fputs(details->command, fp);
|
|
|
|
for (i = 1; i < details->argc; i++) {
|
2019-10-24 20:04:29 -06:00
|
|
|
fputc(' ', fp);
|
2019-10-24 20:04:29 -06:00
|
|
|
fputs(details->argv[i], fp);
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
fputc('\n', fp);
|
|
|
|
fflush(fp);
|
2019-10-24 20:04:30 -06:00
|
|
|
if ((error = ferror(fp))) {
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"unable to write to I/O log file %s", closure->iolog_dir);
|
|
|
|
}
|
2019-10-24 20:04:29 -06:00
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
debug_return_bool(!error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2019-10-24 20:04:31 -06:00
|
|
|
iolog_create(int iofd, struct connection_closure *closure)
|
2019-10-24 20:04:29 -06:00
|
|
|
{
|
2019-10-24 20:04:31 -06:00
|
|
|
char path[PATH_MAX];
|
|
|
|
int len;
|
|
|
|
debug_decl(iolog_create, SUDO_DEBUG_UTIL)
|
2019-10-24 20:04:29 -06:00
|
|
|
|
|
|
|
if (iofd < 0 || iofd >= IOFD_MAX) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"invalid iofd %d", iofd);
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_bool(false);
|
|
|
|
}
|
|
|
|
|
2019-10-24 20:04:31 -06:00
|
|
|
len = snprintf(path, sizeof(path), "%s/%s", closure->iolog_dir,
|
|
|
|
iolog_fd_to_name(iofd));
|
|
|
|
if (len < 0 || len >= ssizeof(path)) {
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"%s/%s", closure->iolog_dir, iolog_fd_to_name(iofd));
|
|
|
|
debug_return_bool(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
closure->iolog_files[iofd].enabled = true;
|
|
|
|
debug_return_bool(iolog_open(&closure->iolog_files[iofd], path, "w"));
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-10-24 20:04:31 -06:00
|
|
|
iolog_close_all(struct connection_closure *closure)
|
2019-10-24 20:04:29 -06:00
|
|
|
{
|
2019-10-24 20:04:31 -06:00
|
|
|
const char *errstr;
|
2019-10-24 20:04:29 -06:00
|
|
|
int i;
|
|
|
|
debug_decl(iolog_close, SUDO_DEBUG_UTIL)
|
|
|
|
|
|
|
|
for (i = 0; i < IOFD_MAX; i++) {
|
2019-10-24 20:04:31 -06:00
|
|
|
if (!closure->iolog_files[i].enabled)
|
2019-10-24 20:04:29 -06:00
|
|
|
continue;
|
2019-10-24 20:04:31 -06:00
|
|
|
if (!iolog_close(&closure->iolog_files[i], &errstr)) {
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"error closing iofd %d: %s", i, errstr);
|
|
|
|
}
|
2019-10-24 20:04:29 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
debug_return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
iolog_init(ExecMessage *msg, struct connection_closure *closure)
|
|
|
|
{
|
2019-10-24 20:04:29 -06:00
|
|
|
struct iolog_details details;
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_decl(iolog_init, SUDO_DEBUG_UTIL)
|
|
|
|
|
2019-10-24 20:04:29 -06:00
|
|
|
/* Fill in iolog_details */
|
|
|
|
if (!iolog_details_fill(&details, msg))
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_bool(false);
|
|
|
|
|
|
|
|
/* Create I/O log dir */
|
2019-10-24 20:04:29 -06:00
|
|
|
if (!create_iolog_dir(&details, closure))
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_bool(false);
|
|
|
|
|
|
|
|
/* Write sudo I/O log info file */
|
2019-10-24 20:04:29 -06:00
|
|
|
if (!iolog_details_write(&details, closure))
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_bool(false);
|
|
|
|
|
2019-10-24 20:04:31 -06:00
|
|
|
/*
|
|
|
|
* Create timing, stdout, stderr and ttyout files for sudoreplay.
|
|
|
|
* Others will be created on demand.
|
|
|
|
*/
|
|
|
|
if (!iolog_create(IOFD_TIMING, closure) ||
|
|
|
|
!iolog_create(IOFD_STDOUT, closure) ||
|
|
|
|
!iolog_create(IOFD_STDERR, closure) ||
|
|
|
|
!iolog_create(IOFD_TTYOUT, closure))
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_bool(false);
|
|
|
|
|
|
|
|
/* Ready to log I/O buffers. */
|
|
|
|
debug_return_bool(true);
|
|
|
|
}
|
|
|
|
|
2019-10-24 20:04:30 -06:00
|
|
|
/*
|
|
|
|
* Read the next record from the timing file.
|
|
|
|
* Return 0 on success, 1 on EOF and -1 on error.
|
|
|
|
*/
|
|
|
|
static int
|
2019-10-24 20:04:31 -06:00
|
|
|
read_timing_record(struct iolog_file *iol, struct timing_closure *timing)
|
2019-10-24 20:04:30 -06:00
|
|
|
{
|
|
|
|
char line[LINE_MAX];
|
2019-10-24 20:04:31 -06:00
|
|
|
const char *errstr;
|
2019-10-24 20:04:30 -06:00
|
|
|
debug_decl(read_timing_record, SUDO_DEBUG_UTIL)
|
|
|
|
|
|
|
|
/* Read next record from timing file. */
|
2019-10-24 20:04:31 -06:00
|
|
|
if (iolog_gets(iol, line, sizeof(line), &errstr) == NULL) {
|
2019-10-24 20:04:30 -06:00
|
|
|
/* EOF or error reading timing file, we are done. */
|
2019-10-24 20:04:31 -06:00
|
|
|
if (iolog_eof(iol))
|
2019-10-24 20:04:30 -06:00
|
|
|
debug_return_int(1); /* EOF */
|
2019-10-24 20:04:31 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"error reading timing file: %s", errstr);
|
2019-10-24 20:04:30 -06:00
|
|
|
debug_return_int(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse timing file record. */
|
|
|
|
line[strcspn(line, "\n")] = '\0';
|
|
|
|
if (!parse_timing(line, timing)) {
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"invalid timing file line: %s", line);
|
|
|
|
debug_return_int(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
debug_return_int(0);
|
|
|
|
}
|
|
|
|
|
2019-10-24 20:04:31 -06:00
|
|
|
/* XXX - compressed I/O logs cannot be restarted, must re-write them */
|
2019-10-24 20:04:30 -06:00
|
|
|
bool
|
|
|
|
iolog_restart(RestartMessage *msg, struct connection_closure *closure)
|
|
|
|
{
|
|
|
|
struct timespec target;
|
|
|
|
struct timing_closure timing;
|
2019-10-24 20:04:31 -06:00
|
|
|
off_t pos;
|
|
|
|
int i;
|
2019-10-24 20:04:30 -06:00
|
|
|
debug_decl(iolog_init, SUDO_DEBUG_UTIL)
|
|
|
|
|
|
|
|
target.tv_sec = msg->resume_point->tv_sec;
|
|
|
|
target.tv_nsec = msg->resume_point->tv_nsec;
|
|
|
|
|
|
|
|
if ((closure->iolog_dir = strdup(msg->log_id)) == NULL) {
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"strdup");
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open existing I/O log files. */
|
|
|
|
for (i = 0; i < IOFD_MAX; i++) {
|
2019-10-24 20:04:31 -06:00
|
|
|
char path[PATH_MAX];
|
|
|
|
int len = snprintf(path, sizeof(path), "%s/%s", closure->iolog_dir,
|
|
|
|
iolog_fd_to_name(i));
|
|
|
|
if (len < 0 || len >= ssizeof(path)) {
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
|
|
|
|
"%s: %s/%s", __func__, closure->iolog_dir, iolog_fd_to_name(i));
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
closure->iolog_files[i].enabled = true;
|
|
|
|
(void)iolog_open(&closure->iolog_files[i], path, "r+");
|
2019-10-24 20:04:30 -06:00
|
|
|
}
|
2019-10-24 20:04:31 -06:00
|
|
|
if (!closure->iolog_files[IOFD_TIMING].enabled)
|
2019-10-24 20:04:30 -06:00
|
|
|
goto bad;
|
|
|
|
|
|
|
|
/* Parse timing file until we reach the target point. */
|
|
|
|
/* XXX - split up */
|
|
|
|
for (;;) {
|
2019-10-24 20:04:31 -06:00
|
|
|
if (read_timing_record(&closure->iolog_files[IOFD_TIMING], &timing) != 0)
|
2019-10-24 20:04:30 -06:00
|
|
|
goto bad;
|
|
|
|
sudo_timespecadd(&timing.delay, &closure->elapsed_time,
|
|
|
|
&closure->elapsed_time);
|
|
|
|
if (timing.event < IOFD_TIMING) {
|
2019-10-24 20:04:31 -06:00
|
|
|
if (!closure->iolog_files[timing.event].enabled) {
|
2019-10-24 20:04:30 -06:00
|
|
|
/* Missing log file. */
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"iofd %d referenced but not open", timing.event);
|
|
|
|
goto bad;
|
|
|
|
}
|
2019-10-24 20:04:31 -06:00
|
|
|
pos = iolog_seek(&closure->iolog_files[timing.event],
|
|
|
|
timing.u.nbytes, SEEK_CUR);
|
|
|
|
if (pos == -1) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(
|
|
|
|
SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
2019-10-24 20:04:31 -06:00
|
|
|
"seek(%d, %lld, SEEK_CUR", timing.event,
|
2019-10-24 20:04:30 -06:00
|
|
|
(long long)timing.u.nbytes);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sudo_timespeccmp(&closure->elapsed_time, &target, >=)) {
|
|
|
|
if (sudo_timespeccmp(&closure->elapsed_time, &target, ==))
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Mismatch between resume point and stored log. */
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"resume point mismatch, target [%lld, %ld], have [%lld, %ld]",
|
|
|
|
(long long)target.tv_sec, target.tv_nsec,
|
|
|
|
(long long)closure->elapsed_time.tv_sec,
|
|
|
|
closure->elapsed_time.tv_nsec);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
}
|
2019-10-24 20:04:31 -06:00
|
|
|
/* Must seek or flush before switching from read -> write. */
|
|
|
|
if (iolog_seek(&closure->iolog_files[IOFD_TIMING], 0, SEEK_CUR) == -1) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
2019-10-24 20:04:31 -06:00
|
|
|
"lseek(IOFD_TIMING, 0, SEEK_CUR)");
|
2019-10-24 20:04:30 -06:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ready to log I/O buffers. */
|
|
|
|
debug_return_bool(true);
|
|
|
|
bad:
|
|
|
|
debug_return_bool(false);
|
|
|
|
}
|
|
|
|
|
2019-10-24 20:04:29 -06:00
|
|
|
/*
|
|
|
|
* Add given delta to elapsed time.
|
|
|
|
* We cannot use timespecadd here since delta is not struct timespec.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
update_elapsed_time(TimeSpec *delta, struct timespec *elapsed)
|
|
|
|
{
|
|
|
|
debug_decl(update_elapsed_time, SUDO_DEBUG_UTIL)
|
|
|
|
|
|
|
|
/* Cannot use timespecadd since msg doesn't use struct timespec. */
|
|
|
|
elapsed->tv_sec += delta->tv_sec;
|
|
|
|
elapsed->tv_nsec += delta->tv_nsec;
|
|
|
|
while (elapsed->tv_nsec >= 1000000000) {
|
|
|
|
elapsed->tv_sec++;
|
|
|
|
elapsed->tv_nsec -= 1000000000;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug_return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
store_iobuf(int iofd, IoBuffer *msg, struct connection_closure *closure)
|
|
|
|
{
|
2019-10-24 20:04:31 -06:00
|
|
|
const char *errstr;
|
2019-10-24 20:04:29 -06:00
|
|
|
char tbuf[1024];
|
|
|
|
int len;
|
|
|
|
debug_decl(store_iobuf, SUDO_DEBUG_UTIL)
|
|
|
|
|
|
|
|
/* Open log file as needed. */
|
2019-10-24 20:04:31 -06:00
|
|
|
if (!closure->iolog_files[iofd].enabled) {
|
|
|
|
if (!iolog_create(iofd, closure))
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_int(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Format timing data. */
|
|
|
|
/* FIXME - assumes IOFD_* matches IO_EVENT_* */
|
|
|
|
len = snprintf(tbuf, sizeof(tbuf), "%d %lld.%09d %zu\n",
|
|
|
|
iofd, (long long)msg->delay->tv_sec, (int)msg->delay->tv_nsec,
|
|
|
|
msg->data.len);
|
|
|
|
if (len < 0 || len >= ssizeof(tbuf)) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"unable to format timing buffer");
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_int(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write to specified I/O log file. */
|
2019-10-24 20:04:31 -06:00
|
|
|
if (!iolog_write(&closure->iolog_files[iofd], msg->data.data,
|
|
|
|
msg->data.len, &errstr)) {
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"unable to write to %s/%s: %s", closure->iolog_dir,
|
|
|
|
iolog_fd_to_name(iofd), errstr);
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_int(-1);
|
2019-10-24 20:04:31 -06:00
|
|
|
}
|
2019-10-24 20:04:29 -06:00
|
|
|
|
|
|
|
/* Write timing data. */
|
2019-10-24 20:04:31 -06:00
|
|
|
if (!iolog_write(&closure->iolog_files[IOFD_TIMING], tbuf,
|
|
|
|
len, &errstr)) {
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"unable to write to %s/%s: %s", closure->iolog_dir,
|
|
|
|
iolog_fd_to_name(IOFD_TIMING), errstr);
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_int(-1);
|
2019-10-24 20:04:31 -06:00
|
|
|
}
|
2019-10-24 20:04:29 -06:00
|
|
|
|
|
|
|
update_elapsed_time(msg->delay, &closure->elapsed_time);
|
|
|
|
|
|
|
|
debug_return_int(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
store_suspend(CommandSuspend *msg, struct connection_closure *closure)
|
|
|
|
{
|
2019-10-24 20:04:31 -06:00
|
|
|
const char *errstr;
|
2019-10-24 20:04:29 -06:00
|
|
|
char tbuf[1024];
|
|
|
|
int len;
|
|
|
|
debug_decl(store_suspend, SUDO_DEBUG_UTIL)
|
|
|
|
|
|
|
|
/* Format timing data including suspend signal. */
|
|
|
|
len = snprintf(tbuf, sizeof(tbuf), "%d %lld.%09d %s\n", IO_EVENT_SUSPEND,
|
|
|
|
(long long)msg->delay->tv_sec, (int)msg->delay->tv_nsec,
|
|
|
|
msg->signal);
|
|
|
|
if (len < 0 || len >= ssizeof(tbuf)) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"unable to format timing buffer");
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_int(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write timing data. */
|
2019-10-24 20:04:31 -06:00
|
|
|
if (!iolog_write(&closure->iolog_files[IOFD_TIMING], tbuf,
|
|
|
|
len, &errstr)) {
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"unable to write to %s/%s: %s", closure->iolog_dir,
|
|
|
|
iolog_fd_to_name(IOFD_TIMING), errstr);
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_int(-1);
|
2019-10-24 20:04:31 -06:00
|
|
|
}
|
2019-10-24 20:04:29 -06:00
|
|
|
|
|
|
|
update_elapsed_time(msg->delay, &closure->elapsed_time);
|
|
|
|
|
|
|
|
debug_return_int(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
store_winsize(ChangeWindowSize *msg, struct connection_closure *closure)
|
|
|
|
{
|
2019-10-24 20:04:31 -06:00
|
|
|
const char *errstr;
|
2019-10-24 20:04:29 -06:00
|
|
|
char tbuf[1024];
|
|
|
|
int len;
|
|
|
|
debug_decl(store_winsize, SUDO_DEBUG_UTIL)
|
|
|
|
|
|
|
|
/* Format timing data including new window size. */
|
|
|
|
len = snprintf(tbuf, sizeof(tbuf), "%d %lld.%09d %d %d\n", IO_EVENT_WINSIZE,
|
|
|
|
(long long)msg->delay->tv_sec, (int)msg->delay->tv_nsec,
|
|
|
|
msg->rows, msg->cols);
|
|
|
|
if (len < 0 || len >= ssizeof(tbuf)) {
|
2019-10-24 20:04:30 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
|
|
|
"unable to format timing buffer");
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_int(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write timing data. */
|
2019-10-24 20:04:31 -06:00
|
|
|
if (!iolog_write(&closure->iolog_files[IOFD_TIMING], tbuf,
|
|
|
|
len, &errstr)) {
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
|
"unable to write to %s/%s: %s", closure->iolog_dir,
|
|
|
|
iolog_fd_to_name(IOFD_TIMING), errstr);
|
2019-10-24 20:04:29 -06:00
|
|
|
debug_return_int(-1);
|
2019-10-24 20:04:31 -06:00
|
|
|
}
|
2019-10-24 20:04:29 -06:00
|
|
|
|
|
|
|
update_elapsed_time(msg->delay, &closure->elapsed_time);
|
|
|
|
|
|
|
|
debug_return_int(0);
|
|
|
|
}
|