diff options
author | Todd C. Miller <Todd.Miller@sudo.ws> | 2022-01-28 08:52:42 -0700 |
---|---|---|
committer | Todd C. Miller <Todd.Miller@sudo.ws> | 2022-01-28 08:52:42 -0700 |
commit | d877deed1edd31a24e634ed651c9b32253587b73 (patch) | |
tree | 1863ca2deb25f8e8aa00e488968da2c2bfcf729f | |
parent | 2c14ad69b00f5bafb63feebdf5931d5392fc219f (diff) | |
download | sudo-d877deed1edd31a24e634ed651c9b32253587b73.tar.gz |
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.
-rw-r--r-- | docs/sudo_logsrvd.conf.man.in | 54 | ||||
-rw-r--r-- | docs/sudo_logsrvd.conf.mdoc.in | 52 | ||||
-rw-r--r-- | examples/sudo_logsrvd.conf | 10 | ||||
-rw-r--r-- | logsrvd/logsrvd.h | 4 | ||||
-rw-r--r-- | logsrvd/logsrvd_conf.c | 86 | ||||
-rw-r--r-- | logsrvd/logsrvd_local.c | 20 |
6 files changed, 205 insertions, 21 deletions
diff --git a/docs/sudo_logsrvd.conf.man.in b/docs/sudo_logsrvd.conf.man.in index 5e44c15fd..d34aa10a8 100644 --- a/docs/sudo_logsrvd.conf.man.in +++ b/docs/sudo_logsrvd.conf.man.in @@ -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. diff --git a/docs/sudo_logsrvd.conf.mdoc.in b/docs/sudo_logsrvd.conf.mdoc.in index 539bc14c7..bcf0bda36 100644 --- a/docs/sudo_logsrvd.conf.mdoc.in +++ b/docs/sudo_logsrvd.conf.mdoc.in @@ -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. diff --git a/examples/sudo_logsrvd.conf b/examples/sudo_logsrvd.conf index 32dbd821b..5fd7d3f40 100644 --- a/examples/sudo_logsrvd.conf +++ b/examples/sudo_logsrvd.conf @@ -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. diff --git a/logsrvd/logsrvd.h b/logsrvd/logsrvd.h index ef8191b65..2565e999a 100644 --- a/logsrvd/logsrvd.h +++ b/logsrvd/logsrvd.h @@ -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); diff --git a/logsrvd/logsrvd_conf.c b/logsrvd/logsrvd_conf.c index 836f1a7d8..993c4b4f6 100644 --- a/logsrvd/logsrvd_conf.c +++ b/logsrvd/logsrvd_conf.c @@ -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) @@ -368,6 +382,19 @@ cb_iolog_compress(struct logsrvd_config *config, const char *str, size_t offset) } 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) { int val; @@ -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); diff --git a/logsrvd/logsrvd_local.c b/logsrvd/logsrvd_local.c index 819c60bb2..a4cbb9fdb 100644 --- a/logsrvd/logsrvd_local.c +++ b/logsrvd/logsrvd_local.c @@ -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); |