2
0
mirror of https://github.com/sudo-project/sudo.git synced 2025-08-31 14:25:15 +00:00

Implement search expressions in sudoreplay similar in concept to

what find or tcpdump uses.  TODO: date ranges
This commit is contained in:
Todd C. Miller
2009-09-13 22:02:07 +00:00
parent 10fa87dfa8
commit 3724c2e900
5 changed files with 386 additions and 120 deletions

View File

@@ -137,7 +137,7 @@ SUDO_OBJS = $(COMMON_OBJS) $(AUTH_OBJS) @SUDO_OBJS@ audit.o check.o env.o \
VISUDO_OBJS = $(COMMON_OBJS) visudo.o fileops.o gettime.o goodpath.o \
find_path.o pwutil.o
REPLAY_OBJS = sudoreplay.o error.o
REPLAY_OBJS = sudoreplay.o error.o alloc.o
TEST_OBJS = $(COMMON_OBJS) interfaces.o testsudoers.o tsgetgrpw.o tspwutil.o
@@ -154,8 +154,8 @@ DISTFILES = $(SRCS) $(HDRS) ChangeLog HISTORY INSTALL INSTALL.configure \
schema.ActiveDirectory schema.OpenLDAP schema.iPlanet sudo.cat \
sudo.man.in sudo.pod sudo.psf sudo_usage.h.in sudoers sudoers.cat \
sudoers.man.in sudoers.pod sudoers.ldap.cat sudoers.ldap.man.in \
sudoers.ldap.pod sudoers2ldif sudoreplay.cat sudoreplay.man.in \
sudoreplay.pod visudo.cat visudo.man.in visudo.pod auth/API
sudoers.ldap.pod sudoers2ldif sudoreplay.cat sudoreplay.man.in \
sudoreplay.pod visudo.cat visudo.man.in visudo.pod auth/API
BINFILES= ChangeLog HISTORY LICENSE README TROUBLESHOOTING \
UPGRADE install-sh mkinstalldirs sample.syslog.conf sample.sudoers \

View File

