From 3e69bc330f32730a75ce492b1813704fb1db3405 Mon Sep 17 00:00:00 2001 From: Haley Date: Sat, 2 May 2026 22:11:42 -0400 Subject: [PATCH] Add pwfeedback_char option to allow configuring the feedback character --- lib/util/term.c | 1 + plugins/sudoers/auth/sudo_auth.c | 7 +- plugins/sudoers/def_data.c | 4 + plugins/sudoers/def_data.h | 184 ++++++++++++++++--------------- plugins/sudoers/def_data.in | 3 + plugins/sudoers/defaults.c | 9 ++ src/tgetpass.c | 3 +- 7 files changed, 118 insertions(+), 93 deletions(-) diff --git a/lib/util/term.c b/lib/util/term.c index 23d93e2a91..eb70cfefe7 100644 --- a/lib/util/term.c +++ b/lib/util/term.c @@ -93,6 +93,7 @@ static bool changed; sudo_dso_public int sudo_term_eof; sudo_dso_public int sudo_term_erase; sudo_dso_public int sudo_term_kill; +sudo_dso_public const char *sudo_pwfeedback_char = "*"; static volatile sig_atomic_t got_sigttou; diff --git a/plugins/sudoers/auth/sudo_auth.c b/plugins/sudoers/auth/sudo_auth.c index 204a7c8359..7c3a417e65 100644 --- a/plugins/sudoers/auth/sudo_auth.c +++ b/plugins/sudoers/auth/sudo_auth.c @@ -487,6 +487,8 @@ sudo_auth_end_session(void) debug_return_int(ret); } +extern const char *sudo_pwfeedback_char; + /* * Prompts the user for a password using the conversation function. * Returns the plaintext password or NULL. @@ -504,8 +506,11 @@ auth_getpass(const char *prompt, int type, struct sudo_conv_callback *callback) display_lecture(callback); /* Mask user input if pwfeedback set and echo is off. */ - if (type == SUDO_CONV_PROMPT_ECHO_OFF && def_pwfeedback) + if (type == SUDO_CONV_PROMPT_ECHO_OFF && def_pwfeedback) { type = SUDO_CONV_PROMPT_MASK; + if (def_pwfeedback_char != NULL) + sudo_pwfeedback_char = def_pwfeedback_char; + } /* If visiblepw set, do not error out if there is no tty. */ if (def_visiblepw) diff --git a/plugins/sudoers/def_data.c b/plugins/sudoers/def_data.c index fd04b21952..0a7f2c5e86 100644 --- a/plugins/sudoers/def_data.c +++ b/plugins/sudoers/def_data.c @@ -341,6 +341,10 @@ struct sudo_defs_types sudo_defs_table[] = { "pwfeedback", T_FLAG, N_("Provide visual feedback at the password prompt when there is user input"), NULL, + }, { + "pwfeedback_char", T_STR, + N_("Character to use for password feedback: %s"), + NULL, }, { "fast_glob", T_FLAG, N_("Use faster globbing that is less accurate but does not access the filesystem"), diff --git a/plugins/sudoers/def_data.h b/plugins/sudoers/def_data.h index 785938205a..896a57d5c8 100644 --- a/plugins/sudoers/def_data.h +++ b/plugins/sudoers/def_data.h @@ -144,187 +144,189 @@ #define def_visiblepw (sudo_defs_table[I_VISIBLEPW].sd_un.flag) #define I_PWFEEDBACK 71 #define def_pwfeedback (sudo_defs_table[I_PWFEEDBACK].sd_un.flag) -#define I_FAST_GLOB 72 +#define I_PWFEEDBACK_CHAR 72 +#define def_pwfeedback_char (sudo_defs_table[I_PWFEEDBACK_CHAR].sd_un.str) +#define I_FAST_GLOB 73 #define def_fast_glob (sudo_defs_table[I_FAST_GLOB].sd_un.flag) -#define I_UMASK_OVERRIDE 73 +#define I_UMASK_OVERRIDE 74 #define def_umask_override (sudo_defs_table[I_UMASK_OVERRIDE].sd_un.flag) -#define I_LOG_INPUT 74 +#define I_LOG_INPUT 75 #define def_log_input (sudo_defs_table[I_LOG_INPUT].sd_un.flag) -#define I_LOG_STDIN 75 +#define I_LOG_STDIN 76 #define def_log_stdin (sudo_defs_table[I_LOG_STDIN].sd_un.flag) -#define I_LOG_TTYIN 76 +#define I_LOG_TTYIN 77 #define def_log_ttyin (sudo_defs_table[I_LOG_TTYIN].sd_un.flag) -#define I_LOG_OUTPUT 77 +#define I_LOG_OUTPUT 78 #define def_log_output (sudo_defs_table[I_LOG_OUTPUT].sd_un.flag) -#define I_LOG_STDOUT 78 +#define I_LOG_STDOUT 79 #define def_log_stdout (sudo_defs_table[I_LOG_STDOUT].sd_un.flag) -#define I_LOG_STDERR 79 +#define I_LOG_STDERR 80 #define def_log_stderr (sudo_defs_table[I_LOG_STDERR].sd_un.flag) -#define I_LOG_TTYOUT 80 +#define I_LOG_TTYOUT 81 #define def_log_ttyout (sudo_defs_table[I_LOG_TTYOUT].sd_un.flag) -#define I_COMPRESS_IO 81 +#define I_COMPRESS_IO 82 #define def_compress_io (sudo_defs_table[I_COMPRESS_IO].sd_un.flag) -#define I_USE_PTY 82 +#define I_USE_PTY 83 #define def_use_pty (sudo_defs_table[I_USE_PTY].sd_un.flag) -#define I_GROUP_PLUGIN 83 +#define I_GROUP_PLUGIN 84 #define def_group_plugin (sudo_defs_table[I_GROUP_PLUGIN].sd_un.str) -#define I_IOLOG_DIR 84 +#define I_IOLOG_DIR 85 #define def_iolog_dir (sudo_defs_table[I_IOLOG_DIR].sd_un.str) -#define I_IOLOG_FILE 85 +#define I_IOLOG_FILE 86 #define def_iolog_file (sudo_defs_table[I_IOLOG_FILE].sd_un.str) -#define I_SET_UTMP 86 +#define I_SET_UTMP 87 #define def_set_utmp (sudo_defs_table[I_SET_UTMP].sd_un.flag) -#define I_UTMP_RUNAS 87 +#define I_UTMP_RUNAS 88 #define def_utmp_runas (sudo_defs_table[I_UTMP_RUNAS].sd_un.flag) -#define I_PRIVS 88 +#define I_PRIVS 89 #define def_privs (sudo_defs_table[I_PRIVS].sd_un.str) -#define I_LIMITPRIVS 89 +#define I_LIMITPRIVS 90 #define def_limitprivs (sudo_defs_table[I_LIMITPRIVS].sd_un.str) -#define I_EXEC_BACKGROUND 90 +#define I_EXEC_BACKGROUND 91 #define def_exec_background (sudo_defs_table[I_EXEC_BACKGROUND].sd_un.flag) -#define I_PAM_SERVICE 91 +#define I_PAM_SERVICE 92 #define def_pam_service (sudo_defs_table[I_PAM_SERVICE].sd_un.str) -#define I_PAM_LOGIN_SERVICE 92 +#define I_PAM_LOGIN_SERVICE 93 #define def_pam_login_service (sudo_defs_table[I_PAM_LOGIN_SERVICE].sd_un.str) -#define I_PAM_ASKPASS_SERVICE 93 +#define I_PAM_ASKPASS_SERVICE 94 #define def_pam_askpass_service (sudo_defs_table[I_PAM_ASKPASS_SERVICE].sd_un.str) -#define I_PAM_SETCRED 94 +#define I_PAM_SETCRED 95 #define def_pam_setcred (sudo_defs_table[I_PAM_SETCRED].sd_un.flag) -#define I_PAM_SESSION 95 +#define I_PAM_SESSION 96 #define def_pam_session (sudo_defs_table[I_PAM_SESSION].sd_un.flag) -#define I_PAM_ACCT_MGMT 96 +#define I_PAM_ACCT_MGMT 97 #define def_pam_acct_mgmt (sudo_defs_table[I_PAM_ACCT_MGMT].sd_un.flag) -#define I_PAM_SILENT 97 +#define I_PAM_SILENT 98 #define def_pam_silent (sudo_defs_table[I_PAM_SILENT].sd_un.flag) -#define I_MAXSEQ 98 +#define I_MAXSEQ 99 #define def_maxseq (sudo_defs_table[I_MAXSEQ].sd_un.str) -#define I_USE_NETGROUPS 99 +#define I_USE_NETGROUPS 100 #define def_use_netgroups (sudo_defs_table[I_USE_NETGROUPS].sd_un.flag) -#define I_SUDOEDIT_CHECKDIR 100 +#define I_SUDOEDIT_CHECKDIR 101 #define def_sudoedit_checkdir (sudo_defs_table[I_SUDOEDIT_CHECKDIR].sd_un.flag) -#define I_SUDOEDIT_FOLLOW 101 +#define I_SUDOEDIT_FOLLOW 102 #define def_sudoedit_follow (sudo_defs_table[I_SUDOEDIT_FOLLOW].sd_un.flag) -#define I_ALWAYS_QUERY_GROUP_PLUGIN 102 +#define I_ALWAYS_QUERY_GROUP_PLUGIN 103 #define def_always_query_group_plugin (sudo_defs_table[I_ALWAYS_QUERY_GROUP_PLUGIN].sd_un.flag) -#define I_NETGROUP_TUPLE 103 +#define I_NETGROUP_TUPLE 104 #define def_netgroup_tuple (sudo_defs_table[I_NETGROUP_TUPLE].sd_un.flag) -#define I_IGNORE_AUDIT_ERRORS 104 +#define I_IGNORE_AUDIT_ERRORS 105 #define def_ignore_audit_errors (sudo_defs_table[I_IGNORE_AUDIT_ERRORS].sd_un.flag) -#define I_IGNORE_IOLOG_ERRORS 105 +#define I_IGNORE_IOLOG_ERRORS 106 #define def_ignore_iolog_errors (sudo_defs_table[I_IGNORE_IOLOG_ERRORS].sd_un.flag) -#define I_IGNORE_LOGFILE_ERRORS 106 +#define I_IGNORE_LOGFILE_ERRORS 107 #define def_ignore_logfile_errors (sudo_defs_table[I_IGNORE_LOGFILE_ERRORS].sd_un.flag) -#define I_MATCH_GROUP_BY_GID 107 +#define I_MATCH_GROUP_BY_GID 108 #define def_match_group_by_gid (sudo_defs_table[I_MATCH_GROUP_BY_GID].sd_un.flag) -#define I_SYSLOG_MAXLEN 108 +#define I_SYSLOG_MAXLEN 109 #define def_syslog_maxlen (sudo_defs_table[I_SYSLOG_MAXLEN].sd_un.uival) -#define I_IOLOG_USER 109 +#define I_IOLOG_USER 110 #define def_iolog_user (sudo_defs_table[I_IOLOG_USER].sd_un.str) -#define I_IOLOG_GROUP 110 +#define I_IOLOG_GROUP 111 #define def_iolog_group (sudo_defs_table[I_IOLOG_GROUP].sd_un.str) -#define I_IOLOG_MODE 111 +#define I_IOLOG_MODE 112 #define def_iolog_mode (sudo_defs_table[I_IOLOG_MODE].sd_un.mode) -#define I_FDEXEC 112 +#define I_FDEXEC 113 #define def_fdexec (sudo_defs_table[I_FDEXEC].sd_un.tuple) -#define I_IGNORE_UNKNOWN_DEFAULTS 113 +#define I_IGNORE_UNKNOWN_DEFAULTS 114 #define def_ignore_unknown_defaults (sudo_defs_table[I_IGNORE_UNKNOWN_DEFAULTS].sd_un.flag) -#define I_COMMAND_TIMEOUT 114 +#define I_COMMAND_TIMEOUT 115 #define def_command_timeout (sudo_defs_table[I_COMMAND_TIMEOUT].sd_un.ival) -#define I_USER_COMMAND_TIMEOUTS 115 +#define I_USER_COMMAND_TIMEOUTS 116 #define def_user_command_timeouts (sudo_defs_table[I_USER_COMMAND_TIMEOUTS].sd_un.flag) -#define I_IOLOG_FLUSH 116 +#define I_IOLOG_FLUSH 117 #define def_iolog_flush (sudo_defs_table[I_IOLOG_FLUSH].sd_un.flag) -#define I_SYSLOG_PID 117 +#define I_SYSLOG_PID 118 #define def_syslog_pid (sudo_defs_table[I_SYSLOG_PID].sd_un.flag) -#define I_TIMESTAMP_TYPE 118 +#define I_TIMESTAMP_TYPE 119 #define def_timestamp_type (sudo_defs_table[I_TIMESTAMP_TYPE].sd_un.tuple) -#define I_AUTHFAIL_MESSAGE 119 +#define I_AUTHFAIL_MESSAGE 120 #define def_authfail_message (sudo_defs_table[I_AUTHFAIL_MESSAGE].sd_un.str) -#define I_CASE_INSENSITIVE_USER 120 +#define I_CASE_INSENSITIVE_USER 121 #define def_case_insensitive_user (sudo_defs_table[I_CASE_INSENSITIVE_USER].sd_un.flag) -#define I_CASE_INSENSITIVE_GROUP 121 +#define I_CASE_INSENSITIVE_GROUP 122 #define def_case_insensitive_group (sudo_defs_table[I_CASE_INSENSITIVE_GROUP].sd_un.flag) -#define I_LOG_ALLOWED 122 +#define I_LOG_ALLOWED 123 #define def_log_allowed (sudo_defs_table[I_LOG_ALLOWED].sd_un.flag) -#define I_LOG_DENIED 123 +#define I_LOG_DENIED 124 #define def_log_denied (sudo_defs_table[I_LOG_DENIED].sd_un.flag) -#define I_LOG_SERVERS 124 +#define I_LOG_SERVERS 125 #define def_log_servers (sudo_defs_table[I_LOG_SERVERS].sd_un.list) -#define I_LOG_SERVER_TIMEOUT 125 +#define I_LOG_SERVER_TIMEOUT 126 #define def_log_server_timeout (sudo_defs_table[I_LOG_SERVER_TIMEOUT].sd_un.ival) -#define I_LOG_SERVER_KEEPALIVE 126 +#define I_LOG_SERVER_KEEPALIVE 127 #define def_log_server_keepalive (sudo_defs_table[I_LOG_SERVER_KEEPALIVE].sd_un.flag) -#define I_LOG_SERVER_CABUNDLE 127 +#define I_LOG_SERVER_CABUNDLE 128 #define def_log_server_cabundle (sudo_defs_table[I_LOG_SERVER_CABUNDLE].sd_un.str) -#define I_LOG_SERVER_PEER_CERT 128 +#define I_LOG_SERVER_PEER_CERT 129 #define def_log_server_peer_cert (sudo_defs_table[I_LOG_SERVER_PEER_CERT].sd_un.str) -#define I_LOG_SERVER_PEER_KEY 129 +#define I_LOG_SERVER_PEER_KEY 130 #define def_log_server_peer_key (sudo_defs_table[I_LOG_SERVER_PEER_KEY].sd_un.str) -#define I_LOG_SERVER_VERIFY 130 +#define I_LOG_SERVER_VERIFY 131 #define def_log_server_verify (sudo_defs_table[I_LOG_SERVER_VERIFY].sd_un.flag) -#define I_RUNAS_ALLOW_UNKNOWN_ID 131 +#define I_RUNAS_ALLOW_UNKNOWN_ID 132 #define def_runas_allow_unknown_id (sudo_defs_table[I_RUNAS_ALLOW_UNKNOWN_ID].sd_un.flag) -#define I_RUNAS_CHECK_SHELL 132 +#define I_RUNAS_CHECK_SHELL 133 #define def_runas_check_shell (sudo_defs_table[I_RUNAS_CHECK_SHELL].sd_un.flag) -#define I_PAM_RUSER 133 +#define I_PAM_RUSER 134 #define def_pam_ruser (sudo_defs_table[I_PAM_RUSER].sd_un.flag) -#define I_PAM_RHOST 134 +#define I_PAM_RHOST 135 #define def_pam_rhost (sudo_defs_table[I_PAM_RHOST].sd_un.flag) -#define I_RUNCWD 135 +#define I_RUNCWD 136 #define def_runcwd (sudo_defs_table[I_RUNCWD].sd_un.str) -#define I_RUNCHROOT 136 +#define I_RUNCHROOT 137 #define def_runchroot (sudo_defs_table[I_RUNCHROOT].sd_un.str) -#define I_LOG_FORMAT 137 +#define I_LOG_FORMAT 138 #define def_log_format (sudo_defs_table[I_LOG_FORMAT].sd_un.tuple) -#define I_SELINUX 138 +#define I_SELINUX 139 #define def_selinux (sudo_defs_table[I_SELINUX].sd_un.flag) -#define I_ADMIN_FLAG 139 +#define I_ADMIN_FLAG 140 #define def_admin_flag (sudo_defs_table[I_ADMIN_FLAG].sd_un.str) -#define I_INTERCEPT 140 +#define I_INTERCEPT 141 #define def_intercept (sudo_defs_table[I_INTERCEPT].sd_un.flag) -#define I_LOG_SUBCMDS 141 +#define I_LOG_SUBCMDS 142 #define def_log_subcmds (sudo_defs_table[I_LOG_SUBCMDS].sd_un.flag) -#define I_LOG_EXIT_STATUS 142 +#define I_LOG_EXIT_STATUS 143 #define def_log_exit_status (sudo_defs_table[I_LOG_EXIT_STATUS].sd_un.flag) -#define I_INTERCEPT_AUTHENTICATE 143 +#define I_INTERCEPT_AUTHENTICATE 144 #define def_intercept_authenticate (sudo_defs_table[I_INTERCEPT_AUTHENTICATE].sd_un.flag) -#define I_INTERCEPT_ALLOW_SETID 144 +#define I_INTERCEPT_ALLOW_SETID 145 #define def_intercept_allow_setid (sudo_defs_table[I_INTERCEPT_ALLOW_SETID].sd_un.flag) -#define I_RLIMIT_AS 145 +#define I_RLIMIT_AS 146 #define def_rlimit_as (sudo_defs_table[I_RLIMIT_AS].sd_un.str) -#define I_RLIMIT_CORE 146 +#define I_RLIMIT_CORE 147 #define def_rlimit_core (sudo_defs_table[I_RLIMIT_CORE].sd_un.str) -#define I_RLIMIT_CPU 147 +#define I_RLIMIT_CPU 148 #define def_rlimit_cpu (sudo_defs_table[I_RLIMIT_CPU].sd_un.str) -#define I_RLIMIT_DATA 148 +#define I_RLIMIT_DATA 149 #define def_rlimit_data (sudo_defs_table[I_RLIMIT_DATA].sd_un.str) -#define I_RLIMIT_FSIZE 149 +#define I_RLIMIT_FSIZE 150 #define def_rlimit_fsize (sudo_defs_table[I_RLIMIT_FSIZE].sd_un.str) -#define I_RLIMIT_LOCKS 150 +#define I_RLIMIT_LOCKS 151 #define def_rlimit_locks (sudo_defs_table[I_RLIMIT_LOCKS].sd_un.str) -#define I_RLIMIT_MEMLOCK 151 +#define I_RLIMIT_MEMLOCK 152 #define def_rlimit_memlock (sudo_defs_table[I_RLIMIT_MEMLOCK].sd_un.str) -#define I_RLIMIT_NOFILE 152 +#define I_RLIMIT_NOFILE 153 #define def_rlimit_nofile (sudo_defs_table[I_RLIMIT_NOFILE].sd_un.str) -#define I_RLIMIT_NPROC 153 +#define I_RLIMIT_NPROC 154 #define def_rlimit_nproc (sudo_defs_table[I_RLIMIT_NPROC].sd_un.str) -#define I_RLIMIT_RSS 154 +#define I_RLIMIT_RSS 155 #define def_rlimit_rss (sudo_defs_table[I_RLIMIT_RSS].sd_un.str) -#define I_RLIMIT_STACK 155 +#define I_RLIMIT_STACK 156 #define def_rlimit_stack (sudo_defs_table[I_RLIMIT_STACK].sd_un.str) -#define I_NONINTERACTIVE_AUTH 156 +#define I_NONINTERACTIVE_AUTH 157 #define def_noninteractive_auth (sudo_defs_table[I_NONINTERACTIVE_AUTH].sd_un.flag) -#define I_LOG_PASSWORDS 157 +#define I_LOG_PASSWORDS 158 #define def_log_passwords (sudo_defs_table[I_LOG_PASSWORDS].sd_un.flag) -#define I_PASSPROMPT_REGEX 158 +#define I_PASSPROMPT_REGEX 159 #define def_passprompt_regex (sudo_defs_table[I_PASSPROMPT_REGEX].sd_un.list) -#define I_INTERCEPT_TYPE 159 +#define I_INTERCEPT_TYPE 160 #define def_intercept_type (sudo_defs_table[I_INTERCEPT_TYPE].sd_un.tuple) -#define I_INTERCEPT_VERIFY 160 +#define I_INTERCEPT_VERIFY 161 #define def_intercept_verify (sudo_defs_table[I_INTERCEPT_VERIFY].sd_un.flag) -#define I_APPARMOR_PROFILE 161 +#define I_APPARMOR_PROFILE 162 #define def_apparmor_profile (sudo_defs_table[I_APPARMOR_PROFILE].sd_un.str) -#define I_CMDDENIAL_MESSAGE 162 +#define I_CMDDENIAL_MESSAGE 163 #define def_cmddenial_message (sudo_defs_table[I_CMDDENIAL_MESSAGE].sd_un.str) enum def_tuple { diff --git a/plugins/sudoers/def_data.in b/plugins/sudoers/def_data.in index 62be675fcb..f30558d9e1 100644 --- a/plugins/sudoers/def_data.in +++ b/plugins/sudoers/def_data.in @@ -229,6 +229,9 @@ visiblepw pwfeedback T_FLAG "Provide visual feedback at the password prompt when there is user input" +pwfeedback_char + T_STR + "Character to use for password feedback: %s" fast_glob T_FLAG "Use faster globbing that is less accurate but does not access the filesystem" diff --git a/plugins/sudoers/defaults.c b/plugins/sudoers/defaults.c index 32226c8415..95feace579 100644 --- a/plugins/sudoers/defaults.c +++ b/plugins/sudoers/defaults.c @@ -272,6 +272,13 @@ parse_default_entry(const struct sudoers_context *ctx, break; } } + if (val != NULL && strcmp(def->name, "pwfeedback_char") == 0) { + if (val[0] == '\0' || strlen(val) > 4) { + defaults_warnx(ctx, file, line, column, quiet, N_("pwfeedback_char must be a single character")); + rc = -1; + break; + } + } rc = store_str(val, def); break; case T_INT: @@ -633,6 +640,8 @@ init_defaults(void) #endif if ((def_passprompt = strdup(_(PASSPROMPT))) == NULL) goto oom; + if ((def_pwfeedback_char = strdup("*")) == NULL) + goto oom; if ((def_runas_default = strdup(RUNAS_DEFAULT)) == NULL) goto oom; #ifdef _PATH_SUDO_SENDMAIL diff --git a/src/tgetpass.c b/src/tgetpass.c index b1edd8ae55..22b6e8beff 100644 --- a/src/tgetpass.c +++ b/src/tgetpass.c @@ -374,6 +374,7 @@ sudo_askpass(const char *askpass, const char *prompt) } extern int sudo_term_eof, sudo_term_erase, sudo_term_kill; +extern const char *sudo_pwfeedback_char; static ssize_t last_chunk_len(const char *buf, ssize_t len) @@ -444,7 +445,7 @@ getln(int fd, char *buf, size_t bufsiz, bool cbreak, bool feedback, *cp++ = c; if (feedback) { if (last_chunk_len(buf, cp - buf) == 1) - ignore_result(write(fd, "*", 1)); + ignore_result(write(fd, sudo_pwfeedback_char, strlen(sudo_pwfeedback_char))); } } else { *cp++ = c;