mirror of
https://github.com/sudo-project/sudo.git
synced 2025-08-22 01:49:11 +00:00
lib/iolog: add support for filtering password out of tty input
If a password regex is found in the tty output, tty input will be replaced with '*' chars until a newline or another tty output character is received.
This commit is contained in:
parent
86f123cd9d
commit
946404434e
1
MANIFEST
1
MANIFEST
@ -129,6 +129,7 @@ lib/iolog/iolog_clearerr.c
|
|||||||
lib/iolog/iolog_close.c
|
lib/iolog/iolog_close.c
|
||||||
lib/iolog/iolog_conf.c
|
lib/iolog/iolog_conf.c
|
||||||
lib/iolog/iolog_eof.c
|
lib/iolog/iolog_eof.c
|
||||||
|
lib/iolog/iolog_filter.c
|
||||||
lib/iolog/iolog_flush.c
|
lib/iolog/iolog_flush.c
|
||||||
lib/iolog/iolog_gets.c
|
lib/iolog/iolog_gets.c
|
||||||
lib/iolog/iolog_json.c
|
lib/iolog/iolog_json.c
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-License-Identifier: ISC
|
* SPDX-License-Identifier: ISC
|
||||||
*
|
*
|
||||||
* Copyright (c) 2009-2020 Todd C. Miller <Todd.Miller@sudo.ws>
|
* Copyright (c) 2009-2022 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||||
*
|
*
|
||||||
* 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
|
||||||
@ -56,6 +56,11 @@
|
|||||||
#define IOFD_TIMING 5
|
#define IOFD_TIMING 5
|
||||||
#define IOFD_MAX 6
|
#define IOFD_MAX 6
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default password prompt regex.
|
||||||
|
*/
|
||||||
|
#define PASSPROMPT_REGEX "[Pp]assword[: ]*"
|
||||||
|
|
||||||
struct timing_closure {
|
struct timing_closure {
|
||||||
struct timespec delay;
|
struct timespec delay;
|
||||||
const char *decimal;
|
const char *decimal;
|
||||||
@ -140,4 +145,11 @@ void iolog_set_owner(uid_t uid, uid_t gid);
|
|||||||
bool iolog_swapids(bool restore);
|
bool iolog_swapids(bool restore);
|
||||||
bool iolog_mkdirs(char *path);
|
bool iolog_mkdirs(char *path);
|
||||||
|
|
||||||
|
/* iolog_filter.c */
|
||||||
|
void *iolog_pwfilt_alloc();
|
||||||
|
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, unsigned int len, char **newbuf);
|
||||||
|
|
||||||
#endif /* SUDO_IOLOG_H */
|
#endif /* SUDO_IOLOG_H */
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: ISC
|
# SPDX-License-Identifier: ISC
|
||||||
#
|
#
|
||||||
# Copyright (c) 2011-2021 Todd C. Miller <Todd.Miller@sudo.ws>
|
# Copyright (c) 2011-2022 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||||
#
|
#
|
||||||
# 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
|
||||||
@ -100,12 +100,12 @@ DEVEL = @DEVEL@
|
|||||||
SHELL = @SHELL@
|
SHELL = @SHELL@
|
||||||
|
|
||||||
LIBIOLOG_OBJS = host_port.lo hostcheck.lo iolog_clearerr.lo iolog_close.lo \
|
LIBIOLOG_OBJS = host_port.lo hostcheck.lo iolog_clearerr.lo iolog_close.lo \
|
||||||
iolog_conf.lo iolog_eof.lo iolog_flush.lo iolog_gets.lo \
|
iolog_conf.lo iolog_eof.lo iolog_filter.lo iolog_flush.lo \
|
||||||
iolog_json.lo iolog_legacy.lo iolog_loginfo.lo iolog_mkdirs.lo \
|
iolog_gets.lo iolog_json.lo iolog_legacy.lo iolog_loginfo.lo \
|
||||||
iolog_mkdtemp.lo iolog_mkpath.lo iolog_nextid.lo \
|
iolog_mkdirs.lo iolog_mkdtemp.lo iolog_mkpath.lo \
|
||||||
iolog_open.lo iolog_openat.lo iolog_path.lo iolog_read.lo \
|
iolog_nextid.lo iolog_open.lo iolog_openat.lo iolog_path.lo \
|
||||||
iolog_seek.lo iolog_swapids.lo iolog_timing.lo iolog_util.lo \
|
iolog_read.lo iolog_seek.lo iolog_swapids.lo iolog_timing.lo \
|
||||||
iolog_write.lo
|
iolog_util.lo iolog_write.lo
|
||||||
|
|
||||||
IOBJS = $(LIBIOLOG_OBJS:.lo=.i)
|
IOBJS = $(LIBIOLOG_OBJS:.lo=.i)
|
||||||
|
|
||||||
@ -553,6 +553,20 @@ iolog_eof.i: $(srcdir)/iolog_eof.c $(incdir)/compat/stdbool.h \
|
|||||||
$(CC) -E -o $@ $(CPPFLAGS) $<
|
$(CC) -E -o $@ $(CPPFLAGS) $<
|
||||||
iolog_eof.plog: iolog_eof.i
|
iolog_eof.plog: iolog_eof.i
|
||||||
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_eof.c --i-file $< --output-file $@
|
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_eof.c --i-file $< --output-file $@
|
||||||
|
iolog_filter.lo: $(srcdir)/iolog_filter.c $(incdir)/compat/stdbool.h \
|
||||||
|
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
|
||||||
|
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
||||||
|
$(incdir)/sudo_iolog.h $(incdir)/sudo_plugin.h \
|
||||||
|
$(incdir)/sudo_queue.h $(top_builddir)/config.h
|
||||||
|
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_filter.c
|
||||||
|
iolog_filter.i: $(srcdir)/iolog_filter.c $(incdir)/compat/stdbool.h \
|
||||||
|
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
|
||||||
|
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
||||||
|
$(incdir)/sudo_iolog.h $(incdir)/sudo_plugin.h \
|
||||||
|
$(incdir)/sudo_queue.h $(top_builddir)/config.h
|
||||||
|
$(CC) -E -o $@ $(CPPFLAGS) $<
|
||||||
|
iolog_filter.plog: iolog_filter.i
|
||||||
|
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_filter.c --i-file $< --output-file $@
|
||||||
iolog_flush.lo: $(srcdir)/iolog_flush.c $(incdir)/compat/stdbool.h \
|
iolog_flush.lo: $(srcdir)/iolog_flush.c $(incdir)/compat/stdbool.h \
|
||||||
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
|
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
|
||||||
$(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \
|
$(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \
|
||||||
|
249
lib/iolog/iolog_filter.c
Normal file
249
lib/iolog/iolog_filter.c
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: ISC
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
||||||
|
* PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#ifdef HAVE_STDBOOL_H
|
||||||
|
# include <stdbool.h>
|
||||||
|
#else
|
||||||
|
# include "compat/stdbool.h"
|
||||||
|
#endif
|
||||||
|
#include <regex.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "sudo_compat.h"
|
||||||
|
#include "sudo_debug.h"
|
||||||
|
#include "sudo_fatal.h"
|
||||||
|
#include "sudo_gettext.h"
|
||||||
|
#include "sudo_iolog.h"
|
||||||
|
#include "sudo_queue.h"
|
||||||
|
|
||||||
|
struct pwfilt_regex {
|
||||||
|
TAILQ_ENTRY(pwfilt_regex) entries;
|
||||||
|
char *pattern;
|
||||||
|
regex_t regex;
|
||||||
|
};
|
||||||
|
TAILQ_HEAD(pwfilt_regex_list, pwfilt_regex);
|
||||||
|
|
||||||
|
struct pwfilt_handle {
|
||||||
|
struct pwfilt_regex_list filters;
|
||||||
|
bool is_filtered;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a new filter handle.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
iolog_pwfilt_alloc(void)
|
||||||
|
{
|
||||||
|
struct pwfilt_handle *handle;
|
||||||
|
debug_decl(iolog_pwfilt_alloc, SUDO_DEBUG_UTIL);
|
||||||
|
|
||||||
|
handle = malloc(sizeof(*handle));
|
||||||
|
if (handle != NULL) {
|
||||||
|
TAILQ_INIT(&handle->filters);
|
||||||
|
handle->is_filtered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_return_ptr(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unlink filt from filters and free it.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
iolog_pwfilt_free_filter(struct pwfilt_regex_list *filters,
|
||||||
|
struct pwfilt_regex *filt)
|
||||||
|
{
|
||||||
|
debug_decl(iolog_pwfilt_free_filter, SUDO_DEBUG_UTIL);
|
||||||
|
|
||||||
|
TAILQ_REMOVE(filters, filt, entries);
|
||||||
|
regfree(&filt->regex);
|
||||||
|
free(filt->pattern);
|
||||||
|
free(filt);
|
||||||
|
|
||||||
|
debug_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the given password filter handle.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
iolog_pwfilt_free(void *vhandle)
|
||||||
|
{
|
||||||
|
struct pwfilt_handle *handle = vhandle;
|
||||||
|
struct pwfilt_regex *filt;
|
||||||
|
debug_decl(iolog_pwfilt_free, SUDO_DEBUG_UTIL);
|
||||||
|
|
||||||
|
if (handle != NULL) {
|
||||||
|
while ((filt = TAILQ_FIRST(&handle->filters)) != NULL) {
|
||||||
|
iolog_pwfilt_free_filter(&handle->filters, filt);
|
||||||
|
}
|
||||||
|
free(handle);
|
||||||
|
}
|
||||||
|
debug_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a pattern to the password filter list.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
iolog_pwfilt_add(void *vhandle, const char *pattern)
|
||||||
|
{
|
||||||
|
struct pwfilt_handle *handle = vhandle;
|
||||||
|
struct pwfilt_regex *filt;
|
||||||
|
char errbuf[1024];
|
||||||
|
int errcode;
|
||||||
|
debug_decl(iolog_pwfilt_add, SUDO_DEBUG_UTIL);
|
||||||
|
|
||||||
|
filt = malloc(sizeof(*filt));
|
||||||
|
if (filt == NULL)
|
||||||
|
goto oom;
|
||||||
|
filt->pattern = strdup(pattern);
|
||||||
|
if (filt->pattern == NULL)
|
||||||
|
goto oom;
|
||||||
|
|
||||||
|
errcode = regcomp(&filt->regex, pattern, REG_EXTENDED|REG_NOSUB);
|
||||||
|
if (errcode != 0) {
|
||||||
|
regerror(errcode, &filt->regex, errbuf, sizeof(errbuf));
|
||||||
|
sudo_warnx(U_("invalid regular expression \"%s\": %s"),
|
||||||
|
pattern, errbuf);
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
TAILQ_INSERT_TAIL(&handle->filters, filt, entries);
|
||||||
|
debug_return_bool(true);
|
||||||
|
|
||||||
|
oom:
|
||||||
|
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||||
|
bad:
|
||||||
|
if (filt != NULL) {
|
||||||
|
free(filt->pattern);
|
||||||
|
free(filt);
|
||||||
|
}
|
||||||
|
debug_return_bool(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove a pattern from the password filter list.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
iolog_pwfilt_remove(void *vhandle, const char *pattern)
|
||||||
|
{
|
||||||
|
struct pwfilt_handle *handle = vhandle;
|
||||||
|
struct pwfilt_regex *filt, *next;
|
||||||
|
bool ret = false;
|
||||||
|
debug_decl(iolog_pwfilt_remove, SUDO_DEBUG_UTIL);
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(filt, &handle->filters, entries, next) {
|
||||||
|
if (strcmp(filt->pattern, pattern) == 0) {
|
||||||
|
iolog_pwfilt_free_filter(&handle->filters, filt);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug_return_bool(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If logging output and filtering is _not_ enabled, match buf against the
|
||||||
|
* password filter list patterns and, if there is a match, enable filtering.
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
iolog_pwfilt_run(void *vhandle, int event, const char *buf,
|
||||||
|
unsigned int len, char **newbuf)
|
||||||
|
{
|
||||||
|
struct pwfilt_handle *handle = vhandle;
|
||||||
|
struct pwfilt_regex *filt;
|
||||||
|
char *copy;
|
||||||
|
debug_decl(iolog_pwfilt_run, SUDO_DEBUG_UTIL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We only filter ttyin/ttyout. It is only possible to disable
|
||||||
|
* echo when a tty is present. Filtering passwords in the input
|
||||||
|
* log when they appear in the output is pointless. This does
|
||||||
|
* assume that the password prompt is written to the tty as well.
|
||||||
|
*/
|
||||||
|
switch (event) {
|
||||||
|
case IO_EVENT_TTYOUT:
|
||||||
|
/* If filtering passwords and we receive output, disable it. */
|
||||||
|
if (handle->is_filtered) {
|
||||||
|
handle->is_filtered = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make a copy of buf that is NUL-terminated. */
|
||||||
|
copy = malloc(len + 1);
|
||||||
|
if (copy == NULL) {
|
||||||
|
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||||
|
debug_return_ptr(false);
|
||||||
|
}
|
||||||
|
memcpy(copy, buf, len);
|
||||||
|
copy[len] = '\0';
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(copy);
|
||||||
|
break;
|
||||||
|
case IO_EVENT_TTYIN:
|
||||||
|
if (handle->is_filtered) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
/* We will stop filtering after reaching cr/nl. */
|
||||||
|
if (buf[i] == '\r' || buf[i] == '\n')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i != 0) {
|
||||||
|
/* Filtered, replace buffer with '*' chars. */
|
||||||
|
copy = malloc(len);
|
||||||
|
if (copy == NULL) {
|
||||||
|
sudo_warnx(U_("%s: %s"), __func__,
|
||||||
|
U_("unable to allocate memory"));
|
||||||
|
debug_return_ptr(NULL);
|
||||||
|
}
|
||||||
|
memset(copy, '*', i);
|
||||||
|
if (i != len) {
|
||||||
|
/* Done filtering, copy cr/nl and subsequent characters. */
|
||||||
|
memcpy(copy + i, buf + i, len - i);
|
||||||
|
handle->is_filtered = false;
|
||||||
|
}
|
||||||
|
*newbuf = copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_return_bool(true);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user