@@ -94,9 +94,58 @@ int Argc;
char **Argv;
const char *session_dir = _PATH_SUDO_SESSDIR;
void usage __P((void));
void delay __P((double));
int list_sessions __P((int, char **, const char *, const char *, const char *));
/*
* Info present in the transcript log file
*/
struct log_info {
char *user;
char *runas_user;
char *runas_group;
char *tty;
char *cmd;
time_t tstamp;
};
/*
* Handle expressions like:
* ( user millert or user root ) and tty console and command /bin/sh
* XXX - also time-based
*/
struct search_node {
struct search_node *next;
#define ST_EXPR 1
#define ST_TTY 2
#define ST_USER 3
#define ST_PATTERN 4
#define ST_RUNASUSER 5
#define ST_RUNASGROUP 6
char type;
char negated;
char or;
char pad;
union {
#ifdef HAVE_REGCOMP
regex_t cmdre;
#endif
char *tty;
char *user;
char *pattern;
char *runas_group;
char *runas_user;
struct search_node *expr;
void *ptr;
} u;
} *search_expr;
#define STACK_NODE_SIZE 32
static struct search_node *node_stack[32];
static int stack_top;
extern void *emalloc __P((size_t));
static int list_sessions __P((int, char **, const char *, const char *, const char *));
static int parse_expr __P((struct search_node **, char **));
static void delay __P((double));
static void usage __P((void));
#ifdef HAVE_REGCOMP
# define REGEX_T regex_t
@@ -136,7 +185,7 @@ main(argc, argv)
Argv = argv;
/* XXX - timestamp option? (begin,end) */
while ((ch = getopt(argc, argv, "d:lm:p:s:t:u:V")) != -1) {
while ((ch = getopt(argc, argv, "d:lm:s:V")) != -1) {
switch(ch) {
case 'd':
session_dir = optarg;
@@ -150,21 +199,12 @@ main(argc, argv)
if (*ep != '\0' || errno != 0)
error(1, "invalid max wait: %s", optarg);
break;
case 'p':
pattern = optarg;
break;
case 's':
errno = 0;
speed = strtod(optarg, &ep);
if (*ep != '\0' || errno != 0)
error(1, "invalid speed factor: %s", optarg);
break;
case 't':
tty = optarg;
break;
case 'u':
user = optarg;
break;
case 'V':
(void) printf("%s version %s\n", getprogname(), PACKAGE_VERSION);
exit(0);
@@ -177,9 +217,8 @@ main(argc, argv)
argc -= optind;
argv += optind;
if (listonly) {
if (listonly)
exit(list_sessions(argc, argv, pattern, user, tty));
}
if (argc != 1)
usage();
@@ -279,7 +318,7 @@ nanosleep(ts, rts)
}
#endif
void
static void
delay(secs)
double secs;
{
@@ -303,14 +342,149 @@ delay(secs)
error(1, "nanosleep: tv_sec %ld, tv_nsec %ld", ts.tv_sec, ts.tv_nsec);
}
struct log_info {
char *user;
char *runas_user;
char *runas_group;
char *tty;
char *cmd;
time_t tstamp;
};
/*
* Build expression list from search args
* XXX - add additional search terms
*/
static int
parse_expr(headp, argv)
struct search_node **headp;
char **argv;
{
struct search_node *sn, *newsn;
char or = 0, not = 0, type;
char **av;
sn = *headp;
for (av = argv; *av; av++) {
switch (*av[0]) {
case 'a': /* and (ignore) */
continue;
case 'o': /* or */
or = 1;
continue;
case '!': /* negate */
not = 1;
continue;
case 'c': /* command */
type = ST_PATTERN;
break;
case 'g': /* runas group */
type = ST_RUNASGROUP;
break;
case 'r': /* runas user */
type = ST_RUNASUSER;
break;
case 't': /* tty */
type = ST_TTY;
break;
case 'u': /* user */
type = ST_USER;
break;
case '(': /* start sub-expression */
if (stack_top + 1 == STACK_NODE_SIZE) {
errorx(1, "too many parenthesized expressions, max %d",
STACK_NODE_SIZE);
}
node_stack[stack_top++] = sn;
type = ST_EXPR;
break;
case ')': /* end sub-expression */
/* pop */
if (--stack_top < 0)
errorx(1, "unmatched ')' in expression");
if (node_stack[stack_top])
sn->next = node_stack[stack_top]->next;
return(av - argv + 1);
default:
errorx(1, "unknown search term \"%s\"", *av);
/* NOTREACHED */
}
/* Allocate new search node */
newsn = emalloc(sizeof(*newsn));
newsn->next = NULL;
newsn->type = type;
newsn->or = or;
newsn->negated = not;
if (type == ST_EXPR) {
av += parse_expr(&newsn->u.expr, av + 1);
} else {
if (*(++av) == NULL)
errorx(1, "%s requires an argument", av[-1]);
#ifdef HAVE_REGCOMP
if (type == ST_PATTERN) {
if (regcomp(&newsn->u.cmdre, *av, REG_EXTENDED|REG_NOSUB) != 0)
errorx(1, "invalid regex: %s", *av);
} else
#endif
newsn->u.ptr = *av;
}
not = or = 0; /* reset state */
if (sn)
sn->next = newsn;
else
*headp = newsn;
sn = newsn;
}
if (stack_top)
errorx(1, "unmatched '(' in expression");
if (or)
errorx(1, "illegal trailing \"or\"");
if (not)
errorx(1, "illegal trailing \"!\"");
return(av - argv);
}
static int
match_expr(head, log)
struct search_node *head;
struct log_info *log;
{
struct search_node *sn;
int matched = 1, rc;
for (sn = head; sn; sn = sn->next) {
/* If we have no match, skip up to the next OR entry. */
if (!matched && !sn->or)
continue;
switch (sn->type) {
case ST_EXPR:
matched = match_expr(sn->u.expr, log);
break;
case ST_TTY:
matched = strcmp(sn->u.tty, log->tty) == 0;
break;
case ST_RUNASGROUP:
matched = strcmp(sn->u.runas_group, log->runas_group) == 0;
break;
case ST_RUNASUSER:
matched = strcmp(sn->u.runas_user, log->runas_user) == 0;
break;
case ST_USER:
matched = strcmp(sn->u.user, log->user) == 0;
break;
case ST_PATTERN:
#ifdef HAVE_REGCOMP
rc = regexec(&sn->u.cmdre, log->cmd, 0, NULL, 0);
if (rc && rc != REG_NOMATCH) {
char buf[BUFSIZ];
regerror(rc, &sn->u.cmdre, buf, sizeof(buf));
errorx(1, "%s", buf);
}
matched = rc == REG_NOMATCH ? 0 : 1;
#else
matched = strstr(log.cmd, sn->u.pattern) != NULL;
#endif
break;
}
if (sn->negated)
matched = !matched;
}
return(matched);
}
static int
list_session_dir(pathbuf, re, user, tty)
@@ -385,28 +559,9 @@ list_session_dir(pathbuf, re, user, tty)
cmdbuf[strcspn(cmdbuf, "\n")] = '\0';
li.cmd = cmdbuf;
/*
* Select based on user/tty/regex if applicable.
* XXX - select on time and/or runas bits too?
*/
if (user && strcmp(user, li.user) != 0)
/* Match on search expression if there is one. */
if (search_expr && !match_expr(search_expr, &li))
continue;
if (tty && strcmp(tty, li.tty) != 0)
continue;
if (re) {
#ifdef HAVE_REGCOMP
int rc = regexec(re, li.cmd, 0, NULL, 0);
if (rc) {
if (rc == REG_NOMATCH)
continue;
regerror(rc, re, buf, sizeof(buf));
errorx(1, "%s", buf);
}
#else
if (strstr(li.cmd, re) == NULL)
continue;
#endif /* HAVE_REGCOMP */
}
/* Convert from /var/log/sudo-sessions/00/00/01 to 000001 */
idstr[0] = pathbuf[plen - 5];
@@ -417,13 +572,13 @@ list_session_dir(pathbuf, re, user, tty)
idstr[5] = pathbuf[plen + 2];
idstr[6] = '\0';
/* XXX - better format (timestamp?) */
printf("%s: %s %d (%s:%s) %s\n", idstr, li.user, li.tstamp,
printf("%s: %s %ld (%s:%s) %s\n", idstr, li.user, (long)li.tstamp,
li.runas_user, li.runas_group, li.cmd);
}
return(0);
}
int
static int
list_sessions(argc, argv, pattern, user, tty)
int argc;
char **argv;
@@ -437,6 +592,9 @@ list_sessions(argc, argv, pattern, user, tty)
size_t sdlen;
char pathbuf[PATH_MAX];
/* Parse search expression if present */
parse_expr(&search_expr, argv);
d1 = opendir(session_dir);
if (d1 == NULL)
error(1, "unable to open %s", session_dir);
@@ -488,14 +646,14 @@ list_sessions(argc, argv, pattern, user, tty)
return(0);
}
void
static void
usage()
{
fprintf(stderr,
"usage: %s [-d directory] [-m max_wait] [-s speed_factor] ID\n",
getprogname());
fprintf(stderr,
"usage: %s [-d directory] [-p pattern] [-t tty] [-u username] -l\n",
"usage: %s [-d directory] -l [search expression]\n",
getprogname());
exit(1);
}

