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
|
to determine whether to return a fatal or nonfatal
|
||||||
error.
|
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:
|
The member functions can return the following values:
|
||||||
AUTH_SUCCESS Function succeeded. For a ``verify'' function
|
AUTH_SUCCESS Function succeeded. For a ``verify'' function
|
||||||
this means the user correctly authenticated.
|
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
|
When verify_user() gets AUTH_FATAL from an auth
|
||||||
function it does an exit(1).
|
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:
|
The functions in the struct are as follows:
|
||||||
|
|
||||||
int init(struct passwd *pw, sudo_auth *auth)
|
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;
|
struct ktc_token afs_token;
|
||||||
debug_decl(sudo_afs_verify, SUDOERS_DEBUG_AUTH);
|
debug_decl(sudo_afs_verify, SUDOERS_DEBUG_AUTH);
|
||||||
|
|
||||||
|
if (IS_NONINTERACTIVE(auth))
|
||||||
|
debug_return_int(AUTH_NONINTERACTIVE);
|
||||||
|
|
||||||
/* Try to just check the password */
|
/* Try to just check the password */
|
||||||
ka_StringToKey(pass, NULL, &afs_key);
|
ka_StringToKey(pass, NULL, &afs_key);
|
||||||
if (ka_GetAdminToken(pw->pw_name, /* name */
|
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;
|
int ret = AUTH_SUCCESS;
|
||||||
debug_decl(sudo_aix_verify, SUDOERS_DEBUG_AUTH);
|
debug_decl(sudo_aix_verify, SUDOERS_DEBUG_AUTH);
|
||||||
|
|
||||||
|
if (IS_NONINTERACTIVE(auth))
|
||||||
|
debug_return_int(AUTH_NONINTERACTIVE);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
|
pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
|
||||||
if (pass == NULL)
|
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;
|
auth_session_t *as = ((struct bsdauth_state *) auth->data)->as;
|
||||||
debug_decl(bsdauth_verify, SUDOERS_DEBUG_AUTH);
|
debug_decl(bsdauth_verify, SUDOERS_DEBUG_AUTH);
|
||||||
|
|
||||||
|
if (IS_NONINTERACTIVE(auth))
|
||||||
|
debug_return_int(AUTH_NONINTERACTIVE);
|
||||||
|
|
||||||
/* save old signal handler */
|
/* save old signal handler */
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = SA_RESTART;
|
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;
|
error_status_t status;
|
||||||
debug_decl(sudo_dce_verify, SUDOERS_DEBUG_AUTH);
|
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
|
* Create the local context of the DCE principal necessary
|
||||||
* to perform authenticated network operations. The network
|
* to perform authenticated network operations. The network
|
||||||
|
@@ -54,6 +54,9 @@ sudo_fwtk_init(struct passwd *pw, sudo_auth *auth)
|
|||||||
if (auth->data != NULL)
|
if (auth->data != NULL)
|
||||||
debug_return_int(AUTH_SUCCESS);
|
debug_return_int(AUTH_SUCCESS);
|
||||||
|
|
||||||
|
if (IS_NONINTERACTIVE(auth))
|
||||||
|
debug_return_int(AUTH_NONINTERACTIVE);
|
||||||
|
|
||||||
if ((confp = cfg_read("sudo")) == (Cfg *)-1) {
|
if ((confp = cfg_read("sudo")) == (Cfg *)-1) {
|
||||||
sudo_warnx("%s", U_("unable to read fwtk config"));
|
sudo_warnx("%s", U_("unable to read fwtk config"));
|
||||||
debug_return_int(AUTH_FATAL);
|
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 struct pam_conv pam_conv = { converse, &conv_callback };
|
||||||
static char *def_prompt = PASSPROMPT;
|
static char *def_prompt = PASSPROMPT;
|
||||||
static bool getpass_error;
|
static bool getpass_error;
|
||||||
|
static bool noninteractive;
|
||||||
static pam_handle_t *pamh;
|
static pam_handle_t *pamh;
|
||||||
static struct conv_filter *conv_filter;
|
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);
|
debug_return_int(AUTH_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stash value of noninteractive flag for conversation function. */
|
||||||
|
noninteractive = IS_NONINTERACTIVE(auth);
|
||||||
|
|
||||||
/* Initial PAM. */
|
/* Initial PAM. */
|
||||||
pam_service = ISSET(sudo_mode, MODE_LOGIN_SHELL) ?
|
pam_service = ISSET(sudo_mode, MODE_LOGIN_SHELL) ?
|
||||||
def_pam_login_service : def_pam_service;
|
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) {
|
if (getpass_error) {
|
||||||
/* error or ^C from tgetpass() */
|
/* error or ^C from tgetpass() */
|
||||||
debug_return_int(AUTH_INTR);
|
debug_return_int(noninteractive ? AUTH_NONINTERACTIVE : AUTH_INTR);
|
||||||
}
|
}
|
||||||
switch (*pam_status) {
|
switch (*pam_status) {
|
||||||
case PAM_SUCCESS:
|
case PAM_SUCCESS:
|
||||||
@@ -707,6 +711,13 @@ converse(int num_msg, PAM_CONST struct pam_message **msg,
|
|||||||
if (getpass_error)
|
if (getpass_error)
|
||||||
goto done;
|
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. */
|
/* Choose either the sudo prompt or the PAM one. */
|
||||||
prompt = use_pam_prompt(pm->msg) ? pm->msg : def_prompt;
|
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)
|
if (auth->data != NULL)
|
||||||
debug_return_int(AUTH_SUCCESS);
|
debug_return_int(AUTH_SUCCESS);
|
||||||
|
|
||||||
|
if (IS_NONINTERACTIVE(auth))
|
||||||
|
debug_return_int(AUTH_NONINTERACTIVE);
|
||||||
|
|
||||||
/* Start communications */
|
/* Start communications */
|
||||||
if (AceInitialize() == SD_FALSE) {
|
if (AceInitialize() == SD_FALSE) {
|
||||||
sudo_warnx("%s", U_("failed to initialise the ACE API library"));
|
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;
|
int rc;
|
||||||
debug_decl(sudo_sia_verify, SUDOERS_DEBUG_AUTH);
|
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 */
|
/* Get password, return AUTH_INTR if we got ^C */
|
||||||
pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
|
pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
|
||||||
if (pass == NULL)
|
if (pass == NULL)
|
||||||
|
@@ -98,7 +98,7 @@ static bool standalone;
|
|||||||
* Returns 0 on success and -1 on error.
|
* Returns 0 on success and -1 on error.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
sudo_auth_init(struct passwd *pw)
|
sudo_auth_init(struct passwd *pw, int mode)
|
||||||
{
|
{
|
||||||
sudo_auth *auth;
|
sudo_auth *auth;
|
||||||
int status = AUTH_SUCCESS;
|
int status = AUTH_SUCCESS;
|
||||||
@@ -109,6 +109,8 @@ sudo_auth_init(struct passwd *pw)
|
|||||||
|
|
||||||
/* Initialize auth methods and unconfigure the method if necessary. */
|
/* Initialize auth methods and unconfigure the method if necessary. */
|
||||||
for (auth = auth_switch; auth->name; auth++) {
|
for (auth = auth_switch; auth->name; auth++) {
|
||||||
|
if (ISSET(mode, MODE_NONINTERACTIVE))
|
||||||
|
SET(auth->flags, FLAG_NONINTERACTIVE);
|
||||||
if (auth->init && !IS_DISABLED(auth)) {
|
if (auth->init && !IS_DISABLED(auth)) {
|
||||||
/* Disable if it failed to init unless there was a fatal error. */
|
/* Disable if it failed to init unless there was a fatal error. */
|
||||||
status = (auth->init)(pw, auth);
|
status = (auth->init)(pw, auth);
|
||||||
@@ -297,6 +299,8 @@ verify_user(struct passwd *pw, char *prompt, int validated,
|
|||||||
status = (auth->setup)(pw, &prompt, auth);
|
status = (auth->setup)(pw, &prompt, auth);
|
||||||
if (status == AUTH_FAILURE)
|
if (status == AUTH_FAILURE)
|
||||||
SET(auth->flags, FLAG_DISABLED);
|
SET(auth->flags, FLAG_DISABLED);
|
||||||
|
else if (status == AUTH_NONINTERACTIVE)
|
||||||
|
goto done;
|
||||||
else if (status == AUTH_FATAL || user_interrupted())
|
else if (status == AUTH_FATAL || user_interrupted())
|
||||||
goto done; /* assume error msg already printed */
|
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 */
|
/* Get the password unless the auth function will do it for us */
|
||||||
if (!standalone) {
|
if (!standalone) {
|
||||||
|
if (IS_NONINTERACTIVE(auth_switch)) {
|
||||||
|
status = AUTH_NONINTERACTIVE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
|
pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
|
||||||
if (pass == NULL)
|
if (pass == NULL)
|
||||||
break;
|
break;
|
||||||
@@ -344,10 +352,13 @@ done:
|
|||||||
case AUTH_INTR:
|
case AUTH_INTR:
|
||||||
case AUTH_FAILURE:
|
case AUTH_FAILURE:
|
||||||
if (ntries != 0)
|
if (ntries != 0)
|
||||||
validated |= FLAG_BAD_PASSWORD;
|
SET(validated, FLAG_BAD_PASSWORD);
|
||||||
log_auth_failure(validated, ntries);
|
log_auth_failure(validated, ntries);
|
||||||
ret = false;
|
ret = false;
|
||||||
break;
|
break;
|
||||||
|
case AUTH_NONINTERACTIVE:
|
||||||
|
SET(validated, FLAG_NO_USER_INPUT);
|
||||||
|
FALLTHROUGH;
|
||||||
case AUTH_FATAL:
|
case AUTH_FATAL:
|
||||||
default:
|
default:
|
||||||
log_auth_failure(validated, 0);
|
log_auth_failure(validated, 0);
|
||||||
|
@@ -20,10 +20,11 @@
|
|||||||
#define SUDO_AUTH_H
|
#define SUDO_AUTH_H
|
||||||
|
|
||||||
/* Auth function return values. */
|
/* Auth function return values. */
|
||||||
#define AUTH_SUCCESS 0
|
#define AUTH_SUCCESS 0
|
||||||
#define AUTH_FAILURE 1
|
#define AUTH_FAILURE 1
|
||||||
#define AUTH_INTR 2
|
#define AUTH_INTR 2
|
||||||
#define AUTH_FATAL 3
|
#define AUTH_FATAL 3
|
||||||
|
#define AUTH_NONINTERACTIVE 4
|
||||||
|
|
||||||
typedef struct sudo_auth {
|
typedef struct sudo_auth {
|
||||||
int flags; /* various flags, see below */
|
int flags; /* various flags, see below */
|
||||||
@@ -40,14 +41,16 @@ typedef struct sudo_auth {
|
|||||||
} sudo_auth;
|
} sudo_auth;
|
||||||
|
|
||||||
/* Values for sudo_auth.flags. */
|
/* Values for sudo_auth.flags. */
|
||||||
#define FLAG_DISABLED 0x02 /* method disabled */
|
#define FLAG_DISABLED 0x02 /* method disabled */
|
||||||
#define FLAG_STANDALONE 0x04 /* standalone auth method */
|
#define FLAG_STANDALONE 0x04 /* standalone auth method */
|
||||||
#define FLAG_ONEANDONLY 0x08 /* one and only 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. */
|
/* Shortcuts for using the flags above. */
|
||||||
#define IS_DISABLED(x) ((x)->flags & FLAG_DISABLED)
|
#define IS_DISABLED(x) ((x)->flags & FLAG_DISABLED)
|
||||||
#define IS_STANDALONE(x) ((x)->flags & FLAG_STANDALONE)
|
#define IS_STANDALONE(x) ((x)->flags & FLAG_STANDALONE)
|
||||||
#define IS_ONEANDONLY(x) ((x)->flags & FLAG_ONEANDONLY)
|
#define IS_ONEANDONLY(x) ((x)->flags & FLAG_ONEANDONLY)
|
||||||
|
#define IS_NONINTERACTIVE(x) ((x)->flags & FLAG_NONINTERACTIVE)
|
||||||
|
|
||||||
/* Like tgetpass() but uses conversation function */
|
/* Like tgetpass() but uses conversation function */
|
||||||
char *auth_getpass(const char *prompt, int type, struct sudo_conv_callback *callback);
|
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;
|
FALLTHROUGH;
|
||||||
|
|
||||||
default:
|
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. */
|
/* XXX - should not lecture if askpass helper is being used. */
|
||||||
lectured = display_lecture(closure->tstat);
|
lectured = display_lecture(closure->tstat);
|
||||||
|
|
||||||
@@ -170,7 +163,7 @@ check_user(int validated, int mode)
|
|||||||
*/
|
*/
|
||||||
if ((closure.auth_pw = get_authpw(mode)) == NULL)
|
if ((closure.auth_pw = get_authpw(mode)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (sudo_auth_init(closure.auth_pw) == -1)
|
if (sudo_auth_init(closure.auth_pw, mode) == -1)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -435,7 +435,7 @@ log_auth_failure(int status, unsigned int tries)
|
|||||||
audit_failure(NewArgv, "%s", N_("authentication failure"));
|
audit_failure(NewArgv, "%s", N_("authentication failure"));
|
||||||
|
|
||||||
/* If sudoers denied the command we'll log that separately. */
|
/* 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;
|
logit = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -157,7 +157,7 @@ struct sudo_user {
|
|||||||
#define FLAG_NO_USER 0x020
|
#define FLAG_NO_USER 0x020
|
||||||
#define FLAG_NO_HOST 0x040
|
#define FLAG_NO_HOST 0x040
|
||||||
#define FLAG_NO_CHECK 0x080
|
#define FLAG_NO_CHECK 0x080
|
||||||
#define FLAG_NON_INTERACTIVE 0x100
|
#define FLAG_NO_USER_INPUT 0x100
|
||||||
#define FLAG_BAD_PASSWORD 0x200
|
#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 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_begin_session(struct passwd *pw, char **user_env[]);
|
||||||
int sudo_auth_end_session(struct passwd *pw);
|
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_approval(struct passwd *pw, int validated, bool exempt);
|
||||||
int sudo_auth_cleanup(struct passwd *pw, bool force);
|
int sudo_auth_cleanup(struct passwd *pw, bool force);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user