mirror of
https://github.com/sudo-project/sudo.git
synced 2025-09-04 16:25:25 +00:00
Merge check_user() and check_user_interactive(), move getpass callbacks.
The getpass callbacks are now defined in sudo_auth.c, which implements auth_getpass(). As a result, struct getpass_closure is now public and defined in timestamp.h.
This commit is contained in:
@@ -247,6 +247,35 @@ user_interrupted(void)
|
||||
(sigismember(&mask, SIGINT) || sigismember(&mask, SIGQUIT)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when getpass is suspended so we can drop the lock.
|
||||
*/
|
||||
static int
|
||||
getpass_suspend(int signo, void *vclosure)
|
||||
{
|
||||
struct getpass_closure *closure = vclosure;
|
||||
|
||||
timestamp_close(closure->cookie);
|
||||
closure->cookie = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when getpass is resumed so we can reacquire the lock.
|
||||
*/
|
||||
static int
|
||||
getpass_resume(int signo, void *vclosure)
|
||||
{
|
||||
struct getpass_closure *closure = vclosure;
|
||||
|
||||
closure->cookie = timestamp_open(closure->ctx);
|
||||
if (closure->cookie == NULL)
|
||||
return -1;
|
||||
if (!timestamp_lock(closure->cookie, closure->auth_pw))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the specified user.
|
||||
* Returns AUTH_SUCCESS, AUTH_FAILURE or AUTH_ERROR.
|
||||
@@ -273,6 +302,8 @@ verify_user(const struct sudoers_context *ctx, struct passwd *pw, char *prompt,
|
||||
}
|
||||
|
||||
/* Enable suspend during password entry. */
|
||||
callback->on_suspend = getpass_suspend;
|
||||
callback->on_resume = getpass_resume;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = SIG_DFL;
|
||||
|
@@ -42,116 +42,48 @@
|
||||
#include "sudoers.h"
|
||||
#include "timestamp.h"
|
||||
|
||||
struct getpass_closure {
|
||||
int tstat;
|
||||
int lectured;
|
||||
void *cookie;
|
||||
struct passwd *auth_pw;
|
||||
const struct sudoers_context *ctx;
|
||||
};
|
||||
|
||||
static struct passwd *get_authpw(struct sudoers_context *ctx, unsigned int);
|
||||
|
||||
/*
|
||||
* Called when getpass is suspended so we can drop the lock.
|
||||
* Get passwd entry for the user we are going to authenticate as.
|
||||
* By default, this is the user invoking sudo. In the most common
|
||||
* case, this matches ctx->user.pw or ctx->runas.pw.
|
||||
*/
|
||||
static int
|
||||
getpass_suspend(int signo, void *vclosure)
|
||||
static struct passwd *
|
||||
get_authpw(struct sudoers_context *ctx, unsigned int mode)
|
||||
{
|
||||
struct getpass_closure *closure = vclosure;
|
||||
struct passwd *pw = NULL;
|
||||
debug_decl(get_authpw, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
timestamp_close(closure->cookie);
|
||||
closure->cookie = NULL;
|
||||
return 0;
|
||||
if (ISSET(mode, (MODE_CHECK|MODE_LIST))) {
|
||||
/* In list mode we always prompt for the user's password. */
|
||||
sudo_pw_addref(ctx->user.pw);
|
||||
pw = ctx->user.pw;
|
||||
} else {
|
||||
if (def_rootpw) {
|
||||
if ((pw = sudo_getpwuid(ROOT_UID)) == NULL) {
|
||||
log_warningx(ctx, SLOG_SEND_MAIL, N_("unknown uid %u"),
|
||||
ROOT_UID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when getpass is resumed so we can reacquire the lock.
|
||||
*/
|
||||
static int
|
||||
getpass_resume(int signo, void *vclosure)
|
||||
{
|
||||
struct getpass_closure *closure = vclosure;
|
||||
|
||||
closure->cookie = timestamp_open(closure->ctx);
|
||||
if (closure->cookie == NULL)
|
||||
return -1;
|
||||
if (!timestamp_lock(closure->cookie, closure->auth_pw))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns AUTH_SUCCESS if the user successfully authenticates, AUTH_FAILURE
|
||||
* if not or AUTH_ERROR on fatal error.
|
||||
*/
|
||||
static int
|
||||
check_user_interactive(unsigned int validated, unsigned int mode,
|
||||
struct getpass_closure *closure)
|
||||
{
|
||||
const struct sudoers_context *ctx = closure->ctx;
|
||||
struct sudo_conv_callback callback;
|
||||
int ret = AUTH_ERROR;
|
||||
char *prompt;
|
||||
debug_decl(check_user_interactive, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
/* Construct callback for getpass function. */
|
||||
memset(&callback, 0, sizeof(callback));
|
||||
callback.version = SUDO_CONV_CALLBACK_VERSION;
|
||||
callback.closure = closure;
|
||||
callback.on_suspend = getpass_suspend;
|
||||
callback.on_resume = getpass_resume;
|
||||
|
||||
/* Open, lock and read time stamp file if we are using it. */
|
||||
if (!ISSET(mode, MODE_IGNORE_TICKET)) {
|
||||
/* Open time stamp file and check its status. */
|
||||
closure->cookie = timestamp_open(ctx);
|
||||
if (closure->cookie != NULL) {
|
||||
if (timestamp_lock(closure->cookie, closure->auth_pw)) {
|
||||
closure->tstat = timestamp_status(closure->cookie,
|
||||
closure->auth_pw);
|
||||
} else if (def_runaspw) {
|
||||
if ((pw = sudo_getpwnam(def_runas_default)) == NULL) {
|
||||
log_warningx(ctx, SLOG_SEND_MAIL,
|
||||
N_("unknown user %s"), def_runas_default);
|
||||
}
|
||||
} else if (def_targetpw) {
|
||||
if (ctx->runas.pw->pw_name == NULL) {
|
||||
/* This should never be NULL as we fake up the passwd struct */
|
||||
log_warningx(ctx, SLOG_RAW_MSG, N_("unknown uid %u"),
|
||||
(unsigned int) ctx->runas.pw->pw_uid);
|
||||
} else {
|
||||
sudo_pw_addref(ctx->runas.pw);
|
||||
pw = ctx->runas.pw;
|
||||
}
|
||||
} else {
|
||||
sudo_pw_addref(ctx->user.pw);
|
||||
pw = ctx->user.pw;
|
||||
}
|
||||
}
|
||||
|
||||
switch (closure->tstat) {
|
||||
case TS_FATAL:
|
||||
/* Fatal error (usually setuid failure), unsafe to proceed. */
|
||||
goto done;
|
||||
|
||||
case TS_CURRENT:
|
||||
/* Time stamp file is valid and current. */
|
||||
if (!ISSET(validated, FLAG_CHECK_USER)) {
|
||||
ret = AUTH_SUCCESS;
|
||||
break;
|
||||
}
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO,
|
||||
"%s: check user flag overrides time stamp", __func__);
|
||||
FALLTHROUGH;
|
||||
|
||||
default:
|
||||
if (ISSET(mode, MODE_NONINTERACTIVE) && !def_noninteractive_auth) {
|
||||
validated |= FLAG_NO_USER_INPUT;
|
||||
log_auth_failure(ctx, validated, 0);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Expand any escapes in the prompt. */
|
||||
prompt = expand_prompt(ctx,
|
||||
ctx->user.prompt ? ctx->user.prompt : def_passprompt,
|
||||
closure->auth_pw->pw_name);
|
||||
if (prompt == NULL)
|
||||
goto done;
|
||||
|
||||
ret = verify_user(ctx, closure->auth_pw, prompt, validated, &callback);
|
||||
if (ret == AUTH_SUCCESS && closure->lectured)
|
||||
(void)set_lectured(ctx->user.name); /* lecture error not fatal */
|
||||
free(prompt);
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
debug_return_int(ret);
|
||||
debug_return_ptr(pw);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -162,9 +94,12 @@ int
|
||||
check_user(struct sudoers_context *ctx, unsigned int validated,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct getpass_closure closure = { TS_ERROR };
|
||||
struct getpass_closure closure = { 0 };
|
||||
struct sudo_conv_callback callback;
|
||||
int status = TS_ERROR;
|
||||
int ret = AUTH_ERROR;
|
||||
bool exempt = false;
|
||||
char *prompt;
|
||||
debug_decl(check_user, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
/*
|
||||
@@ -219,7 +154,57 @@ check_user(struct sudoers_context *ctx, unsigned int validated,
|
||||
}
|
||||
}
|
||||
|
||||
ret = check_user_interactive(validated, mode, &closure);
|
||||
/* Construct callback for getpass function. */
|
||||
memset(&callback, 0, sizeof(callback));
|
||||
callback.version = SUDO_CONV_CALLBACK_VERSION;
|
||||
callback.closure = &closure;
|
||||
|
||||
/* Open, lock and read time stamp file if we are using it. */
|
||||
if (!ISSET(mode, MODE_IGNORE_TICKET)) {
|
||||
/* Open time stamp file and check its status. */
|
||||
closure.cookie = timestamp_open(ctx);
|
||||
if (closure.cookie != NULL) {
|
||||
if (timestamp_lock(closure.cookie, closure.auth_pw)) {
|
||||
status = timestamp_status(closure.cookie, closure.auth_pw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case TS_FATAL:
|
||||
/* Fatal error (usually setuid failure), unsafe to proceed. */
|
||||
goto done;
|
||||
|
||||
case TS_CURRENT:
|
||||
/* Time stamp file is valid and current. */
|
||||
if (!ISSET(validated, FLAG_CHECK_USER)) {
|
||||
ret = AUTH_SUCCESS;
|
||||
break;
|
||||
}
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO,
|
||||
"%s: check user flag overrides time stamp", __func__);
|
||||
FALLTHROUGH;
|
||||
|
||||
default:
|
||||
if (ISSET(mode, MODE_NONINTERACTIVE) && !def_noninteractive_auth) {
|
||||
validated |= FLAG_NO_USER_INPUT;
|
||||
log_auth_failure(ctx, validated, 0);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Expand any escapes in the prompt. */
|
||||
prompt = expand_prompt(ctx,
|
||||
ctx->user.prompt ? ctx->user.prompt : def_passprompt,
|
||||
closure.auth_pw->pw_name);
|
||||
if (prompt == NULL)
|
||||
goto done;
|
||||
|
||||
ret = verify_user(ctx, closure.auth_pw, prompt, validated, &callback);
|
||||
if (ret == AUTH_SUCCESS && closure.lectured)
|
||||
(void)set_lectured(ctx->user.name); /* lecture error not fatal */
|
||||
free(prompt);
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
if (ret == AUTH_SUCCESS) {
|
||||
@@ -231,7 +216,7 @@ done:
|
||||
* Failure to update the time stamp is not a fatal error.
|
||||
*/
|
||||
if (ret == AUTH_SUCCESS && ISSET(validated, VALIDATE_SUCCESS)) {
|
||||
if (ISSET(mode, MODE_UPDATE_TICKET) && closure.tstat != TS_ERROR)
|
||||
if (ISSET(mode, MODE_UPDATE_TICKET) && status != TS_ERROR)
|
||||
(void)timestamp_update(closure.cookie, closure.auth_pw);
|
||||
}
|
||||
}
|
||||
@@ -240,6 +225,7 @@ done:
|
||||
if (closure.auth_pw != NULL)
|
||||
sudo_pw_delref(closure.auth_pw);
|
||||
|
||||
/* TODO: return AUTH_* directly */
|
||||
switch (ret) {
|
||||
case AUTH_SUCCESS:
|
||||
debug_return_int(true);
|
||||
@@ -344,47 +330,3 @@ user_is_exempt(const struct sudoers_context *ctx)
|
||||
}
|
||||
debug_return_bool(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get passwd entry for the user we are going to authenticate as.
|
||||
* By default, this is the user invoking sudo. In the most common
|
||||
* case, this matches ctx->user.pw or ctx->runas.pw.
|
||||
*/
|
||||
static struct passwd *
|
||||
get_authpw(struct sudoers_context *ctx, unsigned int mode)
|
||||
{
|
||||
struct passwd *pw = NULL;
|
||||
debug_decl(get_authpw, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
if (ISSET(mode, (MODE_CHECK|MODE_LIST))) {
|
||||
/* In list mode we always prompt for the user's password. */
|
||||
sudo_pw_addref(ctx->user.pw);
|
||||
pw = ctx->user.pw;
|
||||
} else {
|
||||
if (def_rootpw) {
|
||||
if ((pw = sudo_getpwuid(ROOT_UID)) == NULL) {
|
||||
log_warningx(ctx, SLOG_SEND_MAIL, N_("unknown uid %u"),
|
||||
ROOT_UID);
|
||||
}
|
||||
} else if (def_runaspw) {
|
||||
if ((pw = sudo_getpwnam(def_runas_default)) == NULL) {
|
||||
log_warningx(ctx, SLOG_SEND_MAIL,
|
||||
N_("unknown user %s"), def_runas_default);
|
||||
}
|
||||
} else if (def_targetpw) {
|
||||
if (ctx->runas.pw->pw_name == NULL) {
|
||||
/* This should never be NULL as we fake up the passwd struct */
|
||||
log_warningx(ctx, SLOG_RAW_MSG, N_("unknown uid %u"),
|
||||
(unsigned int) ctx->runas.pw->pw_uid);
|
||||
} else {
|
||||
sudo_pw_addref(ctx->runas.pw);
|
||||
pw = ctx->runas.pw;
|
||||
}
|
||||
} else {
|
||||
sudo_pw_addref(ctx->user.pw);
|
||||
pw = ctx->user.pw;
|
||||
}
|
||||
}
|
||||
|
||||
debug_return_ptr(pw);
|
||||
}
|
||||
|
@@ -26,6 +26,15 @@
|
||||
|
||||
#include "auth/sudo_auth.h"
|
||||
|
||||
struct passwd;
|
||||
struct sudoers_context;
|
||||
struct getpass_closure {
|
||||
int lectured;
|
||||
void *cookie;
|
||||
struct passwd *auth_pw;
|
||||
const struct sudoers_context *ctx;
|
||||
};
|
||||
|
||||
/* Status codes for timestamp_status() */
|
||||
#define TS_CURRENT 0
|
||||
#define TS_OLD 1
|
||||
@@ -79,7 +88,6 @@ struct timestamp_entry {
|
||||
} u;
|
||||
};
|
||||
|
||||
struct sudoers_context;
|
||||
union sudo_defs_val;
|
||||
void *timestamp_open(const struct sudoers_context *ctx);
|
||||
void timestamp_close(void *vcookie);
|
||||
|
Reference in New Issue
Block a user