View File

@@ -10,7 +10,7 @@ NNAAMMEE
SSYYNNOOPPSSIISS
ssuuddoorreeppllaayy [--dd _d_i_r_e_c_t_o_r_y] [--mm _m_a_x___w_a_i_t] [--ss _s_p_e_e_d___f_a_c_t_o_r] ID
ssuuddoorreeppllaayy [--dd _d_i_r_e_c_t_o_r_y] [--pp _p_a_t_t_e_r_n] [--tt _t_t_y] [--uu _u_s_e_r] -l
ssuuddoorreeppllaayy [--dd _d_i_r_e_c_t_o_r_y] -l [search expression]
DDEESSCCRRIIPPTTIIOONN
ssuuddoorreeppllaayy plays back or lists the session logs created by ssuuddoo. When
@@ -31,8 +31,56 @@ OOPPTTIIOONNSS
default, _/_v_a_r_/_l_o_g_/_s_u_d_o_-_s_e_s_s_i_o_n_s.
-l Enable "list mode". In this mode, ssuuddoorreeppllaayy will list
available session IDs. The -p, <-t> and <-u> options can
be used to restrict the IDs that are displayed.
available session IDs. If a _s_e_a_r_c_h _e_x_p_r_e_s_s_i_o_n is
specified, it will be used to restrict the IDs that are
displayed. An expression is composed of the following
predicates:
user _u_s_e_r_n_a_m_e
Evaluates to true if the ID matches a command run
by _u_s_e_r_n_a_m_e.
command _c_o_m_m_a_n_d _p_a_t_t_e_r_n
Evaluates to true if the command run matches
_c_o_m_m_a_n_d _p_a_t_t_e_r_n. On systems with POSIX regular
expression support, the pattern may be an extended
regular expression. On systems without POSIX
regular expression support, a simple substring
match is performed instead.
tty _t_t_y Evaluates to true if the command was run on the
specified terminal device. The _t_t_y should be
specified without the _/_d_e_v_/ prefix, e.g. _t_t_y_0_1
instead of _/_d_e_v_/_t_t_y_0_1.
runas _r_u_n_a_s___u_s_e_r
Evaluates to true if the command was run as the
specified _r_u_n_a_s___u_s_e_r. Note that ssuuddoo runs commands
as user _r_o_o_t by default.
1.7.2 September 13, 2009 1
SUDOREPLAY(1m) MAINTENANCE COMMANDS SUDOREPLAY(1m)
runas _r_u_n_a_s___g_r_o_u_p
Evaluates to true if the command was run with the
specified _r_u_n_a_s___g_r_o_u_p. Note that unless a
_r_u_n_a_s___g_r_o_u_p was explicitly specified when ssuuddoo was
run this field will be empty in the log.
Predicates may be combined using _a_n_d, _o_r and _! operators as
well as '(' and ')' for grouping (note that parentheses
must generally be escaped from the shell). The _a_n_d
operator is optional, adjacent predicates have an implied
_a_n_d unless separated by an _o_r.
-m _m_a_x___w_a_i_t Specify an upper bound on how long to wait between key
presses or output data. By default, ssuuddoo__rreeppllaayy will
@@ -43,12 +91,6 @@ OOPPTTIIOONNSS
_m_a_x___w_a_i_t seconds. The value may be specified as a floating
point number, .e.g. _2_._5.
-p _p_a_t_t_e_r_n Restrict list output to sessions where the command matches
_p_a_t_t_e_r_n. On systems with POSIX regular expression support,
the pattern may be an extended regular expression. On
systems without POSIX regular expression support, a simple
substring match is performed instead.
-s _s_p_e_e_d___f_a_c_t_o_r
This option causes ssuuddoorreeppllaayy to adjust the number of
seconds it will wait between key presses or program output.
@@ -57,26 +99,6 @@ OOPPTTIIOONNSS
fast whereas a _s_p_e_e_d___f_a_c_t_o_r of <.5> would make the output
twice as slow.
-t _t_t_y Restrict list output to sessions where the command was run
1.7.2 August 30, 2009 1
SUDOREPLAY(1m) MAINTENANCE COMMANDS SUDOREPLAY(1m)
on the specified terming device. The _t_t_y should be
specified without the _/_d_e_v_/ prefix, e.g. _t_t_y_0_1 instead of
_/_d_e_v_/_t_t_y_0_1.
-u _u_s_e_r Restrict list output to sessions where the command was run
by _u_s_e_r.
-V The --VV (version) option causes ssuuddoorreeppllaayy to print its
version number and exit.
@@ -102,6 +124,18 @@ BBUUGGSS
If you feel you have found a bug in ssuuddoorreeppllaayy, please submit a bug
report at http://www.sudo.ws/sudo/bugs/
1.7.2 September 13, 2009 2
SUDOREPLAY(1m) MAINTENANCE COMMANDS SUDOREPLAY(1m)
SSUUPPPPOORRTT
Limited free support is available via the sudo-users mailing list, see
http://www.sudo.ws/mailman/listinfo/sudo-users to subscribe or search
@@ -127,6 +161,38 @@ DDIISSCCLLAAIIMMEERR
1.7.2 August 30, 2009 2
1.7.2 September 13, 2009 3

