version 1.1.1.3, 2012/10/09 09:29:52
|
version 1.1.1.4, 2013/07/22 10:46:12
|
Line 1
|
Line 1
|
/* |
/* |
* Copyright (c) 2009-2012 Todd C. Miller <Todd.Miller@courtesan.com> | * Copyright (c) 2009-2013 Todd C. Miller <Todd.Miller@courtesan.com> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
* purpose with or without fee is hereby granted, provided that the above |
* purpose with or without fee is hereby granted, provided that the above |
Line 17
|
Line 17
|
#include <config.h> |
#include <config.h> |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/param.h> |
|
#include <sys/uio.h> |
#include <sys/uio.h> |
#ifdef HAVE_SYS_SYSMACROS_H |
#ifdef HAVE_SYS_SYSMACROS_H |
# include <sys/sysmacros.h> |
# include <sys/sysmacros.h> |
Line 82
|
Line 81
|
#ifdef HAVE_ZLIB_H |
#ifdef HAVE_ZLIB_H |
# include <zlib.h> |
# include <zlib.h> |
#endif |
#endif |
#ifdef HAVE_SETLOCALE |
|
# include <locale.h> |
|
#endif |
|
#include <signal.h> |
#include <signal.h> |
#ifdef HAVE_STDBOOL_H |
#ifdef HAVE_STDBOOL_H |
# include <stdbool.h> |
# include <stdbool.h> |
Line 98
|
Line 94
|
#include "alloc.h" |
#include "alloc.h" |
#include "error.h" |
#include "error.h" |
#include "gettext.h" |
#include "gettext.h" |
|
#include "logging.h" |
#include "sudo_plugin.h" |
#include "sudo_plugin.h" |
#include "sudo_conf.h" |
#include "sudo_conf.h" |
#include "sudo_debug.h" |
#include "sudo_debug.h" |
Line 181 struct search_node {
|
Line 178 struct search_node {
|
} u; |
} u; |
} *search_expr; |
} *search_expr; |
|
|
sudo_conv_t sudo_conv; /* NULL in non-plugin */ |
|
|
|
#define STACK_NODE_SIZE 32 |
#define STACK_NODE_SIZE 32 |
static struct search_node *node_stack[32]; |
static struct search_node *node_stack[32]; |
static int stack_top; |
static int stack_top; |
Line 204 extern char *get_timestr(time_t, int);
|
Line 199 extern char *get_timestr(time_t, int);
|
extern int term_raw(int, int); |
extern int term_raw(int, int); |
extern int term_restore(int, int); |
extern int term_restore(int, int); |
extern void get_ttysize(int *rowp, int *colp); |
extern void get_ttysize(int *rowp, int *colp); |
void cleanup(int); |
|
|
|
static int list_sessions(int, char **, const char *, const char *, const char *); |
static int list_sessions(int, char **, const char *, const char *, const char *); |
static int parse_expr(struct search_node **, char **); |
static int parse_expr(struct search_node **, char **); |
Line 217 static int parse_timing(const char *buf, const char *d
|
Line 211 static int parse_timing(const char *buf, const char *d
|
static struct log_info *parse_logfile(char *logfile); |
static struct log_info *parse_logfile(char *logfile); |
static void free_log_info(struct log_info *li); |
static void free_log_info(struct log_info *li); |
static size_t atomic_writev(int fd, struct iovec *iov, int iovcnt); |
static size_t atomic_writev(int fd, struct iovec *iov, int iovcnt); |
|
static void sudoreplay_handler(int); |
|
static void sudoreplay_cleanup(void); |
|
|
#ifdef HAVE_REGCOMP |
#ifdef HAVE_REGCOMP |
# define REGEX_T regex_t |
# define REGEX_T regex_t |
Line 238 static size_t atomic_writev(int fd, struct iovec *iov,
|
Line 234 static size_t atomic_writev(int fd, struct iovec *iov,
|
(s)[8] == '/' && (s)[9] == 'l' && (s)[10] == 'o' && (s)[11] == 'g' && \ |
(s)[8] == '/' && (s)[9] == 'l' && (s)[10] == 'o' && (s)[11] == 'g' && \ |
(s)[12] == '\0') |
(s)[12] == '\0') |
|
|
|
__dso_public int main(int argc, char *argv[]); |
|
|
int |
int |
main(int argc, char *argv[]) |
main(int argc, char *argv[]) |
{ |
{ |
int ch, idx, plen, exitcode = 0, rows = 0, cols = 0; |
int ch, idx, plen, exitcode = 0, rows = 0, cols = 0; |
bool interactive = false, listonly = false, need_nlcr = false; |
bool interactive = false, listonly = false, need_nlcr = false; |
const char *id, *user = NULL, *pattern = NULL, *tty = NULL, *decimal = "."; | const char *decimal, *id, *user = NULL, *pattern = NULL, *tty = NULL; |
char path[PATH_MAX], buf[LINE_MAX], *cp, *ep; |
char path[PATH_MAX], buf[LINE_MAX], *cp, *ep; |
double seconds, to_wait, speed = 1.0, max_wait = 0; |
double seconds, to_wait, speed = 1.0, max_wait = 0; |
sigaction_t sa; |
sigaction_t sa; |
Line 264 main(int argc, char *argv[])
|
Line 262 main(int argc, char *argv[])
|
setprogname(argc > 0 ? argv[0] : "sudoreplay"); |
setprogname(argc > 0 ? argv[0] : "sudoreplay"); |
#endif |
#endif |
|
|
#ifdef HAVE_SETLOCALE | sudoers_setlocale(SUDOERS_LOCALE_USER, NULL); |
setlocale(LC_ALL, ""); | |
decimal = localeconv()->decimal_point; |
decimal = localeconv()->decimal_point; |
#endif |
|
bindtextdomain("sudoers", LOCALEDIR); /* XXX - should have sudoreplay domain */ |
bindtextdomain("sudoers", LOCALEDIR); /* XXX - should have sudoreplay domain */ |
textdomain("sudoers"); |
textdomain("sudoers"); |
|
|
|
/* Register fatal/fatalx callback. */ |
|
fatal_callback_register(sudoreplay_cleanup); |
|
|
/* Read sudo.conf. */ |
/* Read sudo.conf. */ |
sudo_conf_read(); | sudo_conf_read(NULL); |
|
|
while ((ch = getopt(argc, argv, "d:f:hlm:s:V")) != -1) { |
while ((ch = getopt(argc, argv, "d:f:hlm:s:V")) != -1) { |
switch(ch) { |
switch(ch) { |
Line 290 main(int argc, char *argv[])
|
Line 289 main(int argc, char *argv[])
|
else if (strcmp(cp, "ttyout") == 0) |
else if (strcmp(cp, "ttyout") == 0) |
SET(replay_filter, 1 << IOFD_TTYOUT); |
SET(replay_filter, 1 << IOFD_TTYOUT); |
else |
else |
errorx(1, _("invalid filter option: %s"), optarg); | fatalx(_("invalid filter option: %s"), optarg); |
} |
} |
break; |
break; |
case 'h': |
case 'h': |
Line 303 main(int argc, char *argv[])
|
Line 302 main(int argc, char *argv[])
|
errno = 0; |
errno = 0; |
max_wait = strtod(optarg, &ep); |
max_wait = strtod(optarg, &ep); |
if (*ep != '\0' || errno != 0) |
if (*ep != '\0' || errno != 0) |
errorx(1, _("invalid max wait: %s"), optarg); | fatalx(_("invalid max wait: %s"), optarg); |
break; |
break; |
case 's': |
case 's': |
errno = 0; |
errno = 0; |
speed = strtod(optarg, &ep); |
speed = strtod(optarg, &ep); |
if (*ep != '\0' || errno != 0) |
if (*ep != '\0' || errno != 0) |
errorx(1, _("invalid speed factor: %s"), optarg); | fatalx(_("invalid speed factor: %s"), optarg); |
break; |
break; |
case 'V': |
case 'V': |
(void) printf(_("%s version %s\n"), getprogname(), PACKAGE_VERSION); |
(void) printf(_("%s version %s\n"), getprogname(), PACKAGE_VERSION); |
Line 337 main(int argc, char *argv[])
|
Line 336 main(int argc, char *argv[])
|
plen = snprintf(path, sizeof(path), "%s/%.2s/%.2s/%.2s/timing", |
plen = snprintf(path, sizeof(path), "%s/%.2s/%.2s/%.2s/timing", |
session_dir, id, &id[2], &id[4]); |
session_dir, id, &id[2], &id[4]); |
if (plen <= 0 || plen >= sizeof(path)) |
if (plen <= 0 || plen >= sizeof(path)) |
errorx(1, _("%s/%.2s/%.2s/%.2s/timing: %s"), session_dir, | fatalx(_("%s/%.2s/%.2s/%.2s/timing: %s"), session_dir, |
id, &id[2], &id[4], strerror(ENAMETOOLONG)); |
id, &id[2], &id[4], strerror(ENAMETOOLONG)); |
} else { |
} else { |
plen = snprintf(path, sizeof(path), "%s/%s/timing", |
plen = snprintf(path, sizeof(path), "%s/%s/timing", |
session_dir, id); |
session_dir, id); |
if (plen <= 0 || plen >= sizeof(path)) |
if (plen <= 0 || plen >= sizeof(path)) |
errorx(1, _("%s/%s/timing: %s"), session_dir, | fatalx(_("%s/%s/timing: %s"), session_dir, |
id, strerror(ENAMETOOLONG)); |
id, strerror(ENAMETOOLONG)); |
} |
} |
plen -= 7; |
plen -= 7; |
Line 352 main(int argc, char *argv[])
|
Line 351 main(int argc, char *argv[])
|
for (idx = 0; idx < IOFD_MAX; idx++) { |
for (idx = 0; idx < IOFD_MAX; idx++) { |
if (ISSET(replay_filter, 1 << idx) || idx == IOFD_TIMING) { |
if (ISSET(replay_filter, 1 << idx) || idx == IOFD_TIMING) { |
if (open_io_fd(path, plen, io_fnames[idx], &io_fds[idx]) == -1) |
if (open_io_fd(path, plen, io_fnames[idx], &io_fds[idx]) == -1) |
error(1, _("unable to open %s"), path); | fatal(_("unable to open %s"), path); |
} |
} |
} |
} |
|
|
Line 380 main(int argc, char *argv[])
|
Line 379 main(int argc, char *argv[])
|
memset(&sa, 0, sizeof(sa)); |
memset(&sa, 0, sizeof(sa)); |
sigemptyset(&sa.sa_mask); |
sigemptyset(&sa.sa_mask); |
sa.sa_flags = SA_RESETHAND; |
sa.sa_flags = SA_RESETHAND; |
sa.sa_handler = cleanup; | sa.sa_handler = sudoreplay_handler; |
(void) sigaction(SIGINT, &sa, NULL); |
(void) sigaction(SIGINT, &sa, NULL); |
(void) sigaction(SIGKILL, &sa, NULL); |
(void) sigaction(SIGKILL, &sa, NULL); |
(void) sigaction(SIGTERM, &sa, NULL); |
(void) sigaction(SIGTERM, &sa, NULL); |
Line 398 main(int argc, char *argv[])
|
Line 397 main(int argc, char *argv[])
|
if (ch != -1) |
if (ch != -1) |
(void) fcntl(STDIN_FILENO, F_SETFL, ch | O_NONBLOCK); |
(void) fcntl(STDIN_FILENO, F_SETFL, ch | O_NONBLOCK); |
if (!term_raw(STDIN_FILENO, 1)) |
if (!term_raw(STDIN_FILENO, 1)) |
error(1, _("unable to set tty to raw mode")); | fatal(_("unable to set tty to raw mode")); |
iovcnt = 0; | |
iovmax = 32; |
iovmax = 32; |
iov = ecalloc(iovmax, sizeof(*iov)); |
iov = ecalloc(iovmax, sizeof(*iov)); |
} |
} |
Line 415 main(int argc, char *argv[])
|
Line 413 main(int argc, char *argv[])
|
char last_char = '\0'; |
char last_char = '\0'; |
|
|
if (!parse_timing(buf, decimal, &idx, &seconds, &nbytes)) |
if (!parse_timing(buf, decimal, &idx, &seconds, &nbytes)) |
errorx(1, _("invalid timing file line: %s"), buf); | fatalx(_("invalid timing file line: %s"), buf); |
|
|
if (interactive) |
if (interactive) |
check_input(STDIN_FILENO, &speed); |
check_input(STDIN_FILENO, &speed); |
Line 498 main(int argc, char *argv[])
|
Line 496 main(int argc, char *argv[])
|
iovcnt = 1; |
iovcnt = 1; |
} |
} |
if (atomic_writev(STDOUT_FILENO, iov, iovcnt) == -1) |
if (atomic_writev(STDOUT_FILENO, iov, iovcnt) == -1) |
error(1, _("writing to standard output")); | fatal(_("writing to standard output")); |
} |
} |
} |
} |
term_restore(STDIN_FILENO, 1); |
term_restore(STDIN_FILENO, 1); |
Line 527 delay(double secs)
|
Line 525 delay(double secs)
|
rval = nanosleep(&ts, &rts); |
rval = nanosleep(&ts, &rts); |
} while (rval == -1 && errno == EINTR); |
} while (rval == -1 && errno == EINTR); |
if (rval == -1) { |
if (rval == -1) { |
error2(1, _("nanosleep: tv_sec %ld, tv_nsec %ld"), | fatal_nodebug("nanosleep: tv_sec %lld, tv_nsec %ld", |
(long)ts.tv_sec, (long)ts.tv_nsec); | (long long)ts.tv_sec, (long)ts.tv_nsec); |
} |
} |
} |
} |
|
|
Line 640 parse_expr(struct search_node **headp, char *argv[])
|
Line 638 parse_expr(struct search_node **headp, char *argv[])
|
continue; |
continue; |
case 'c': /* command */ |
case 'c': /* command */ |
if (av[0][1] == '\0') |
if (av[0][1] == '\0') |
errorx(1, _("ambiguous expression \"%s\""), *av); | fatalx(_("ambiguous expression \"%s\""), *av); |
if (strncmp(*av, "cwd", strlen(*av)) == 0) |
if (strncmp(*av, "cwd", strlen(*av)) == 0) |
type = ST_CWD; |
type = ST_CWD; |
else if (strncmp(*av, "command", strlen(*av)) == 0) |
else if (strncmp(*av, "command", strlen(*av)) == 0) |
Line 665 parse_expr(struct search_node **headp, char *argv[])
|
Line 663 parse_expr(struct search_node **headp, char *argv[])
|
break; |
break; |
case 't': /* tty or to date */ |
case 't': /* tty or to date */ |
if (av[0][1] == '\0') |
if (av[0][1] == '\0') |
errorx(1, _("ambiguous expression \"%s\""), *av); | fatalx(_("ambiguous expression \"%s\""), *av); |
if (strncmp(*av, "todate", strlen(*av)) == 0) |
if (strncmp(*av, "todate", strlen(*av)) == 0) |
type = ST_TODATE; |
type = ST_TODATE; |
else if (strncmp(*av, "tty", strlen(*av)) == 0) |
else if (strncmp(*av, "tty", strlen(*av)) == 0) |
Line 682 parse_expr(struct search_node **headp, char *argv[])
|
Line 680 parse_expr(struct search_node **headp, char *argv[])
|
if (av[0][1] != '\0') |
if (av[0][1] != '\0') |
goto bad; |
goto bad; |
if (stack_top + 1 == STACK_NODE_SIZE) { |
if (stack_top + 1 == STACK_NODE_SIZE) { |
errorx(1, _("too many parenthesized expressions, max %d"), | fatalx(_("too many parenthesized expressions, max %d"), |
STACK_NODE_SIZE); |
STACK_NODE_SIZE); |
} |
} |
node_stack[stack_top++] = sn; |
node_stack[stack_top++] = sn; |
Line 693 parse_expr(struct search_node **headp, char *argv[])
|
Line 691 parse_expr(struct search_node **headp, char *argv[])
|
goto bad; |
goto bad; |
/* pop */ |
/* pop */ |
if (--stack_top < 0) |
if (--stack_top < 0) |
errorx(1, _("unmatched ')' in expression")); | fatalx(_("unmatched ')' in expression")); |
if (node_stack[stack_top]) |
if (node_stack[stack_top]) |
sn->next = node_stack[stack_top]->next; |
sn->next = node_stack[stack_top]->next; |
debug_return_int(av - argv + 1); |
debug_return_int(av - argv + 1); |
bad: |
bad: |
default: |
default: |
errorx(1, _("unknown search term \"%s\""), *av); | fatalx(_("unknown search term \"%s\""), *av); |
/* NOTREACHED */ |
/* NOTREACHED */ |
} |
} |
|
|
Line 713 parse_expr(struct search_node **headp, char *argv[])
|
Line 711 parse_expr(struct search_node **headp, char *argv[])
|
av += parse_expr(&newsn->u.expr, av + 1); |
av += parse_expr(&newsn->u.expr, av + 1); |
} else { |
} else { |
if (*(++av) == NULL) |
if (*(++av) == NULL) |
errorx(1, _("%s requires an argument"), av[-1]); | fatalx(_("%s requires an argument"), av[-1]); |
#ifdef HAVE_REGCOMP |
#ifdef HAVE_REGCOMP |
if (type == ST_PATTERN) { |
if (type == ST_PATTERN) { |
if (regcomp(&newsn->u.cmdre, *av, REG_EXTENDED|REG_NOSUB) != 0) |
if (regcomp(&newsn->u.cmdre, *av, REG_EXTENDED|REG_NOSUB) != 0) |
errorx(1, _("invalid regular expression: %s"), *av); | fatalx(_("invalid regular expression: %s"), *av); |
} else |
} else |
#endif |
#endif |
if (type == ST_TODATE || type == ST_FROMDATE) { |
if (type == ST_TODATE || type == ST_FROMDATE) { |
newsn->u.tstamp = get_date(*av); |
newsn->u.tstamp = get_date(*av); |
if (newsn->u.tstamp == -1) |
if (newsn->u.tstamp == -1) |
errorx(1, _("could not parse date \"%s\""), *av); | fatalx(_("could not parse date \"%s\""), *av); |
} else { |
} else { |
newsn->u.ptr = *av; |
newsn->u.ptr = *av; |
} |
} |
Line 736 parse_expr(struct search_node **headp, char *argv[])
|
Line 734 parse_expr(struct search_node **headp, char *argv[])
|
sn = newsn; |
sn = newsn; |
} |
} |
if (stack_top) |
if (stack_top) |
errorx(1, _("unmatched '(' in expression")); | fatalx(_("unmatched '(' in expression")); |
if (or) |
if (or) |
errorx(1, _("illegal trailing \"or\"")); | fatalx(_("illegal trailing \"or\"")); |
if (not) |
if (not) |
errorx(1, _("illegal trailing \"!\"")); | fatalx(_("illegal trailing \"!\"")); |
|
|
debug_return_int(av - argv); |
debug_return_int(av - argv); |
} |
} |
Line 783 match_expr(struct search_node *head, struct log_info *
|
Line 781 match_expr(struct search_node *head, struct log_info *
|
if (rc && rc != REG_NOMATCH) { |
if (rc && rc != REG_NOMATCH) { |
char buf[BUFSIZ]; |
char buf[BUFSIZ]; |
regerror(rc, &sn->u.cmdre, buf, sizeof(buf)); |
regerror(rc, &sn->u.cmdre, buf, sizeof(buf)); |
errorx(1, "%s", buf); | fatalx("%s", buf); |
} |
} |
matched = rc == REG_NOMATCH ? 0 : 1; |
matched = rc == REG_NOMATCH ? 0 : 1; |
#else |
#else |
Line 958 session_compare(const void *v1, const void *v2)
|
Line 956 session_compare(const void *v1, const void *v2)
|
return strcmp(s1, s2); |
return strcmp(s1, s2); |
} |
} |
|
|
/* XXX - always returns 0, calls error() on failure */ | /* XXX - always returns 0, calls fatal() on failure */ |
static int |
static int |
find_sessions(const char *dir, REGEX_T *re, const char *user, const char *tty) |
find_sessions(const char *dir, REGEX_T *re, const char *user, const char *tty) |
{ |
{ |
Line 977 find_sessions(const char *dir, REGEX_T *re, const char
|
Line 975 find_sessions(const char *dir, REGEX_T *re, const char
|
|
|
d = opendir(dir); |
d = opendir(dir); |
if (d == NULL) |
if (d == NULL) |
error(1, _("unable to open %s"), dir); | fatal(_("unable to open %s"), dir); |
|
|
/* XXX - would be faster to chdir and use relative names */ |
/* XXX - would be faster to chdir and use relative names */ |
sdlen = strlcpy(pathbuf, dir, sizeof(pathbuf)); |
sdlen = strlcpy(pathbuf, dir, sizeof(pathbuf)); |
if (sdlen + 1 >= sizeof(pathbuf)) { |
if (sdlen + 1 >= sizeof(pathbuf)) { |
errno = ENAMETOOLONG; |
errno = ENAMETOOLONG; |
error(1, "%s/", dir); | fatal("%s/", dir); |
} |
} |
pathbuf[sdlen++] = '/'; |
pathbuf[sdlen++] = '/'; |
pathbuf[sdlen] = '\0'; |
pathbuf[sdlen] = '\0'; |
Line 1022 find_sessions(const char *dir, REGEX_T *re, const char
|
Line 1020 find_sessions(const char *dir, REGEX_T *re, const char
|
"%s/log", sessions[i]); |
"%s/log", sessions[i]); |
if (len <= 0 || len >= sizeof(pathbuf) - sdlen) { |
if (len <= 0 || len >= sizeof(pathbuf) - sdlen) { |
errno = ENAMETOOLONG; |
errno = ENAMETOOLONG; |
error(1, "%s/%s/log", dir, sessions[i]); | fatal("%s/%s/log", dir, sessions[i]); |
} |
} |
efree(sessions[i]); |
efree(sessions[i]); |
|
|
Line 1041 find_sessions(const char *dir, REGEX_T *re, const char
|
Line 1039 find_sessions(const char *dir, REGEX_T *re, const char
|
debug_return_int(0); |
debug_return_int(0); |
} |
} |
|
|
/* XXX - always returns 0, calls error() on failure */ | /* XXX - always returns 0, calls fatal() on failure */ |
static int |
static int |
list_sessions(int argc, char **argv, const char *pattern, const char *user, |
list_sessions(int argc, char **argv, const char *pattern, const char *user, |
const char *tty) |
const char *tty) |
Line 1057 list_sessions(int argc, char **argv, const char *patte
|
Line 1055 list_sessions(int argc, char **argv, const char *patte
|
if (pattern) { |
if (pattern) { |
re = &rebuf; |
re = &rebuf; |
if (regcomp(re, pattern, REG_EXTENDED|REG_NOSUB) != 0) |
if (regcomp(re, pattern, REG_EXTENDED|REG_NOSUB) != 0) |
errorx(1, _("invalid regex: %s"), pattern); | fatalx(_("invalid regular expression: %s"), pattern); |
} |
} |
#else |
#else |
re = (char *) pattern; |
re = (char *) pattern; |
Line 1207 help(void)
|
Line 1205 help(void)
|
} |
} |
|
|
/* |
/* |
* Cleanup hook for error()/errorx() | * Cleanup hook for fatal()/fatalx() |
*/ |
*/ |
void | static void |
cleanup(int signo) | sudoreplay_cleanup(void) |
{ |
{ |
term_restore(STDIN_FILENO, 0); |
term_restore(STDIN_FILENO, 0); |
if (signo) | } |
kill(getpid(), signo); | |
| /* |
| * Signal handler for SIGINT, SIGKILL, SIGTERM, SIGHUP |
| * Must be installed with SA_RESETHAND enabled. |
| */ |
| static void |
| sudoreplay_handler(int signo) |
| { |
| term_restore(STDIN_FILENO, 0); |
| kill(getpid(), signo); |
} |
} |