Push non-interactive mode checking down into the auth methods.
For "sudo -n" we only want to reject a command if user input is actually required. In the case of PAM at least, we may not need to interact with the user. Bug #956, GitHub issue #83
This commit is contained in:
@@ -45,6 +45,10 @@ Possible values of sudo_auth.flags:
|
||||
to determine whether to return a fatal or nonfatal
|
||||
error.
|
||||
|
||||
FLAG_NONINTERACTIVE If set, this indicates that the user invoked
|
||||
sudo with the -n option and no user interaction
|
||||
is allowed.
|
||||
|
||||
The member functions can return the following values:
|
||||
AUTH_SUCCESS Function succeeded. For a ``verify'' function
|
||||
this means the user correctly authenticated.
|
||||
@@ -59,6 +63,14 @@ The member functions can return the following values:
|
||||
When verify_user() gets AUTH_FATAL from an auth
|
||||
function it does an exit(1).
|
||||
|
||||
AUTH_INTR An attempt to read the password read was interrupted.
|
||||
Usually this means the user entered ^C at the
|
||||
password prompt.
|
||||
|
||||
AUTH_NONINTERACTIVE Function failed because user interaction was
|
||||
required but sudo was run in non-interactive
|
||||
mode.
|
||||
|
||||
The functions in the struct are as follows:
|
||||
|
||||
int init(struct passwd *pw, sudo_auth *auth)
|
||||
|
@@ -50,6 +50,9 @@ sudo_afs_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv
|
||||
struct ktc_token afs_token;
|
||||
debug_decl(sudo_afs_verify, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
if (IS_NONINTERACTIVE(auth))
|
||||
debug_return_int(AUTH_NONINTERACTIVE);
|
||||
|
||||
/* Try to just check the password */
|
||||
ka_StringToKey(pass, NULL, &afs_key);
|
||||
if (ka_GetAdminToken(pw->pw_name, /* name */
|
||||
|
@@ -236,6 +236,9 @@ sudo_aix_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_co
|
||||
int ret = AUTH_SUCCESS;
|
||||
debug_decl(sudo_aix_verify, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
if (IS_NONINTERACTIVE(auth))
|
||||
debug_return_int(AUTH_NONINTERACTIVE);
|
||||
|
||||
do {
|
||||
pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
|
||||
if (pass == NULL)
|
||||
|
@@ -114,6 +114,9 @@ bsdauth_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_con
|
||||
auth_session_t *as = ((struct bsdauth_state *) auth->data)->as;
|
||||
debug_decl(bsdauth_verify, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
if (IS_NONINTERACTIVE(auth))
|
||||
debug_return_int(AUTH_NONINTERACTIVE);
|
||||
|
||||
/* save old signal handler */
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
|
@@ -68,6 +68,9 @@ sudo_dce_verify(struct passwd *pw, char *plain_pw, sudo_auth *auth, struct sudo_
|
||||
error_status_t status;
|
||||
debug_decl(sudo_dce_verify, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
if (IS_NONINTERACTIVE(auth))
|
||||
debug_return_int(AUTH_NONINTERACTIVE);
|
||||
|
||||
/*
|
||||
* Create the local context of the DCE principal necessary
|
||||
* to perform authenticated network operations. The network
|
||||
|
@@ -54,6 +54,9 @@ sudo_fwtk_init(struct passwd *pw, sudo_auth *auth)
|
||||
if (auth->data != NULL)
|
||||
debug_return_int(AUTH_SUCCESS);
|
||||
|
||||
if (IS_NONINTERACTIVE(auth))
|
||||
debug_return_int(AUTH_NONINTERACTIVE);
|
||||
|
||||
if ((confp = cfg_read("sudo")) == (Cfg *)-1) {
|
||||
sudo_warnx("%s", U_("unable to read fwtk config"));
|
||||
debug_return_int(AUTH_FATAL);
|
||||
|
@@ -92,6 +92,7 @@ static struct sudo_conv_callback *conv_callback;
|
||||
static struct pam_conv pam_conv = { converse, &conv_callback };
|
||||
static char *def_prompt = PASSPROMPT;
|
||||
static bool getpass_error;
|
||||
static bool noninteractive;
|
||||
static pam_handle_t *pamh;
|
||||
static struct conv_filter *conv_filter;
|
||||
|
||||
@@ -203,6 +204,9 @@ sudo_pam_init2(struct passwd *pw, sudo_auth *auth, bool quiet)
|
||||
debug_return_int(AUTH_SUCCESS);
|
||||
}
|
||||
|
||||
/* Stash value of noninteractive flag for conversation function. */
|
||||
noninteractive = IS_NONINTERACTIVE(auth);
|
||||
|
||||
/* Initial PAM. */
|
||||
pam_service = ISSET(sudo_mode, MODE_LOGIN_SHELL) ?
|
||||
def_pam_login_service : def_pam_service;
|
||||
@@ -321,7 +325,7 @@ sudo_pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_co
|
||||
|
||||
if (getpass_error) {
|
||||
/* error or ^C from tgetpass() */
|
||||
debug_return_int(AUTH_INTR);
|
||||
debug_return_int(noninteractive ? AUTH_NONINTERACTIVE : AUTH_INTR);
|
||||
}
|
||||
switch (*pam_status) {
|
||||
case PAM_SUCCESS:
|
||||
@@ -707,6 +711,13 @@ converse(int num_msg, PAM_CONST struct pam_message **msg,
|
||||
if (getpass_error)
|
||||
goto done;
|
||||
|
||||
/* Treat non-interactive mode as a getpass error. */
|
||||
if (noninteractive) {
|
||||
getpass_error = true;
|
||||
ret = PAM_CONV_ERR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Choose either the sudo prompt or the PAM one. */
|
||||
prompt = use_pam_prompt(pm->msg) ? pm->msg : def_prompt;
|
||||
|
||||
|
@@ -67,6 +67,9 @@ sudo_securid_init(struct passwd *pw, sudo_auth *auth)
|
||||
if (auth->data != NULL)
|
||||
debug_return_int(AUTH_SUCCESS);
|
||||
|
||||
if (IS_NONINTERACTIVE(auth))
|
||||
debug_return_int(AUTH_NONINTERACTIVE);
|
||||
|
||||
/* Start communications */
|
||||
if (AceInitialize() == SD_FALSE) {
|
||||
sudo_warnx("%s", U_("failed to initialise the ACE API library"));
|
||||
|
@@ -83,6 +83,9 @@ sudo_sia_verify(struct passwd *pw, char *prompt, sudo_auth *auth,
|
||||
int rc;
|
||||
debug_decl(sudo_sia_verify, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
if (IS_NONINTERACTIVE(auth))
|
||||
debug_return_int(AUTH_NONINTERACTIVE);
|
||||
|
||||
/* Get password, return AUTH_INTR if we got ^C */
|
||||
pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
|
||||
if (pass == NULL)
|
||||
|
@@ -98,7 +98,7 @@ static bool standalone;
|
||||
* Returns 0 on success and -1 on error.
|
||||
*/
|
||||
int
|
||||
sudo_auth_init(struct passwd *pw)
|
||||
sudo_auth_init(struct passwd *pw, int mode)
|
||||
{
|
||||
sudo_auth *auth;
|
||||
int status = AUTH_SUCCESS;
|
||||
@@ -109,6 +109,8 @@ sudo_auth_init(struct passwd *pw)
|
||||
|
||||
/* Initialize auth methods and unconfigure the method if necessary. */
|
||||
for (auth = auth_switch; auth->name; auth++) {
|
||||
if (ISSET(mode, MODE_NONINTERACTIVE))
|
||||
SET(auth->flags, FLAG_NONINTERACTIVE);
|
||||
if (auth->init && !IS_DISABLED(auth)) {
|
||||
/* Disable if it failed to init unless there was a fatal error. */
|
||||
status = (auth->init)(pw, auth);
|
||||
@@ -297,6 +299,8 @@ verify_user(struct passwd *pw, char *prompt, int validated,
|
||||
status = (auth->setup)(pw, &prompt, auth);
|
||||
if (status == AUTH_FAILURE)
|
||||
SET(auth->flags, FLAG_DISABLED);
|
||||
else if (status == AUTH_NONINTERACTIVE)
|
||||
goto done;
|
||||
else if (status == AUTH_FATAL || user_interrupted())
|
||||
goto done; /* assume error msg already printed */
|
||||
}
|
||||
@@ -310,6 +314,10 @@ verify_user(struct passwd *pw, char *prompt, int validated,
|
||||
|
||||
/* Get the password unless the auth function will do it for us */
|
||||
if (!standalone) {
|
||||
if (IS_NONINTERACTIVE(auth_switch)) {
|
||||
status = AUTH_NONINTERACTIVE;
|
||||
goto done;
|
||||
}
|
||||
pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
|
||||
if (pass == NULL)
|
||||
break;
|
||||
@@ -344,10 +352,13 @@ done:
|
||||
case AUTH_INTR:
|
||||
case AUTH_FAILURE:
|
||||
if (ntries != 0)
|
||||
validated |= FLAG_BAD_PASSWORD;
|
||||
SET(validated, FLAG_BAD_PASSWORD);
|
||||
log_auth_failure(validated, ntries);
|
||||
ret = false;
|
||||
break;
|
||||
case AUTH_NONINTERACTIVE:
|
||||
SET(validated, FLAG_NO_USER_INPUT);
|
||||
FALLTHROUGH;
|
||||
case AUTH_FATAL:
|
||||
default:
|
||||
log_auth_failure(validated, 0);
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#define AUTH_FAILURE 1
|
||||
#define AUTH_INTR 2
|
||||
#define AUTH_FATAL 3
|
||||
#define AUTH_NONINTERACTIVE 4
|
||||
|
||||
typedef struct sudo_auth {
|
||||
int flags; /* various flags, see below */
|
||||
@@ -43,11 +44,13 @@ typedef struct sudo_auth {
|
||||
#define FLAG_DISABLED 0x02 /* method disabled */
|
||||
#define FLAG_STANDALONE 0x04 /* standalone auth method */
|
||||
#define FLAG_ONEANDONLY 0x08 /* one and only auth method */
|
||||
#define FLAG_NONINTERACTIVE 0x10 /* no user input allowed */
|
||||
|
||||
/* Shortcuts for using the flags above. */
|
||||
#define IS_DISABLED(x) ((x)->flags & FLAG_DISABLED)
|
||||
#define IS_STANDALONE(x) ((x)->flags & FLAG_STANDALONE)
|
||||
#define IS_ONEANDONLY(x) ((x)->flags & FLAG_ONEANDONLY)
|
||||
#define IS_NONINTERACTIVE(x) ((x)->flags & FLAG_NONINTERACTIVE)
|
||||
|
||||
/* Like tgetpass() but uses conversation function */
|
||||
char *auth_getpass(const char *prompt, int type, struct sudo_conv_callback *callback);
|
||||
|
@@ -125,13 +125,6 @@ check_user_interactive(int validated, int mode, struct getpass_closure *closure)
|
||||
FALLTHROUGH;
|
||||
|
||||
default:
|
||||
/* Bail out if we are non-interactive and a password is required */
|
||||
if (ISSET(mode, MODE_NONINTERACTIVE)) {
|
||||
validated |= FLAG_NON_INTERACTIVE;
|
||||
log_auth_failure(validated, 0);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* XXX - should not lecture if askpass helper is being used. */
|
||||
lectured = display_lecture(closure->tstat);
|
||||
|
||||
@@ -170,7 +163,7 @@ check_user(int validated, int mode)
|
||||
*/
|
||||
if ((closure.auth_pw = get_authpw(mode)) == NULL)
|
||||
goto done;
|
||||
if (sudo_auth_init(closure.auth_pw) == -1)
|
||||
if (sudo_auth_init(closure.auth_pw, mode) == -1)
|
||||
goto done;
|
||||
|
||||
/*
|
||||
|
@@ -435,7 +435,7 @@ log_auth_failure(int status, unsigned int tries)
|
||||
audit_failure(NewArgv, "%s", N_("authentication failure"));
|
||||
|
||||
/* If sudoers denied the command we'll log that separately. */
|
||||
if (!ISSET(status, FLAG_BAD_PASSWORD|FLAG_NON_INTERACTIVE))
|
||||
if (!ISSET(status, FLAG_BAD_PASSWORD|FLAG_NO_USER_INPUT))
|
||||
logit = false;
|
||||
|
||||
/*
|
||||
|
@@ -157,7 +157,7 @@ struct sudo_user {
|
||||
#define FLAG_NO_USER 0x020
|
||||
#define FLAG_NO_HOST 0x040
|
||||
#define FLAG_NO_CHECK 0x080
|
||||
#define FLAG_NON_INTERACTIVE 0x100
|
||||
#define FLAG_NO_USER_INPUT 0x100
|
||||
#define FLAG_BAD_PASSWORD 0x200
|
||||
|
||||
/*
|
||||
@@ -297,7 +297,7 @@ bool sudo_auth_needs_end_session(void);
|
||||
int verify_user(struct passwd *pw, char *prompt, int validated, struct sudo_conv_callback *callback);
|
||||
int sudo_auth_begin_session(struct passwd *pw, char **user_env[]);
|
||||
int sudo_auth_end_session(struct passwd *pw);
|
||||
int sudo_auth_init(struct passwd *pw);
|
||||
int sudo_auth_init(struct passwd *pw, int mode);
|
||||
int sudo_auth_approval(struct passwd *pw, int validated, bool exempt);
|
||||
int sudo_auth_cleanup(struct passwd *pw, bool force);
|
||||
|
||||
|
Reference in New Issue
Block a user