View File

@@ -148,7 +148,7 @@
.\" ========================================================================
.\"
.IX Title "SUDOREPLAY @mansectsu@"
.TH SUDOREPLAY @mansectsu@ "August 30, 2009" "1.7.2" "MAINTENANCE COMMANDS"
.TH SUDOREPLAY @mansectsu@ "September 13, 2009" "1.7.2" "MAINTENANCE COMMANDS"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
@@ -159,7 +159,7 @@ sudoreplay \- replay sudo session logs
.IX Header "SYNOPSIS"
\&\fBsudoreplay\fR [\fB\-d\fR \fIdirectory\fR] [\fB\-m\fR \fImax_wait\fR] [\fB\-s\fR \fIspeed_factor\fR] \s-1ID\s0
.PP
\&\fBsudoreplay\fR [\fB\-d\fR \fIdirectory\fR] [\fB\-p\fR \fIpattern\fR] [\fB\-t\fR \fItty\fR] [\fB\-u\fR \fIuser\fR] \-l
\&\fBsudoreplay\fR [\fB\-d\fR \fIdirectory\fR] \-l [search expression]
.SH "DESCRIPTION"
.IX Header "DESCRIPTION"
\&\fBsudoreplay\fR plays back or lists the session logs created by
@@ -182,8 +182,42 @@ Use \fIdirectory\fR to for the session logs instead of the default,
.IP "\-l" 12
.IX Item "-l"
Enable \*(L"list mode\*(R". In this mode, \fBsudoreplay\fR will list available
session IDs. The \f(CW\*(C`\-p\*(C'\fR, <\-t> and <\-u> options can be used to
restrict the IDs that are displayed.
session IDs. If a \fIsearch expression\fR is specified, it will be
used to restrict the IDs that are displayed. An expression is
composed of the following predicates:
.RS 12
.IP "user \fIusername\fR" 8
.IX Item "user username"
Evaluates to true if the \s-1ID\s0 matches a command run by \fIusername\fR.
.IP "command \fIcommand pattern\fR" 8
.IX Item "command command pattern"
Evaluates to true if the command run matches \fIcommand pattern\fR.
On systems with \s-1POSIX\s0 regular expression support, the pattern may
be an extended regular expression. On systems without \s-1POSIX\s0 regular
expression support, a simple substring match is performed instead.
.IP "tty \fItty\fR" 8
.IX Item "tty tty"
Evaluates to true if the command was run on the specified terminal
device. The \fItty\fR should be specified without the \fI/dev/\fR prefix,
e.g. \fItty01\fR instead of \fI/dev/tty01\fR.
.IP "runas \fIrunas_user\fR" 8
.IX Item "runas runas_user"
Evaluates to true if the command was run as the specified \fIrunas_user\fR.
Note that \fBsudo\fR runs commands as user \fIroot\fR by default.
.IP "runas \fIrunas_group\fR" 8
.IX Item "runas runas_group"
Evaluates to true if the command was run with the specified
\&\fIrunas_group\fR. Note that unless a \fIrunas_group\fR was explicitly
specified when \fBsudo\fR was run this field will be empty in the log.
.RE
.RS 12
.Sp
Predicates may be combined using \fIand\fR, \fIor\fR and \fI!\fR operators
as well as \f(CW\*(Aq(\*(Aq\fR and \f(CW\*(Aq)\*(Aq\fR for grouping (note that parentheses
must generally be escaped from the shell). The \fIand\fR operator is
optional, adjacent predicates have an implied \fIand\fR unless separated
by an \fIor\fR.
.RE
.IP "\-m \fImax_wait\fR" 12
.IX Item "-m max_wait"
Specify an upper bound on how long to wait between key presses or
@@ -193,12 +227,6 @@ can be tedious when the session includes long pauses. When the
\&\fI\-m\fR option is specified, \fBsudoreplay\fR will limit these pauses
to at most \fImax_wait\fR seconds. The value may be specified as a
floating point number, .e.g. \fI2.5\fR.
.IP "\-p \fIpattern\fR" 12
.IX Item "-p pattern"
Restrict list output to sessions where the command matches \fIpattern\fR.
On systems with \s-1POSIX\s0 regular expression support, the pattern may
be an extended regular expression. On systems without \s-1POSIX\s0 regular
expression support, a simple substring match is performed instead.
.IP "\-s \fIspeed_factor\fR" 12
.IX Item "-s speed_factor"
This option causes \fBsudoreplay\fR to adjust the number of seconds
@@ -206,14 +234,6 @@ it will wait between key presses or program output. This can be
used to slow down or speed up the display. For example, a
\&\fIspeed_factor\fR of \fI2\fR would make the output twice as fast whereas
a \fIspeed_factor\fR of <.5> would make the output twice as slow.
.IP "\-t \fItty\fR" 12
.IX Item "-t tty"
Restrict list output to sessions where the command was run on the
specified terming device. The \fItty\fR should be specified without the
\&\fI/dev/\fR prefix, e.g. \fItty01\fR instead of \fI/dev/tty01\fR.
.IP "\-u \fIuser\fR" 12
.IX Item "-u user"
Restrict list output to sessions where the command was run by \fIuser\fR.
.IP "\-V" 12
.IX Item "-V"
The \fB\-V\fR (version) option causes \fBsudoreplay\fR to print its version number

View File

@@ -24,7 +24,7 @@ sudoreplay - replay sudo session logs
B<sudoreplay> [B<-d> I<directory>] [B<-m> I<max_wait>] [B<-s> I<speed_factor>] ID
B<sudoreplay> [B<-d> I<directory>] [B<-p> I<pattern>] [B<-t> I<tty>] [B<-u> I<user>] -l
B<sudoreplay> [B<-d> I<directory>] -l [search expression]
=head1 DESCRIPTION
@@ -53,8 +53,47 @@ F</var/log/sudo-sessions>.
=item -l
Enable "list mode". In this mode, B<sudoreplay> will list available
session IDs. The C<-p>, <-t> and <-u> options can be used to
restrict the IDs that are displayed.
session IDs. If a I<search expression> is specified, it will be
used to restrict the IDs that are displayed. An expression is
composed of the following predicates:
=over 8
=item user I<username>
Evaluates to true if the ID matches a command run by I<username>.
=item command I<command pattern>
Evaluates to true if the command run matches I<command pattern>.
On systems with POSIX regular expression support, the pattern may
be an extended regular expression. On systems without POSIX regular
expression support, a simple substring match is performed instead.
=item tty I<tty>
Evaluates to true if the command was run on the specified terminal
device. The I<tty> should be specified without the F</dev/> prefix,
e.g. F<tty01> instead of F</dev/tty01>.
=item runas I<runas_user>
Evaluates to true if the command was run as the specified I<runas_user>.
Note that B<sudo> runs commands as user I<root> by default.
=item runas I<runas_group>
Evaluates to true if the command was run with the specified
I<runas_group>. Note that unless a I<runas_group> was explicitly
specified when B<sudo> was run this field will be empty in the log.
=back
Predicates may be combined using I<and>, I<or> and I<!> operators
as well as C<'('> and C<')'> for grouping (note that parentheses
must generally be escaped from the shell). The I<and> operator is
optional, adjacent predicates have an implied I<and> unless separated
by an I<or>.
=item -m I<max_wait>
@@ -66,13 +105,6 @@ I<-m> option is specified, B<sudoreplay> will limit these pauses
to at most I<max_wait> seconds. The value may be specified as a
floating point number, .e.g. I<2.5>.
=item -p I<pattern>
Restrict list output to sessions where the command matches I<pattern>.
On systems with POSIX regular expression support, the pattern may
be an extended regular expression. On systems without POSIX regular
expression support, a simple substring match is performed instead.
=item -s I<speed_factor>
This option causes B<sudoreplay> to adjust the number of seconds
@@ -81,16 +113,6 @@ used to slow down or speed up the display. For example, a
I<speed_factor> of I<2> would make the output twice as fast whereas
a I<speed_factor> of <.5> would make the output twice as slow.
=item -t I<tty>
Restrict list output to sessions where the command was run on the
specified terming device. The I<tty> should be specified without the
F</dev/> prefix, e.g. F<tty01> instead of F</dev/tty01>.
=item -u I<user>
Restrict list output to sessions where the command was run by I<user>.
=item -V
The B<-V> (version) option causes B<sudoreplay> to print its version number