Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/sudo_iolog.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,6 @@ void *iolog_pwfilt_alloc(void);
bool iolog_pwfilt_add(void *handle, const char *pattern);
void iolog_pwfilt_free(void *handle);
bool iolog_pwfilt_remove(void *handle, const char *pattern);
bool iolog_pwfilt_run(void *handle, int event, const char *buf, size_t len, char **newbuf);
bool iolog_pwfilt_run(void *handle, bool *is_filtered, int event, const char *buf, size_t len, char **newbuf);

#endif /* SUDO_IOLOG_H */
16 changes: 8 additions & 8 deletions lib/iolog/iolog_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ TAILQ_HEAD(pwfilt_regex_list, pwfilt_regex);

struct pwfilt_handle {
struct pwfilt_regex_list filters;
bool is_filtered;
};

/*
Expand All @@ -62,7 +61,6 @@ iolog_pwfilt_alloc(void)
handle = malloc(sizeof(*handle));
if (handle != NULL) {
TAILQ_INIT(&handle->filters);
handle->is_filtered = false;
}

debug_return_ptr(handle);
Expand Down Expand Up @@ -169,9 +167,11 @@ iolog_pwfilt_remove(void *vhandle, const char *pattern)
* If logging output and filtering _is_ enabled, disable filtering.
* If logging input and filtering is enabled, replace all characters in
* buf with stars ('*') up to the next linefeed or carriage return.
* The filtering state is stored in is_filtered, not in the handle, since
* a handle may be shared by multiple sessions (as in sudo_logsrvd).
*/
bool
iolog_pwfilt_run(void *vhandle, int event, const char *buf,
iolog_pwfilt_run(void *vhandle, bool *is_filtered, int event, const char *buf,
size_t len, char **newbuf)
{
struct pwfilt_handle *handle = vhandle;
Expand All @@ -188,8 +188,8 @@ iolog_pwfilt_run(void *vhandle, int event, const char *buf,
switch (event) {
case IO_EVENT_TTYOUT:
/* If filtering passwords and we receive output, disable it. */
if (handle->is_filtered)
handle->is_filtered = false;
if (*is_filtered)
*is_filtered = false;

/* Make a copy of buf that is NUL-terminated. */
copy = malloc(len + 1);
Expand All @@ -203,20 +203,20 @@ iolog_pwfilt_run(void *vhandle, int event, const char *buf,
/* Check output for a password prompt. */
TAILQ_FOREACH(filt, &handle->filters, entries) {
if (regexec(&filt->regex, copy, 0, NULL, 0) == 0) {
handle->is_filtered = true;
*is_filtered = true;
break;
}
}
free(copy);
break;
case IO_EVENT_TTYIN:
if (handle->is_filtered) {
if (*is_filtered) {
size_t i;

for (i = 0; i < len; i++) {
/* We will stop filtering after reaching cr/nl. */
if (buf[i] == '\r' || buf[i] == '\n') {
handle->is_filtered = false;
*is_filtered = false;
break;
}
}
Expand Down
53 changes: 51 additions & 2 deletions lib/iolog/regress/iolog_filter/check_iolog_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,49 @@

sudo_dso_public int main(int argc, char *argv[]);

/*
* Two sessions that share a filter handle (as in sudo_logsrvd) must
* not affect each other's filter state.
*/
static int
test_shared_handle(void *handle)
{
bool filtered[2] = { false, false };
char *newbuf = NULL;
int errors = 0;

/* Password prompt in the first session. */
if (!iolog_pwfilt_run(handle, &filtered[0], IO_EVENT_TTYOUT,
"Password: ", 10, &newbuf))
return 1;

/* Output and input in the second session, input must not be filtered. */
if (!iolog_pwfilt_run(handle, &filtered[1], IO_EVENT_TTYOUT,
"total 0\r\n", 9, &newbuf))
return 1;
if (!iolog_pwfilt_run(handle, &filtered[1], IO_EVENT_TTYIN,
"ls\r", 3, &newbuf))
return 1;
if (newbuf != NULL) {
sudo_warnx("shared handle: input filtered without a password prompt");
errors++;
free(newbuf);
newbuf = NULL;
}

/* The password in the first session must still be filtered. */
if (!iolog_pwfilt_run(handle, &filtered[0], IO_EVENT_TTYIN,
"secret\r", 7, &newbuf))
return 1;
if (newbuf == NULL || memcmp(newbuf, "******\r", 7) != 0) {
sudo_warnx("shared handle: password not filtered");
errors++;
}
free(newbuf);

return errors;
}

int
main(int argc, char *argv[])
{
Expand Down Expand Up @@ -67,6 +110,7 @@ main(int argc, char *argv[])
struct timing_closure timing;
const char *logdir = argv[i];
char tbuf[8192], fbuf[8192];
bool is_filtered = false;
ssize_t nread;

ntests++;
Expand Down Expand Up @@ -147,8 +191,8 @@ main(int argc, char *argv[])
}

/* Apply filter. */
if (!iolog_pwfilt_run(passprompt_regex, timing.event, tbuf,
timing.u.nbytes, &newbuf)) {
if (!iolog_pwfilt_run(passprompt_regex, &is_filtered, timing.event,
tbuf, timing.u.nbytes, &newbuf)) {
errors++;
continue;
}
Expand Down Expand Up @@ -191,6 +235,11 @@ main(int argc, char *argv[])
if (iolog_timing.enabled)
iolog_close(&iolog_timing, NULL);
}

ntests++;
if (test_shared_handle(passprompt_regex) != 0)
errors++;

iolog_pwfilt_free(passprompt_regex);

if (ntests != 0) {
Expand Down
1 change: 1 addition & 0 deletions logsrvd/logsrvd.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ struct connection_closure {
bool error;
bool tls;
bool log_io;
bool pwfilt_active;
bool store_first;
bool read_instead_of_write;
bool write_instead_of_read;
Expand Down
5 changes: 3 additions & 2 deletions logsrvd/logsrvd_local.c
Original file line number Diff line number Diff line change
Expand Up @@ -662,8 +662,9 @@ store_iobuf_local(int iofd, const IoBuffer *iobuf, const uint8_t *buf,
}

if (!logsrvd_conf_iolog_log_passwords()) {
if (!iolog_pwfilt_run(logsrvd_conf_iolog_passprompt_regex(), iofd,
(char *)data.data, data.len, &newbuf))
if (!iolog_pwfilt_run(logsrvd_conf_iolog_passprompt_regex(),
&closure->pwfilt_active, iofd, (char *)data.data, data.len,
&newbuf))
goto bad;
if (newbuf != NULL)
data.data = (uint8_t *)newbuf;
Expand Down
5 changes: 4 additions & 1 deletion plugins/sudoers/iolog.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ static bool log_passwords = false;
static int iolog_dir_fd = -1;
static struct timespec last_time;
static void *passprompt_regex_handle;
static bool pwfilt_active = false;
static void sudoers_io_setops(void);

/* sudoers_io is declared at the end of this file. */
Expand Down Expand Up @@ -934,6 +935,7 @@ sudoers_io_close(int exit_status, int error)
sudo_freegrcache();
iolog_pwfilt_free(passprompt_regex_handle);
passprompt_regex_handle = NULL;
pwfilt_active = false;

/* sudoers_debug_deregister() calls sudo_debug_exit() for us. */
sudoers_debug_deregister();
Expand Down Expand Up @@ -979,7 +981,8 @@ sudoers_io_log_local(int event, const char *buf, unsigned int len,
}

if (!log_passwords && passprompt_regex_handle != NULL) {
if (!iolog_pwfilt_run(passprompt_regex_handle, event, buf, len, &newbuf))
if (!iolog_pwfilt_run(passprompt_regex_handle, &pwfilt_active, event,
buf, len, &newbuf))
debug_return_int(-1);
}

Expand Down
Loading