Add a way to run a command without updating the cached credentials.

This can also be used to test for whether or not the user's
credentials are currently cached.
This commit is contained in:
Todd C. Miller
2022-08-02 14:28:28 -06:00
parent 2d94d329cf
commit 556dacf1ff
9 changed files with 115 additions and 49 deletions

View File

@@ -25,7 +25,7 @@
.nr BA @BAMAN@
.nr LC @LCMAN@
.nr PS @PSMAN@
.TH "SUDO" "@mansectsu@" "February 16, 2022" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
.TH "SUDO" "@mansectsu@" "August 2, 2022" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
.nh
.if n .ad l
.SH "NAME"
@@ -41,7 +41,7 @@
.HP 5n
\fBsudo\fR
\fB\-v\fR
[\fB\-ABknS\fR]
[\fB\-ABkNnS\fR]
.if \n(BA [\fB\-a\fR\ \fItype\fR]
[\fB\-g\fR\ \fIgroup\fR]
[\fB\-h\fR\ \fIhost\fR]
@@ -51,7 +51,7 @@
.HP 5n
\fBsudo\fR
\fB\-l\fR
[\fB\-ABknS\fR]
[\fB\-ABkNnS\fR]
.if \n(BA [\fB\-a\fR\ \fItype\fR]
[\fB\-g\fR\ \fIgroup\fR]
[\fB\-h\fR\ \fIhost\fR]
@@ -81,7 +81,7 @@
.br
.HP 9n
\fBsudoedit\fR
[\fB\-ABknS\fR]
[\fB\-ABkNnS\fR]
.if \n(BA [\fB\-a\fR\ \fItype\fR]
[\fB\-C\fR\ \fInum\fR]
.if \n(LC [\fB\-c\fR\ \fIclass\fR]
@@ -532,6 +532,22 @@ is specified but not allowed by the policy,
\fBsudo\fR
will exit with a status value of 1.
.TP 12n
\fB\-N\fR, \fB\--no-update\fR
Do not update the user's cached credentials, even if the user successfully
authenticates.
Unlike the
\fB\-k\fR
flag, existing cached credentials are used if they are valid.
To detect when the user's cached credentials are valid (or when no
authentication is required), the following command can be used:
.RS 18n
sudo -Nnv
.RE
.RS 12n
.sp
Not all security policies support credential caching.
.RE
.TP 12n
\fB\-n\fR, \fB\--non-interactive\fR
Avoid prompting the user for input of any kind.
If a password is required for the command to run,

View File

@@ -24,7 +24,7 @@
.nr BA @BAMAN@
.nr LC @LCMAN@
.nr PS @PSMAN@
.Dd February 16, 2022
.Dd August 2, 2022
.Dt SUDO @mansectsu@
.Os Sudo @PACKAGE_VERSION@
.Sh NAME
@@ -36,7 +36,7 @@
.Fl h | K | k | V
.Nm sudo
.Fl v
.Op Fl ABknS
.Op Fl ABkNnS
.if \n(BA \{\
.Op Fl a Ar type
.\}
@@ -46,7 +46,7 @@
.Op Fl u Ar user
.Nm sudo
.Fl l
.Op Fl ABknS
.Op Fl ABkNnS
.if \n(BA \{\
.Op Fl a Ar type
.\}
@@ -80,7 +80,7 @@
.Op Fl i | s
.Op Ar command
.Nm sudoedit
.Op Fl ABknS
.Op Fl ABkNnS
.if \n(BA \{\
.Op Fl a Ar type
.\}
@@ -505,6 +505,17 @@ If a
is specified but not allowed by the policy,
.Nm
will exit with a status value of 1.
.It Fl N , -no-update
Do not update the user's cached credentials, even if the user successfully
authenticates.
Unlike the
.Fl k
flag, existing cached credentials are used if they are valid.
To detect when the user's cached credentials are valid (or when no
authentication is required), the following command can be used:
.Dl sudo -Nnv
.Pp
Not all security policies support credential caching.
.It Fl n , -non-interactive
Avoid prompting the user for input of any kind.
If a password is required for the command to run,

View File

@@ -215,8 +215,8 @@ done:
* Only update time stamp if user validated and was approved.
* Failure to update the time stamp is not a fatal error.
*/
if (ret == true && closure.tstat != TS_ERROR) {
if (ISSET(validated, VALIDATE_SUCCESS))
if (ret == true && ISSET(validated, VALIDATE_SUCCESS)) {
if (ISSET(mode, MODE_UPDATE_TICKET) && closure.tstat != TS_ERROR)
(void)timestamp_update(closure.cookie, closure.auth_pw);
}
}

View File

@@ -84,11 +84,11 @@ parse_bool(const char *line, int varlen, int *flags, int fval)
}
}
#define RUN_VALID_FLAGS (MODE_ASKPASS|MODE_PRESERVE_ENV|MODE_RESET_HOME|MODE_IMPLIED_SHELL|MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_IGNORE_TICKET|MODE_PRESERVE_GROUPS|MODE_SHELL|MODE_RUN|MODE_POLICY_INTERCEPTED)
#define EDIT_VALID_FLAGS (MODE_ASKPASS|MODE_NONINTERACTIVE|MODE_IGNORE_TICKET|MODE_EDIT)
#define LIST_VALID_FLAGS (MODE_ASKPASS|MODE_NONINTERACTIVE|MODE_IGNORE_TICKET|MODE_LIST|MODE_CHECK)
#define VALIDATE_VALID_FLAGS (MODE_ASKPASS|MODE_NONINTERACTIVE|MODE_IGNORE_TICKET|MODE_VALIDATE)
#define INVALIDATE_VALID_FLAGS (MODE_ASKPASS|MODE_NONINTERACTIVE|MODE_IGNORE_TICKET|MODE_INVALIDATE)
#define RUN_VALID_FLAGS (MODE_ASKPASS|MODE_PRESERVE_ENV|MODE_RESET_HOME|MODE_IMPLIED_SHELL|MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_IGNORE_TICKET|MODE_UPDATE_TICKET|MODE_PRESERVE_GROUPS|MODE_SHELL|MODE_RUN|MODE_POLICY_INTERCEPTED)
#define EDIT_VALID_FLAGS (MODE_ASKPASS|MODE_NONINTERACTIVE|MODE_IGNORE_TICKET|MODE_UPDATE_TICKET|MODE_EDIT)
#define LIST_VALID_FLAGS (MODE_ASKPASS|MODE_NONINTERACTIVE|MODE_IGNORE_TICKET|MODE_UPDATE_TICKET|MODE_LIST|MODE_CHECK)
#define VALIDATE_VALID_FLAGS (MODE_ASKPASS|MODE_NONINTERACTIVE|MODE_IGNORE_TICKET|MODE_UPDATE_TICKET|MODE_VALIDATE)
#define INVALIDATE_VALID_FLAGS (MODE_ASKPASS|MODE_NONINTERACTIVE|MODE_IGNORE_TICKET|MODE_UPDATE_TICKET|MODE_INVALIDATE)
/*
* Deserialize args, settings and user_info arrays.
@@ -102,7 +102,7 @@ sudoers_policy_deserialize_info(void *v, struct defaults_list *defaults)
const char *remhost = NULL;
unsigned char uuid[16];
char * const *cur;
int flags = 0;
int flags = MODE_UPDATE_TICKET;
debug_decl(sudoers_policy_deserialize_info, SUDOERS_DEBUG_PLUGIN);
#define MATCHES(s, v) \
@@ -280,6 +280,12 @@ sudoers_policy_deserialize_info(void *v, struct defaults_list *defaults)
goto bad;
continue;
}
if (MATCHES(*cur, "update_ticket=")) {
if (parse_bool(*cur, sizeof("update_ticket") -1, &flags,
MODE_UPDATE_TICKET) == -1)
goto bad;
continue;
}
if (MATCHES(*cur, "noninteractive=")) {
if (parse_bool(*cur, sizeof("noninteractive") - 1, &flags,
MODE_NONINTERACTIVE) == -1)
@@ -395,6 +401,9 @@ sudoers_policy_deserialize_info(void *v, struct defaults_list *defaults)
}
#endif
}
/* Ignore ticket trumps update. */
if (ISSET(flags, MODE_IGNORE_TICKET))
CLR(flags, MODE_UPDATE_TICKET);
user_gid = (gid_t)-1;
user_sid = (pid_t)-1;

View File

@@ -201,7 +201,8 @@ struct sudo_user {
#define MODE_PRESERVE_ENV 0x00400000
#define MODE_NONINTERACTIVE 0x00800000
#define MODE_IGNORE_TICKET 0x01000000
#define MODE_POLICY_INTERCEPTED 0x02000000
#define MODE_UPDATE_TICKET 0x02000000
#define MODE_POLICY_INTERCEPTED 0x04000000
/* Mode bits allowed for intercepted commands. */
#define MODE_INTERCEPT_MASK (MODE_RUN|MODE_NONINTERACTIVE|MODE_IGNORE_TICKET|MODE_POLICY_INTERCEPTED)

View File

@@ -49,6 +49,7 @@ int tgetpass_flags;
*/
static void help(void) __attribute__((__noreturn__));
static void usage_excl(void) __attribute__((__noreturn__));
static void usage_excl_ticket(void) __attribute__((__noreturn__));
/*
* Mapping of command line flags to name/value settings.
@@ -63,6 +64,7 @@ static struct sudo_settings sudo_settings[] = {
{ "run_shell" },
{ "login_shell" },
{ "ignore_ticket" },
{ "update_ticket" },
{ "prompt" },
{ "selinux_role" },
{ "selinux_type" },
@@ -111,8 +113,8 @@ struct environment {
* There is a more limited set of options for sudoedit (the sudo-specific
* long options are listed first).
*/
static const char sudo_short_opts[] = "+Aa:BbC:c:D:Eeg:Hh::iKklnPp:R:r:SsT:t:U:u:Vv";
static const char edit_short_opts[] = "+Aa:BC:c:D:g:h::knp:R:r:ST:t:u:V";
static const char sudo_short_opts[] = "+Aa:BbC:c:D:Eeg:Hh::iKklNnPp:R:r:SsT:t:U:u:Vv";
static const char edit_short_opts[] = "+Aa:BC:c:D:g:h::KkNnp:R:r:ST:t:u:V";
static struct option sudo_long_opts[] = {
/* sudo-specific long options */
{ "background", no_argument, NULL, 'b' },
@@ -137,6 +139,7 @@ static struct option sudo_long_opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "host", required_argument, NULL, OPT_HOSTNAME },
{ "reset-timestamp", no_argument, NULL, 'k' },
{ "no-update", no_argument, NULL, 'N' },
{ "non-interactive", no_argument, NULL, 'n' },
{ "prompt", required_argument, NULL, 'p' },
{ "chroot", required_argument, NULL, 'R' },
@@ -403,15 +406,16 @@ parse_args(int argc, char **argv, int *old_optind, int *nargc, char ***nargv,
sudo_settings[ARG_LOGIN_SHELL].value = "true";
SET(flags, MODE_LOGIN_SHELL);
break;
case 'k':
sudo_settings[ARG_IGNORE_TICKET].value = "true";
break;
case 'K':
sudo_settings[ARG_IGNORE_TICKET].value = "true";
if (mode && mode != MODE_KILL)
usage_excl();
mode = MODE_KILL;
valid_flags = 0;
FALLTHROUGH;
case 'k':
if (sudo_settings[ARG_UPDATE_TICKET].value != NULL)
usage_excl_ticket();
sudo_settings[ARG_IGNORE_TICKET].value = "true";
break;
case 'l':
if (mode) {
@@ -423,6 +427,11 @@ parse_args(int argc, char **argv, int *old_optind, int *nargc, char ***nargv,
mode = MODE_LIST;
valid_flags = LIST_VALID_FLAGS;
break;
case 'N':
if (sudo_settings[ARG_IGNORE_TICKET].value != NULL)
usage_excl_ticket();
sudo_settings[ARG_UPDATE_TICKET].value = "false";
break;
case 'n':
SET(flags, MODE_NONINTERACTIVE);
sudo_settings[ARG_NONINTERACTIVE].value = "true";
@@ -758,6 +767,19 @@ usage_excl(void)
usage();
}
/*
* Tell which options are mutually exclusive and exit.
*/
static void
usage_excl_ticket(void)
{
debug_decl(usage_excl_ticket, SUDO_DEBUG_ARGS);
sudo_warnx("%s",
U_("Only one of the -K, -k or -N options may be specified"));
usage();
}
static void
help(void)
{

View File

@@ -1065,6 +1065,12 @@ format_plugin_settings(struct plugin_container *plugin)
unsigned int i = 0;
debug_decl(format_plugin_settings, SUDO_DEBUG_PCOMM);
/* We update the ticket entry by default. */
if (sudo_settings[ARG_IGNORE_TICKET].value == NULL &&
sudo_settings[ARG_UPDATE_TICKET].value == NULL) {
sudo_settings[ARG_UPDATE_TICKET].value = "true";
}
/* Determine sudo_settings array size (including plugin_path and NULL) */
plugin_settings_size = 2;
for (setting = sudo_settings; setting->name != NULL; setting++)

View File

@@ -84,27 +84,28 @@
#define ARG_USER_SHELL 5
#define ARG_LOGIN_SHELL 6
#define ARG_IGNORE_TICKET 7
#define ARG_PROMPT 8
#define ARG_SELINUX_ROLE 9
#define ARG_SELINUX_TYPE 10
#define ARG_RUNAS_USER 11
#define ARG_PROGNAME 12
#define ARG_IMPLIED_SHELL 13
#define ARG_PRESERVE_GROUPS 14
#define ARG_NONINTERACTIVE 15
#define ARG_SUDOEDIT 16
#define ARG_CLOSEFROM 17
#define ARG_NET_ADDRS 18
#define ARG_MAX_GROUPS 19
#define ARG_PLUGIN_DIR 20
#define ARG_REMOTE_HOST 21
#define ARG_TIMEOUT 22
#define ARG_CHROOT 23
#define ARG_CWD 24
#define ARG_ASKPASS 25
#define ARG_INTERCEPT_SETID 26
#define ARG_INTERCEPT_PTRACE 27
#define ARG_APPARMOR_PROFILE 28
#define ARG_UPDATE_TICKET 8
#define ARG_PROMPT 9
#define ARG_SELINUX_ROLE 10
#define ARG_SELINUX_TYPE 11
#define ARG_RUNAS_USER 12
#define ARG_PROGNAME 13
#define ARG_IMPLIED_SHELL 14
#define ARG_PRESERVE_GROUPS 15
#define ARG_NONINTERACTIVE 16
#define ARG_SUDOEDIT 17
#define ARG_CLOSEFROM 18
#define ARG_NET_ADDRS 19
#define ARG_MAX_GROUPS 20
#define ARG_PLUGIN_DIR 21
#define ARG_REMOTE_HOST 22
#define ARG_TIMEOUT 23
#define ARG_CHROOT 24
#define ARG_CWD 25
#define ARG_ASKPASS 26
#define ARG_INTERCEPT_SETID 27
#define ARG_INTERCEPT_PTRACE 28
#define ARG_APPARMOR_PROFILE 29
/*
* Flags for tgetpass()

View File

@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2007-2010, 2013, 2015, 2017, 2020
* Copyright (c) 2007-2010, 2013, 2015, 2017, 2020-2022
* Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -26,10 +26,10 @@
*/
#define SUDO_USAGE0 " -h | -V"
#define SUDO_USAGE1 " -h | -K | -k | -V"
#define SUDO_USAGE2 " -v [-ABknS] @BSDAUTH_USAGE@[-g group] [-h host] [-p prompt] [-u user]"
#define SUDO_USAGE3 " -l [-ABknS] @BSDAUTH_USAGE@[-g group] [-h host] [-p prompt] [-U user] [-u user] [command]"
#define SUDO_USAGE4 " [-ABbEHknPS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C num] [-D directory] @LOGINCAP_USAGE@[-g group] [-h host] [-p prompt] [-R directory] [-T timeout] [-u user] [VAR=value] [-i|-s] [<command>]"
#define SUDO_USAGE5 " -e [-ABknS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C num] @LOGINCAP_USAGE@[-D directory] [-g group] [-h host] [-p prompt] [-R directory] [-T timeout] [-u user] file ..."
#define SUDO_USAGE2 " -v [-ABkNnS] @BSDAUTH_USAGE@[-g group] [-h host] [-p prompt] [-u user]"
#define SUDO_USAGE3 " -l [-ABkNnS] @BSDAUTH_USAGE@[-g group] [-h host] [-p prompt] [-U user] [-u user] [command]"
#define SUDO_USAGE4 " [-ABbEHkNnPS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C num] [-D directory] @LOGINCAP_USAGE@[-g group] [-h host] [-p prompt] [-R directory] [-T timeout] [-u user] [VAR=value] [-i|-s] [<command>]"
#define SUDO_USAGE5 " -e [-ABkNnS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C num] @LOGINCAP_USAGE@[-D directory] [-g group] [-h host] [-p prompt] [-R directory] [-T timeout] [-u user] file ..."
/*
* Configure script arguments used to build sudo.