From: Todd C. Miller Date: Wed, 23 May 2012 14:55:54 +0000 (-0400) Subject: If I/O log file includes rows + cols, warn if the user's tty is X-Git-Tag: SUDO_1_7_10~102 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b12c50880bd1ae0561823a29bfd532ba0949fae3;p=sudo If I/O log file includes rows + cols, warn if the user's tty is not big enough. --HG-- branch : 1.7 --- diff --git a/Makefile.in b/Makefile.in index d641c8b78..af3430bb5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -114,8 +114,8 @@ SRCS = aix.c alias.c alloc.c audit.c boottime.c bsm_audit.c check.c \ parse_args.c pwutil.c secure_path.c set_perms.c setsid.c sigaction.c \ snprintf.c strcasecmp.c strerror.c strlcat.c strlcpy.c strsignal.c \ sudo.c sudo_noexec.c sudo_edit.c sudo_nss.c term.c testsudoers.c \ - tgetpass.c toke.c toke.l toke_util.c tsgetgrpw.c ttyname.c utimes.c \ - vasgroups.c visudo.c zero_bytes.c redblack.c selinux.c sesh.c \ + tgetpass.c toke.c toke.l toke_util.c tsgetgrpw.c ttyname.c ttysize.c \ + utimes.c vasgroups.c visudo.c zero_bytes.c redblack.c selinux.c sesh.c \ sudoreplay.c getdate.c getdate.y getline.c timestr.c $(AUTH_SRCS) AUTH_SRCS = auth/afs.c auth/aix_auth.c auth/bsdauth.c auth/dce.c auth/fwtk.c \ @@ -143,7 +143,7 @@ SUDO_OBJS = $(AUTH_OBJS) @SUDO_OBJS@ audit.o boottime.o check.o env.o \ VISUDO_OBJS = visudo.o fileops.o gettime.o goodpath.o find_path.o -REPLAY_OBJS = getdate.o sudoreplay.o +REPLAY_OBJS = getdate.o sudoreplay.o ttysize.o TEST_OBJS = interfaces.o testsudoers.o tsgetgrpw.o @@ -395,6 +395,8 @@ tgetpass.o: $(srcdir)/tgetpass.c $(SUDODEP) $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/tgetpass.c ttyname.o: $(srcdir)/ttyname.c $(SUDODEP) $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/ttyname.c +ttysize.o: $(srcdir)/ttysize.c $(SUDODEP) + $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/ttysize.c timestr.o: $(srcdir)/timestr.c $(srcdir)/missing.h config.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/timestr.c toke.o: $(devdir)/toke.c $(SUDODEP) $(srcdir)/parse.h $(srcdir)/list.h $(srcdir)/toke.h $(devdir)/gram.h diff --git a/alloc.c b/alloc.c index fc04da584..a341caa24 100644 --- a/alloc.c +++ b/alloc.c @@ -196,6 +196,30 @@ estrdup(src) return dst; } +/* + * estrdup() is like strndup(3) except that it exits with an error if + * malloc(3) fails. NOTE: unlike strdup(3), estrdup(NULL) is legal. + */ +char * +estrndup(src, maxlen) + const char *src; + size_t maxlen; +{ + char *dst = NULL; + size_t len = 0; + + if (src != NULL) { + while (maxlen != 0 && src[len] != '\0') { + len++; + maxlen--; + } + dst = (char *) emalloc(len + 1); + (void) memcpy(dst, src, len); + dst[len] = '\0'; + } + return dst; +} + /* * easprintf() calls vasprintf() and exits with an error if vasprintf() * returns -1 (out of memory). diff --git a/alloc.h b/alloc.h index 6f11cb748..dd7d2346c 100644 --- a/alloc.h +++ b/alloc.h @@ -28,6 +28,7 @@ void *emalloc2(size_t, size_t); void *erealloc(void *, size_t); void *erealloc3(void *, size_t, size_t); char *estrdup(const char *); +char *estrndup(const char *, size_t); #else # include int easprintf(); @@ -39,6 +40,7 @@ void *emalloc2(); void *erealloc(); void *erealloc3(); char *estrdup(); +char *estrndup(); #endif /* __STDC__ */ #endif /* _SUDO_ALLOC_H */ diff --git a/sudoreplay.c b/sudoreplay.c index 78d8028c3..9e49c33f7 100644 --- a/sudoreplay.c +++ b/sudoreplay.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 Todd C. Miller + * Copyright (c) 2009-2012 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -135,6 +135,8 @@ struct log_info { char *tty; char *cmd; time_t tstamp; + int rows; + int cols; }; /* @@ -192,6 +194,7 @@ extern time_t get_date __P((char *)); extern char *get_timestr __P((time_t, int)); extern int term_raw __P((int, int)); extern int term_restore __P((int, int)); +extern void get_ttysize __P((int *rowp, int *colp)); RETSIGTYPE cleanup __P((int)); static int list_sessions __P((int, char **, const char *, const char *, const char *)); @@ -202,6 +205,8 @@ static void help __P((void)) __attribute__((__noreturn__)); static void usage __P((int)); static int open_io_fd __P((char *pathbuf, int len, const char *suffix, union io_fd *fdp)); static int parse_timing __P((const char *buf, const char *decimal, int *idx, double *seconds, size_t *nbytes)); +static struct log_info *parse_logfile __P((char *logfile)); +static void free_log_info __P((struct log_info *li)); #ifdef HAVE_REGCOMP # define REGEX_T regex_t @@ -229,14 +234,15 @@ main(argc, argv) char *argv[]; { int ch, idx, plen, nready, interactive = 0, listonly = 0; + int rows = 0, cols = 0; const char *id, *user = NULL, *pattern = NULL, *tty = NULL, *decimal = "."; char path[PATH_MAX], buf[LINE_MAX], *cp, *ep; double seconds, to_wait, speed = 1.0, max_wait = 0; - FILE *lfile; fd_set *fdsw; sigaction_t sa; size_t len, nbytes, nread, off; ssize_t nwritten; + struct log_info *li; Argc = argc; Argv = argv; @@ -326,23 +332,25 @@ main(argc, argv) } } - /* Read log file. */ + /* Parse log file. */ path[plen] = '\0'; strlcat(path, "/log", sizeof(path)); - lfile = fopen(path, "r"); - if (lfile == NULL) - error(1, "unable to open %s", path); - cp = NULL; - len = 0; - /* Pull out command (third line). */ - if (getline(&cp, &len, lfile) == -1 || - getline(&cp, &len, lfile) == -1 || - getline(&cp, &len, lfile) == -1) { - errorx(1, "invalid log file %s", path); + if ((li = parse_logfile(path)) == NULL) + exit(1); + printf("Replaying sudo session: %s", li->cmd); + + /* Make sure the terminal is large enough. */ + get_ttysize(&rows, &cols); + if (li->rows != 0 && li->cols != 0) { + if (li->rows > rows) { + printf("Warning: your terminal is too small to properly replay the log.\n"); + printf("Log geometry is %d x %d, your terminal's geometry is %d x %d.", li->rows, li->cols, rows, cols); + } } - printf("Replaying sudo session: %s", cp); - free(cp); - fclose(lfile); + + /* Done with parsed log file. */ + free_log_info(li); + li = NULL; fflush(stdout); memset(&sa, 0, sizeof(sa)); @@ -669,23 +677,19 @@ match_expr(head, log) return matched; } -static int -list_session(logfile, re, user, tty) +static struct log_info * +parse_logfile(logfile) char *logfile; - REGEX_T *re; - const char *user; - const char *tty; { FILE *fp; - char *buf = NULL, *cmd = NULL, *cwd = NULL, idbuf[7], *idstr, *cp; - struct log_info li; + char *buf = NULL, *cp, *ep; size_t bufsize = 0, cwdsize = 0, cmdsize = 0; - int rval = -1; + struct log_info *li = NULL; fp = fopen(logfile, "r"); if (fp == NULL) { warning("unable to open %s", logfile); - goto done; + goto bad; } /* @@ -694,45 +698,103 @@ list_session(logfile, re, user, tty) * 2) cwd * 3) command with args */ + li = ecalloc(1, sizeof(*li)); if (getline(&buf, &bufsize, fp) == -1 || - getline(&cwd, &cwdsize, fp) == -1 || - getline(&cmd, &cmdsize, fp) == -1) { - goto done; + getline(&li->cwd, &cwdsize, fp) == -1 || + getline(&li->cmd, &cmdsize, fp) == -1) { + goto bad; } - /* crack the log line: timestamp:user:runas_user:runas_group:tty */ + /* Strip the newline from the cwd and command. */ + li->cwd[strcspn(li->cwd, "\n")] = '\0'; + li->cmd[strcspn(li->cmd, "\n")] = '\0'; + + /* + * Crack the log line (rows and cols not present in old versions). + * timestamp:user:runas_user:runas_group:tty:rows:cols + */ buf[strcspn(buf, "\n")] = '\0'; - if ((li.tstamp = atoi(buf)) == 0) - goto done; - if ((cp = strchr(buf, ':')) == NULL) - goto done; - *cp++ = '\0'; - li.user = cp; + /* timestamp */ + if ((ep = strchr(buf, ':')) == NULL) + goto bad; + if ((li->tstamp = atoi(buf)) == 0) + goto bad; - if ((cp = strchr(cp, ':')) == NULL) - goto done; - *cp++ = '\0'; - li.runas_user = cp; + /* user */ + cp = ep + 1; + if ((ep = strchr(cp, ':')) == NULL) + goto bad; + li->user = estrndup(cp, (size_t)(ep - cp)); - if ((cp = strchr(cp, ':')) == NULL) - goto done; - *cp++ = '\0'; - li.runas_group = cp; + /* runas user */ + cp = ep + 1; + if ((ep = strchr(cp, ':')) == NULL) + goto bad; + li->runas_user = estrndup(cp, (size_t)(ep - cp)); - if ((cp = strchr(cp, ':')) == NULL) - goto done; - *cp++ = '\0'; - li.tty = cp; + /* runas group */ + cp = ep + 1; + if ((ep = strchr(cp, ':')) == NULL) + goto bad; + if (cp != ep) + li->runas_group = estrndup(cp, (size_t)(ep - cp)); - cwd[strcspn(cwd, "\n")] = '\0'; - li.cwd = cwd; + /* tty, followed by optional rows + columns */ + cp = ep + 1; + if ((ep = strchr(cp, ':')) == NULL) { + li->tty = estrdup(cp); + } else { + li->tty = estrndup(cp, (size_t)(ep - cp)); + cp = ep + 1; + li->rows = atoi(cp); + if ((ep = strchr(cp, ':')) != NULL) { + cp = ep + 1; + li->cols = atoi(cp); + } + } + fclose(fp); + efree(buf); + return li; - cmd[strcspn(cmd, "\n")] = '\0'; - li.cmd = cmd; +bad: + fclose(fp); + efree(buf); + free_log_info(li); + return NULL; +} + +static void +free_log_info(li) + struct log_info *li; +{ + if (li != NULL) { + efree(li->cwd); + efree(li->user); + efree(li->runas_user); + efree(li->runas_group); + efree(li->tty); + efree(li->cmd); + efree(li); + } +} + +static int +list_session(logfile, re, user, tty) + char *logfile; + REGEX_T *re; + const char *user; + const char *tty; +{ + char idbuf[7], *idstr, *cp; + struct log_info *li; + int rval = -1; + + if ((li = parse_logfile(logfile)) == NULL) + goto done; /* Match on search expression if there is one. */ - if (search_expr && !match_expr(search_expr, &li)) + if (search_expr && !match_expr(search_expr, li)) goto done; /* Convert from /var/log/sudo-sessions/00/00/01/log to 000001 */ @@ -751,16 +813,17 @@ list_session(logfile, re, user, tty) cp[strlen(cp) - 4] = '\0'; idstr = cp; } + /* XXX - print rows + cols? */ printf("%s : %s : TTY=%s ; CWD=%s ; USER=%s ; ", - get_timestr(li.tstamp, 1), li.user, li.tty, li.cwd, li.runas_user); - if (*li.runas_group) - printf("GROUP=%s ; ", li.runas_group); - printf("TSID=%s ; COMMAND=%s\n", idstr, li.cmd); + get_timestr(li->tstamp, 1), li->user, li->tty, li->cwd, li->runas_user); + if (li->runas_group) + printf("GROUP=%s ; ", li->runas_group); + printf("TSID=%s ; COMMAND=%s\n", idstr, li->cmd); rval = 0; done: - fclose(fp); + free_log_info(li); return rval; } diff --git a/ttysize.c b/ttysize.c new file mode 100644 index 000000000..0e42a0888 --- /dev/null +++ b/ttysize.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010 Todd C. Miller + * + * 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. + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ +#include + +#include "missing.h" + +/* Compatibility with older tty systems. */ +#if !defined(TIOCGWINSZ) && defined(TIOCGSIZE) +# define TIOCGWINSZ TIOCGSIZE +# define winsize ttysize +# define ws_col ts_cols +# define ws_row ts_lines +#endif + +#ifdef TIOCGWINSZ +static int +get_ttysize_ioctl(int *rowp, int *colp) +{ + struct winsize wsize; + + if (ioctl(STDERR_FILENO, TIOCGWINSZ, &wsize) == 0 && + wsize.ws_row != 0 && wsize.ws_col != 0) { + *rowp = wsize.ws_row; + *colp = wsize.ws_col; + return 0; + } + return -1; +} +#else +static int +get_ttysize_ioctl(int *rowp, int *colp) +{ + return -1; +} +#endif /* TIOCGWINSZ */ + +void +get_ttysize(int *rowp, int *colp) +{ + if (get_ttysize_ioctl(rowp, colp) == -1) { + char *p; + + /* Fall back on $LINES and $COLUMNS. */ + if ((p = getenv("LINES")) == NULL || (*rowp = atoi(p)) <= 0) + *rowp = 24; + if ((p = getenv("COLUMNS")) == NULL || (*colp = atoi(p)) <= 0) + *colp = 80; + } + + return; +}