Add new log_passwords and passprompt_regex settings.

When logging terminal input, if log_passwords is false and any
of the regular expressions in the passprompt_regex list are found
in the terminal output, terminal input will be replaced with '*'
characters until a newline or carriage return is found in the input
or an output character is received.
This commit is contained in:
Todd C. Miller
2022-01-28 08:52:42 -07:00
parent c58a080dd5
commit 9b93961b3e
6 changed files with 205 additions and 21 deletions

View File

@@ -16,7 +16,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.TH "SUDO_LOGSRVD.CONF" "@mansectform@" "January 19, 2022" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
.TH "SUDO_LOGSRVD.CONF" "@mansectform@" "January 27, 2022" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
.nh
.if n .ad l
.SH "NAME"
@@ -268,7 +268,7 @@ The default value is
.TP 10n
tls_verify = bool
If true,
\fBsudo_logsrvd.conf\fR
\fBsudo_logsrvd\fR
will validate its own certificate at startup time or when the
configuration is changed.
If false, no verification is performed of the server certificate.
@@ -590,6 +590,35 @@ is set, it will be used instead of the user's primary group-ID.
By default, I/O log files and directories are created with user and
group-ID 0.
.TP 10n
log_passwords = bool
Most programs that require a user's password will disable echo before
reading the password to avoid displaying the plaintext password on
the screen.
However, if terminal input is being logged,
the password will still be present in the I/O log.
If
\fIlog_passwords\fR
is set to
\fRfalse\fR,
\fBsudo_logsrvd\fR
will attempt to prevent passwords from being logged.
It does this by using the regular expressions in
\fIpassprompt_regex\fR
to match a password prompt in the terminal output buffer.
When a match is found, input characters in the I/O log will be replaced with
\(oq*\(cq
until either a line feed or carriage return is found in the terminal input
or a new terminal output buffer is received.
If, however, a program displays characters as the user types them
(such as
\fBsudo\fR
when the
\fIpwfeedback\fR
option is set), only the
first character of the password will be replaced in the I/O log.
The default value is
\fRtrue\fR.
.TP 10n
maxseq = number
The maximum sequence number that will be substituted for the
\(lq\fR%{seq}\fR\(rq
@@ -606,6 +635,17 @@ base 36 sequence number
\(lqZZZZZZ\(rq)
will be silently truncated to 2176782336.
The default value is 2176782336.
.TP 10n
passprompt_regex = string
One or more POSIX extended regular expressions used to
match password prompts in the terminal output when
\fIlog_passwords\fR
is disabled.
Multiple
\fIpassprompt_regex\fR
settings may be specified.
The default value is
\(lq[Pp]assword[: ]*\(rq.
.SS "eventlog"
The
\fIeventlog\fR
@@ -948,6 +988,10 @@ Sudo log server configuration file
# specified by iolog_mode.
#iolog_mode = 0600
# If disabled, sudo_logsrvd will attempt to avoid logging plaintext
# password in the terminal input using passprompt_regex.
#log_passwords = true
# The maximum sequence number that will be substituted for the "%{seq}"
# escape in the I/O log file. While the value substituted for "%{seq}"
# is in base 36, maxseq itself should be expressed in decimal. Values
@@ -955,6 +999,12 @@ Sudo log server configuration file
# number "ZZZZZZ") will be silently truncated to 2176782336.
#maxseq = 2176782336
# One or more POSIX extended regular expressions used to match
# password prompts in the terminal output when log_passwords is
# disabled. Multiple passprompt_regex settings may be specified.
#passprompt_regex = [Pp]assword[: ]*
#passprompt_regex = [Pp]assword for [a-z0-9]+: *
[eventlog]
# Where to log accept, reject, exit, and alert events.
# Accepted values are syslog, logfile, or none.

View File

@@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd January 19, 2022
.Dd January 27, 2022
.Dt SUDO_LOGSRVD.CONF @mansectform@
.Os Sudo @PACKAGE_VERSION@
.Sh NAME
@@ -229,7 +229,7 @@ The default value is
.Pa /etc/ssl/sudo/private/logsrvd_key.pem .
.It tls_verify = bool
If true,
.Nm
.Nm sudo_logsrvd
will validate its own certificate at startup time or when the
configuration is changed.
If false, no verification is performed of the server certificate.
@@ -522,6 +522,34 @@ If
is set, it will be used instead of the user's primary group-ID.
By default, I/O log files and directories are created with user and
group-ID 0.
.It log_passwords = bool
Most programs that require a user's password will disable echo before
reading the password to avoid displaying the plaintext password on
the screen.
However, if terminal input is being logged,
the password will still be present in the I/O log.
If
.Em log_passwords
is set to
.Li false ,
.Nm sudo_logsrvd
will attempt to prevent passwords from being logged.
It does this by using the regular expressions in
.Em passprompt_regex
to match a password prompt in the terminal output buffer.
When a match is found, input characters in the I/O log will be replaced with
.Ql *
until either a line feed or carriage return is found in the terminal input
or a new terminal output buffer is received.
If, however, a program displays characters as the user types them
(such as
.Nm sudo
when the
.Em pwfeedback
option is set), only the
first character of the password will be replaced in the I/O log.
The default value is
.Li true .
.It maxseq = number
The maximum sequence number that will be substituted for the
.Dq Li %{seq}
@@ -538,6 +566,16 @@ base 36 sequence number
.Dq ZZZZZZ )
will be silently truncated to 2176782336.
The default value is 2176782336.
.It passprompt_regex = string
One or more POSIX extended regular expressions used to
match password prompts in the terminal output when
.Em log_passwords
is disabled.
Multiple
.Em passprompt_regex
settings may be specified.
The default value is
.Dq [Pp]assword[: ]* .
.El
.Ss eventlog
The
@@ -876,6 +914,10 @@ Sudo log server configuration file
# specified by iolog_mode.
#iolog_mode = 0600
# If disabled, sudo_logsrvd will attempt to avoid logging plaintext
# password in the terminal input using passprompt_regex.
#log_passwords = true
# The maximum sequence number that will be substituted for the "%{seq}"
# escape in the I/O log file. While the value substituted for "%{seq}"
# is in base 36, maxseq itself should be expressed in decimal. Values
@@ -883,6 +925,12 @@ Sudo log server configuration file
# number "ZZZZZZ") will be silently truncated to 2176782336.
#maxseq = 2176782336
# One or more POSIX extended regular expressions used to match
# password prompts in the terminal output when log_passwords is
# disabled. Multiple passprompt_regex settings may be specified.
#passprompt_regex = [Pp]assword[: ]*
#passprompt_regex = [Pp]assword for [a-z0-9]+: *
[eventlog]
# Where to log accept, reject, exit, and alert events.
# Accepted values are syslog, logfile, or none.

View File

@@ -179,6 +179,10 @@
# specified by iolog_mode.
#iolog_mode = 0600
# If disabled, sudo_logsrvd will attempt to avoid logging plaintext
# password in the terminal input using passprompt_regex.
#log_passwords = true
# The maximum sequence number that will be substituted for the "%{seq}"
# escape in the I/O log file. While the value substituted for "%{seq}"
# is in base 36, maxseq itself should be expressed in decimal. Values
@@ -186,6 +190,12 @@
# number "ZZZZZZ") will be silently truncated to 2176782336.
#maxseq = 2176782336
# One or more POSIX extended regular expressions used to match
# password prompts in the terminal output when log_passwords is
# disabled. Multiple passprompt_regex settings may be specified.
#passprompt_regex = [Pp]assword[: ]*
#passprompt_regex = [Pp]assword for [a-z0-9]+: *
[eventlog]
# Where to log accept, reject, exit, and alert events.
# Accepted values are syslog, logfile, or none.

View File

@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2019-2021 Todd C. Miller <Todd.Miller@sudo.ws>
* Copyright (c) 2019-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
@@ -208,6 +208,8 @@ struct connection_closure *connection_closure_alloc(int fd, bool tls, bool relay
bool logsrvd_conf_read(const char *path);
const char *logsrvd_conf_iolog_dir(void);
const char *logsrvd_conf_iolog_file(void);
bool logsrvd_conf_iolog_log_passwords(void);
void *logsrvd_conf_iolog_passprompt_regex(void);
struct server_address_list *logsrvd_conf_server_listen_address(void);
struct server_address_list *logsrvd_conf_relay_address(void);
const char *logsrvd_conf_relay_dir(void);

View File

@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2019-2021 Todd C. Miller <Todd.Miller@sudo.ws>
* Copyright (c) 2019-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
@@ -148,12 +148,14 @@ static struct logsrvd_config {
bool compress;
bool flush;
bool gid_set;
bool log_passwords;
uid_t uid;
gid_t gid;
mode_t mode;
unsigned int maxseq;
char *iolog_dir;
char *iolog_file;
void *passprompt_regex;
} iolog;
struct logsrvd_config_eventlog {
int log_type;
@@ -215,6 +217,18 @@ logsrvd_conf_iolog_file(void)
return logsrvd_config->iolog.iolog_file;
}
bool
logsrvd_conf_iolog_log_passwords(void)
{
return logsrvd_config->iolog.log_passwords;
}
void *
logsrvd_conf_iolog_passprompt_regex(void)
{
return logsrvd_config->iolog.passprompt_regex;
}
/* server getters */
struct server_address_list *
logsrvd_conf_server_listen_address(void)
@@ -367,6 +381,19 @@ cb_iolog_compress(struct logsrvd_config *config, const char *str, size_t offset)
debug_return_bool(true);
}
static bool
cb_iolog_log_passwords(struct logsrvd_config *config, const char *str, size_t offset)
{
int val;
debug_decl(cb_iolog_log_passwords, SUDO_DEBUG_UTIL);
if ((val = sudo_strtobool(str)) == -1)
debug_return_bool(false);
config->iolog.log_passwords = val;
debug_return_bool(true);
}
static bool
cb_iolog_flush(struct logsrvd_config *config, const char *str, size_t offset)
{
@@ -449,6 +476,20 @@ cb_iolog_maxseq(struct logsrvd_config *config, const char *str, size_t offset)
debug_return_bool(true);
}
static bool
cb_iolog_passprompt_regex(struct logsrvd_config *config, const char *str, size_t offset)
{
debug_decl(cb_iolog_passprompt_regex, SUDO_DEBUG_UTIL);
if (config->iolog.passprompt_regex == NULL) {
/* Lazy alloc of the passprompt regex handle. */
config->iolog.passprompt_regex = iolog_pwfilt_alloc();
if (config->iolog.passprompt_regex == NULL)
debug_return_bool(false);
}
debug_return_bool(iolog_pwfilt_add(config->iolog.passprompt_regex, str));
}
/* Server callbacks */
static bool
append_address(struct server_address_list *addresses, const char *str,
@@ -1069,7 +1110,9 @@ static struct logsrvd_config_entry iolog_conf_entries[] = {
{ "iolog_user", cb_iolog_user },
{ "iolog_group", cb_iolog_group },
{ "iolog_mode", cb_iolog_mode },
{ "log_passwords", cb_iolog_log_passwords },
{ "maxseq", cb_iolog_maxseq },
{ "passprompt_regex", cb_iolog_passprompt_regex },
{ NULL }
};
@@ -1242,7 +1285,7 @@ logsrvd_stub_close_log(int type, FILE *fp)
return;
}
/* Set eventlog configuration settings from on logsrvd config. */
/* Set eventlog configuration settings from logsrvd config. */
static void
logsrvd_conf_eventlog_setconf(struct logsrvd_config *config)
{
@@ -1262,6 +1305,22 @@ logsrvd_conf_eventlog_setconf(struct logsrvd_config *config)
debug_return;
}
/* Set I/O log configuration settings from logsrvd config. */
static void
logsrvd_conf_iolog_setconf(struct logsrvd_config *config)
{
debug_decl(logsrvd_conf_iolog_setconf, SUDO_DEBUG_UTIL);
iolog_set_defaults();
iolog_set_compress(config->iolog.compress);
iolog_set_flush(config->iolog.flush);
iolog_set_owner(config->iolog.uid, config->iolog.gid);
iolog_set_mode(config->iolog.mode);
iolog_set_maxseq(config->iolog.maxseq);
debug_return;
}
/*
* Conversation function for use by sudo_warn/sudo_fatal.
* Logs to stdout/stderr.
@@ -1483,6 +1542,7 @@ logsrvd_conf_free(struct logsrvd_config *config)
/* struct logsrvd_config_iolog */
free(config->iolog.iolog_dir);
free(config->iolog.iolog_file);
iolog_pwfilt_free(config->iolog.passprompt_regex);
/* struct logsrvd_config_logfile */
free(config->logfile.path);
@@ -1573,6 +1633,7 @@ logsrvd_conf_alloc(void)
config->iolog.uid = ROOT_UID;
config->iolog.gid = ROOT_GID;
config->iolog.gid_set = false;
config->iolog.log_passwords = true;
/* Event log defaults */
config->eventlog.log_type = EVLOG_SYSLOG;
@@ -1619,6 +1680,12 @@ logsrvd_conf_apply(struct logsrvd_config *config)
#endif
debug_decl(logsrvd_conf_apply, SUDO_DEBUG_UTIL);
/* There can be multiple passprompt regular expressions. */
if (config->iolog.passprompt_regex == NULL) {
if (!cb_iolog_passprompt_regex(config, PASSPROMPT_REGEX, 0))
debug_return_bool(false);
}
/* There can be multiple addresses so we can't set a default earlier. */
if (TAILQ_EMPTY(&config->server.addresses.addrs)) {
/* Enable plaintext listender. */
@@ -1738,15 +1805,12 @@ logsrvd_conf_apply(struct logsrvd_config *config)
break;
}
/* Set I/O log library settings */
iolog_set_defaults();
iolog_set_compress(config->iolog.compress);
iolog_set_flush(config->iolog.flush);
iolog_set_owner(config->iolog.uid, config->iolog.gid);
iolog_set_mode(config->iolog.mode);
iolog_set_maxseq(config->iolog.maxseq);
/* Set event log config */
/*
* Update event and I/O log library config and install the new
* logsrvd config. We must not fail past this point or the event
* and I/O log config will be inconsistent with the logsrvd config.
*/
logsrvd_conf_iolog_setconf(config);
logsrvd_conf_eventlog_setconf(config);
logsrvd_conf_free(logsrvd_config);

View File

@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2019-2021 Todd C. Miller <Todd.Miller@sudo.ws>
* Copyright (c) 2019-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
@@ -523,8 +523,9 @@ store_iobuf_local(int iofd, IoBuffer *iobuf, uint8_t *buf, size_t buflen,
struct connection_closure *closure)
{
const struct eventlog *evlog = closure->evlog;
struct ProtobufCBinaryData data = iobuf->data;
char tbuf[1024], *newbuf = NULL;
const char *errstr;
char tbuf[1024];
int len;
debug_decl(store_iobuf_local, SUDO_DEBUG_UTIL);
@@ -538,19 +539,27 @@ store_iobuf_local(int iofd, IoBuffer *iobuf, uint8_t *buf, size_t buflen,
/* FIXME - assumes IOFD_* matches IO_EVENT_* */
len = snprintf(tbuf, sizeof(tbuf), "%d %lld.%09d %zu\n",
iofd, (long long)iobuf->delay->tv_sec, (int)iobuf->delay->tv_nsec,
iobuf->data.len);
data.len);
if (len < 0 || len >= ssizeof(tbuf)) {
sudo_warnx(U_("unable to format timing buffer, length %d"), len);
goto bad;
}
if (!logsrvd_conf_iolog_log_passwords()) {
if (!iolog_pwfilt_run(logsrvd_conf_iolog_passprompt_regex(), iofd,
(char *)data.data, data.len, &newbuf))
goto bad;
if (newbuf != NULL)
data.data = (uint8_t *)newbuf;
}
/* Write to specified I/O log file. */
if (!iolog_write(&closure->iolog_files[iofd], iobuf->data.data,
iobuf->data.len, &errstr)) {
if (!iolog_write(&closure->iolog_files[iofd], data.data, data.len, &errstr)) {
sudo_warnx(U_("%s/%s: %s"), evlog->iolog_path, iolog_fd_to_name(iofd),
errstr);
goto bad;
}
free(newbuf);
/* Write timing data. */
if (!iolog_write(&closure->iolog_files[IOFD_TIMING], tbuf,
@@ -574,6 +583,7 @@ store_iobuf_local(int iofd, IoBuffer *iobuf, uint8_t *buf, size_t buflen,
debug_return_bool(true);
bad:
free(newbuf);
if (closure->errstr == NULL)
closure->errstr = _("error writing IoBuffer");
debug_return_bool(false);