Remove check_sudoedit function pointer in struct sudo_policy.

Instead, sudo will set sudoedit=true in the settings array.
The plugin should check for this and modify argv_out as appropriate
in check_policy.
This commit is contained in:
Todd C. Miller
2010-05-17 10:25:27 -04:00
parent 3ac9aee52e
commit fa717176b2
8 changed files with 121 additions and 164 deletions

View File

@@ -139,7 +139,7 @@
.\" ======================================================================== .\" ========================================================================
.\" .\"
.IX Title "SUDO_PLUGIN @mansectsu@" .IX Title "SUDO_PLUGIN @mansectsu@"
.TH SUDO_PLUGIN @mansectsu@ "May 14, 2010" "1.8.0a1" "MAINTENANCE COMMANDS" .TH SUDO_PLUGIN @mansectsu@ "May 17, 2010" "1.8.0a1" "MAINTENANCE COMMANDS"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents. .\" way too many mistakes in technical documents.
.if n .ad l .if n .ad l
@@ -219,9 +219,6 @@ so that \fBsudo\fR can load it.
\& const char *list_user); \& const char *list_user);
\& int (*validate)(void); \& int (*validate)(void);
\& void (*invalidate)(int remove); \& void (*invalidate)(int remove);
\& int (*check_sudoedit)(int argc, char * const argv[],
\& char *env_add[], char **command_info[],
\& char **argv_out[], char **user_env_out[]);
\& }; \& };
.Ve .Ve
.PP .PP
@@ -348,6 +345,13 @@ systems where \s-1BSD\s0 authentication is supported.
.IP "progname=string" 4 .IP "progname=string" 4
.IX Item "progname=string" .IX Item "progname=string"
The command name that sudo was run as, typically \*(L"sudo\*(R" or \*(L"sudoedit\*(R". The command name that sudo was run as, typically \*(L"sudo\*(R" or \*(L"sudoedit\*(R".
.IP "sudoedit=bool" 4
.IX Item "sudoedit=bool"
Set to true when the \f(CW\*(C`\-e\*(C'\fR flag is is specified or if invoked as
\&\fBsudoedit\fR. The plugin shall substitute an editor into \fIargv\fR
in the \fIcheck_policy\fR function or return \f(CW\*(C`\-2\*(C'\fR with a usage error
if the plugin does not support \fIsudoedit\fR. For more information,
see the \fIcheck_policy\fR section.
.RE .RE
.RS 4 .RS 4
.Sp .Sp
@@ -455,13 +459,31 @@ information, the verbose flag will be set.
.Ve .Ve
.Sp .Sp
The \fIcheck_policy\fR function is called by \fBsudo\fR to determine The \fIcheck_policy\fR function is called by \fBsudo\fR to determine
whether the user is allowed to run the specified commands. Returns whether the user is allowed to run the specified commands.
1 if the command is allowed, 0 if not allowed, \-1 for a general .Sp
error, or \-2 for a usage error. In the latter case, \fBsudo\fR will If the \fIsudoedit\fR option was enabled in the \fIsettings\fR array
print a usage message before it exits. If an error occurs, the passed to the \fIopen\fR function, the user has requested \fIsudoedit\fR
plugin may optionally call the conversation or plugin_printf function mode. \fIsudoedit\fR is a mechanism for editing one or more files
with \f(CW\*(C`SUDO_CONF_ERROR_MSG\*(C'\fR to present additional error information where an editor is run with the user's credentials instead of with
to the user. elevated privileges. \fBsudo\fR achieves this by creating user-writable
temporary copies of the files to be edited and then overwriting the
originals with the temporary copies after editing is complete. If
the plugin supports \fBsudoedit\fR, it should choose the editor to be
used, potentially from a variable in the user's environment, such
as \f(CW\*(C`EDITOR\*(C'\fR, and include it in \fIargv_out\fR (note that environment
variables may include command line flags). The files to be edited
should be copied from \fIargv\fR into \fIargv_out\fR, separated from the
editor and its arguments by a \f(CW"\-\-"\fR element. The \f(CW"\-\-"\fR will
be removed by \fBsudo\fR before the editor is executed. The plugin
should also set \fIsudoedit=true\fR in the \fIcommand_info\fR list.
.Sp
The \fIcheck_policy\fR function returns 1 if the command is allowed,
0 if not allowed, \-1 for a general error, or \-2 for a usage error
or if \fBsudoedit\fR was specified but is unsupported by the plugin.
In the latter case, \fBsudo\fR will print a usage message before it
exits. If an error occurs, the plugin may optionally call the
conversation or plugin_printf function with \f(CW\*(C`SUDO_CONF_ERROR_MSG\*(C'\fR
to present additional error information to the user.
.Sp .Sp
The function arguments are as follows: The function arguments are as follows:
.RS 4 .RS 4
@@ -550,6 +572,12 @@ SELinux type to use when executing the command.
.IX Item "timeout=int" .IX Item "timeout=int"
Command timeout. If non-zero then when the timeout expires the Command timeout. If non-zero then when the timeout expires the
command will be killed. command will be killed.
.IP "sudoedit=bool" 4
.IX Item "sudoedit=bool"
Set to true when in \fIsudoedit\fR mode. The plugin may enable
\&\fIsudoedit\fR mode even if \fBsudo\fR was not invoked as \fBsudoedit\fR.
This allows the plugin to perform command substitution and transparently
enable \fIsudoedit\fR when the user attempts to run an editor.
.RE .RE
.RS 4 .RS 4
.Sp .Sp
@@ -638,38 +666,6 @@ the credentials instead of simply invalidating them.
.Sp .Sp
The \f(CW\*(C`invalidate\*(C'\fR function should be \f(CW\*(C`NULL\*(C'\fR if the plugin does not The \f(CW\*(C`invalidate\*(C'\fR function should be \f(CW\*(C`NULL\*(C'\fR if the plugin does not
support credential caching. support credential caching.
.IP "check_sudoedit" 4
.IX Item "check_sudoedit"
.Vb 3
\& int (*check_sudoedit)(int argc, char * const argv[]
\& char *env_add[], char **command_info[],
\& char **argv_out[], char **user_env_out[]);
.Ve
.Sp
The \fIcheck_sudoedit\fR function is called instead of \fIcheck_policy\fR
when \fBsudo\fR is invoked as \fBsudoedit\fR or when the \f(CW\*(C`\-e\*(C'\fR flag is
specified. \fBsudoedit\fR is a mechanism for editing one or more files
where an editor is run with the user's credentials instead of with
elevated privileges. \fBsudo\fR achieves this by creating user-writable
temporary copies of the files to be edited and then overwriting the
originals with the temporary copies after editing is complete.
.Sp
The plugin should choose the editor to be used, potentially from a
variable in the user's environment, such as \f(CW\*(C`EDITOR\*(C'\fR, and include
it in \fIargv_out\fR. The files to be edited should be copied from
\&\fIargv\fR into \fIargv_out\fR, separated from the editor and its arguments
by a \f(CW"\-\-"\fR element. The \f(CW"\-\-"\fR will be removed by \fBsudo\fR before
the editor is executed.
.Sp
Returns 1 if the command is allowed, 0 if not allowed, \-1 for a
general error, or \-2 for a usage error. In the latter case, \fBsudo\fR
will print a usage message before it exits. If an error occurs,
the plugin may optionally call the conversation or plugin_printf
function with \f(CW\*(C`SUDO_CONF_ERROR_MSG\*(C'\fR to present additional error
information to the user.
.Sp
The function arguments are the same as for \fIcheck_policy\fR, except
that \fIargv[0]\fR will always be the string \*(L"sudoedit\*(R".
.PP .PP
\fIConversation \s-1API\s0\fR \fIConversation \s-1API\s0\fR
.IX Subsection "Conversation API" .IX Subsection "Conversation API"
@@ -1035,3 +1031,9 @@ the plugin type.
\& struct io_plugin io; \& struct io_plugin io;
\& }; \& };
.Ve .Ve
.SH "POD ERRORS"
.IX Header "POD ERRORS"
Hey! \fBThe above document had some coding errors, which are explained below:\fR
.IP "Around line 597:" 4
.IX Item "Around line 597:"
You forgot a '=back' before '=head3'

View File

@@ -89,9 +89,6 @@ so that B<sudo> can load it.
const char *list_user); const char *list_user);
int (*validate)(void); int (*validate)(void);
void (*invalidate)(int remove); void (*invalidate)(int remove);
int (*check_sudoedit)(int argc, char * const argv[],
char *env_add[], char **command_info[],
char **argv_out[], char **user_env_out[]);
}; };
The policy_plugin struct has the following fields: The policy_plugin struct has the following fields:
@@ -243,6 +240,14 @@ systems where BSD authentication is supported.
The command name that sudo was run as, typically "sudo" or "sudoedit". The command name that sudo was run as, typically "sudo" or "sudoedit".
=item sudoedit=bool
Set to true when the C<-e> flag is is specified or if invoked as
B<sudoedit>. The plugin shall substitute an editor into I<argv>
in the I<check_policy> function or return C<-2> with a usage error
if the plugin does not support I<sudoedit>. For more information,
see the I<check_policy> section.
=back =back
Additional settings may be added in the future so the plugin should Additional settings may be added in the future so the plugin should
@@ -357,13 +362,31 @@ information, the verbose flag will be set.
char **argv_out[], char **user_env_out[]); char **argv_out[], char **user_env_out[]);
The I<check_policy> function is called by B<sudo> to determine The I<check_policy> function is called by B<sudo> to determine
whether the user is allowed to run the specified commands. Returns whether the user is allowed to run the specified commands.
1 if the command is allowed, 0 if not allowed, -1 for a general
error, or -2 for a usage error. In the latter case, B<sudo> will If the I<sudoedit> option was enabled in the I<settings> array
print a usage message before it exits. If an error occurs, the passed to the I<open> function, the user has requested I<sudoedit>
plugin may optionally call the conversation or plugin_printf function mode. I<sudoedit> is a mechanism for editing one or more files
with C<SUDO_CONF_ERROR_MSG> to present additional error information where an editor is run with the user's credentials instead of with
to the user. elevated privileges. B<sudo> achieves this by creating user-writable
temporary copies of the files to be edited and then overwriting the
originals with the temporary copies after editing is complete. If
the plugin supports B<sudoedit>, it should choose the editor to be
used, potentially from a variable in the user's environment, such
as C<EDITOR>, and include it in I<argv_out> (note that environment
variables may include command line flags). The files to be edited
should be copied from I<argv> into I<argv_out>, separated from the
editor and its arguments by a C<"--"> element. The C<"--"> will
be removed by B<sudo> before the editor is executed. The plugin
should also set I<sudoedit=true> in the I<command_info> list.
The I<check_policy> function returns 1 if the command is allowed,
0 if not allowed, -1 for a general error, or -2 for a usage error
or if B<sudoedit> was specified but is unsupported by the plugin.
In the latter case, B<sudo> will print a usage message before it
exits. If an error occurs, the plugin may optionally call the
conversation or plugin_printf function with C<SUDO_CONF_ERROR_MSG>
to present additional error information to the user.
The function arguments are as follows: The function arguments are as follows:
@@ -475,6 +498,13 @@ SELinux type to use when executing the command.
Command timeout. If non-zero then when the timeout expires the Command timeout. If non-zero then when the timeout expires the
command will be killed. command will be killed.
=item sudoedit=bool
Set to true when in I<sudoedit> mode. The plugin may enable
I<sudoedit> mode even if B<sudo> was not invoked as B<sudoedit>.
This allows the plugin to perform command substitution and transparently
enable I<sudoedit> when the user attempts to run an editor.
=back =back
Unsupported values will be ignored. Unsupported values will be ignored.
@@ -564,39 +594,6 @@ the credentials instead of simply invalidating them.
The C<invalidate> function should be C<NULL> if the plugin does not The C<invalidate> function should be C<NULL> if the plugin does not
support credential caching. support credential caching.
=item check_sudoedit
int (*check_sudoedit)(int argc, char * const argv[]
char *env_add[], char **command_info[],
char **argv_out[], char **user_env_out[]);
The I<check_sudoedit> function is called instead of I<check_policy>
when B<sudo> is invoked as B<sudoedit> or when the C<-e> flag is
specified. B<sudoedit> is a mechanism for editing one or more files
where an editor is run with the user's credentials instead of with
elevated privileges. B<sudo> achieves this by creating user-writable
temporary copies of the files to be edited and then overwriting the
originals with the temporary copies after editing is complete.
The plugin should choose the editor to be used, potentially from a
variable in the user's environment, such as C<EDITOR>, and include
it in I<argv_out>. The files to be edited should be copied from
I<argv> into I<argv_out>, separated from the editor and its arguments
by a C<"--"> element. The C<"--"> will be removed by B<sudo> before
the editor is executed.
Returns 1 if the command is allowed, 0 if not allowed, -1 for a
general error, or -2 for a usage error. In the latter case, B<sudo>
will print a usage message before it exits. If an error occurs,
the plugin may optionally call the conversation or plugin_printf
function with C<SUDO_CONF_ERROR_MSG> to present additional error
information to the user.
The function arguments are the same as for I<check_policy>, except
that I<argv[0]> will always be the string "sudoedit".
=back
=head3 Conversation API =head3 Conversation API
If the plugin needs to interact with the user, it may do so via the If the plugin needs to interact with the user, it may do so via the

View File

@@ -68,9 +68,6 @@ struct policy_plugin {
const char *list_user); const char *list_user);
int (*validate)(void); int (*validate)(void);
void (*invalidate)(int remove); void (*invalidate)(int remove);
int (*check_sudoedit)(int argc, char * const argv[],
char *env_add[], char **command_info[],
char **argv_out[], char **user_env_out[]);
}; };
/* I/O plugin type and defines */ /* I/O plugin type and defines */

View File

@@ -84,6 +84,7 @@ static sudo_printf_t sudo_log;
static FILE *input, *output; static FILE *input, *output;
static uid_t runas_uid = ROOT_UID; static uid_t runas_uid = ROOT_UID;
static gid_t runas_gid = -1; static gid_t runas_gid = -1;
static int use_sudoedit = FALSE;
/* /*
* Allocate storage for a name=value string and return it. * Allocate storage for a name=value string and return it.
@@ -147,6 +148,11 @@ policy_open(unsigned int version, sudo_conv_t conversation,
setprogname(*ui + sizeof("progname=") - 1); setprogname(*ui + sizeof("progname=") - 1);
} }
#endif #endif
/* Check to see if sudo was called as sudoedit or with -e flag. */
if (strncmp(*ui, "sudoedit=", sizeof("sudoedit=") - 1) == 0) {
if (strcasecmp(*ui + sizeof("sudoedit=") - 1, "true") == 0)
use_sudoedit = TRUE;
}
} }
if (runas_user != NULL) { if (runas_user != NULL) {
if ((pw = getpwnam(runas_user)) == NULL) { if ((pw = getpwnam(runas_user)) == NULL) {
@@ -230,7 +236,7 @@ check_passwd(void)
} }
static char ** static char **
build_command_info(char *command, int sudoedit) build_command_info(char *command)
{ {
static char **command_info; static char **command_info;
int i = 0; int i = 0;
@@ -250,7 +256,7 @@ build_command_info(char *command, int sudoedit)
return NULL; return NULL;
} }
} }
if (sudoedit) { if (use_sudoedit) {
command_info[i] = strdup("sudoedit=true"); command_info[i] = strdup("sudoedit=true");
if (command_info[i++] == NULL) if (command_info[i++] == NULL)
return NULL; return NULL;
@@ -344,18 +350,20 @@ policy_check(int argc, char * const argv[],
return FALSE; return FALSE;
} }
/* /* If "sudo vi" is run, auto-convert to sudoedit. */
* If "sudo vi" is run, auto-convert to sudoedit. if (strcmp(command, _PATH_VI) == 0)
*/ use_sudoedit = TRUE;
if (strcmp(command, _PATH_VI) == 0) {
if (use_sudoedit) {
/* Rebuild argv using editor */ /* Rebuild argv using editor */
command = find_editor(argc - 1, argv + 1, argv_out); command = find_editor(argc - 1, argv + 1, argv_out);
if (command == NULL) { if (command == NULL) {
sudo_log(SUDO_CONV_ERROR_MSG, "unable to find valid editor\n"); sudo_log(SUDO_CONV_ERROR_MSG, "unable to find valid editor\n");
return ERROR; return ERROR;
} }
use_sudoedit = TRUE;
} else { } else {
/* No changes to argv */ /* No changes needd to argv */
*argv_out = (char **)argv; *argv_out = (char **)argv;
} }
@@ -363,41 +371,7 @@ policy_check(int argc, char * const argv[],
*user_env_out = plugin_state.envp; *user_env_out = plugin_state.envp;
/* Setup command info. */ /* Setup command info. */
*command_info_out = build_command_info(command, *argv_out != argv); *command_info_out = build_command_info(command);
if (*command_info_out == NULL) {
sudo_log(SUDO_CONV_ERROR_MSG, "out of memory\n");
return ERROR;
}
return TRUE;
}
/*
* Plugin policy edit function.
* Simple example that prompts for a password, hard-coded to "test".
*/
static int
policy_edit(int argc, char * const argv[],
char *env_add[], char **command_info_out[],
char **argv_out[], char **user_env_out[])
{
char *editor;
if (!check_passwd())
return FALSE;
/* Rebuild argv using editor */
editor = find_editor(argc - 1, argv + 1, argv_out);
if (editor == NULL) {
sudo_log(SUDO_CONV_ERROR_MSG, "unable to find valid editor\n");
return ERROR;
}
/* No changes to envp */
*user_env_out = plugin_state.envp;
/* Setup command info. */
*command_info_out = build_command_info(editor, TRUE);
if (*command_info_out == NULL) { if (*command_info_out == NULL) {
sudo_log(SUDO_CONV_ERROR_MSG, "out of memory\n"); sudo_log(SUDO_CONV_ERROR_MSG, "out of memory\n");
return ERROR; return ERROR;
@@ -512,8 +486,7 @@ struct policy_plugin sample_policy = {
policy_check, policy_check,
policy_list, policy_list,
NULL, /* validate */ NULL, /* validate */
NULL, /* invalidate */ NULL /* invalidate */
policy_edit
}; };
/* /*

View File

@@ -565,6 +565,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
if (!editor) if (!editor)
goto done; goto done;
command_info[info_len++] = fmt_string("command", editor); command_info[info_len++] = fmt_string("command", editor);
command_info[info_len++] = estrdup("sudoedit=true");
} else { } else {
command_info[info_len++] = fmt_string("command", safe_cmnd); command_info[info_len++] = fmt_string("command", safe_cmnd);
} }
@@ -623,16 +624,6 @@ sudoers_policy_check(int argc, char * const argv[], char *env_add[],
argv_out, user_env_out); argv_out, user_env_out);
} }
static int
sudoers_policy_sudoedit(int argc, char * const argv[], char *env_add[],
char **command_infop[], char **argv_out[], char **user_env_out[])
{
SET(sudo_mode, MODE_EDIT);
return sudoers_policy_main(argc, argv, 0, env_add, command_infop,
argv_out, user_env_out);
}
static int static int
sudoers_policy_validate(void) sudoers_policy_validate(void)
{ {
@@ -1226,6 +1217,11 @@ deserialize_info(char * const settings[], char * const user_info[])
SET(flags, MODE_IGNORE_TICKET); SET(flags, MODE_IGNORE_TICKET);
continue; continue;
} }
if (MATCHES(*cur, "sudoedit=")) {
if (atobool(*cur + sizeof("sudoedit=") - 1) == TRUE)
SET(flags, MODE_EDIT);
continue;
}
if (MATCHES(*cur, "login_class=")) { if (MATCHES(*cur, "login_class=")) {
login_class = *cur + sizeof("login_class=") - 1; login_class = *cur + sizeof("login_class=") - 1;
def_use_loginclass = TRUE; def_use_loginclass = TRUE;
@@ -1409,8 +1405,7 @@ struct policy_plugin sudoers_policy = {
sudoers_policy_check, sudoers_policy_check,
sudoers_policy_list, sudoers_policy_list,
sudoers_policy_validate, sudoers_policy_validate,
sudoers_policy_invalidate, sudoers_policy_invalidate
sudoers_policy_sudoedit
}; };
struct io_plugin sudoers_io = { struct io_plugin sudoers_io = {

View File

@@ -107,7 +107,9 @@ static struct sudo_settings {
{ "preserve_groups" }, { "preserve_groups" },
#define ARG_NONINTERACTIVE 15 #define ARG_NONINTERACTIVE 15
{ "noninteractive" }, { "noninteractive" },
#define NUM_SETTINGS 16 #define ARG_SUDOEDIT 16
{ "sudoedit" },
#define NUM_SETTINGS 17
{ NULL } { NULL }
}; };
@@ -135,8 +137,10 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp,
sudo_settings[ARG_PROGNAME].value = getprogname(); sudo_settings[ARG_PROGNAME].value = getprogname();
/* First, check to see if we were invoked as "sudoedit". */ /* First, check to see if we were invoked as "sudoedit". */
if (strcmp(getprogname(), "sudoedit") == 0) if (strcmp(getprogname(), "sudoedit") == 0) {
mode = MODE_EDIT; mode = MODE_EDIT;
sudo_settings[ARG_SUDOEDIT].value = "true";
}
/* Returns true if the last option string was "--" */ /* Returns true if the last option string was "--" */
#define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \ #define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \
@@ -194,6 +198,7 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp,
if (mode && mode != MODE_EDIT) if (mode && mode != MODE_EDIT)
usage_excl(1); usage_excl(1);
mode = MODE_EDIT; mode = MODE_EDIT;
sudo_settings[ARG_SUDOEDIT].value = "true";
valid_flags = MODE_NONINTERACTIVE; valid_flags = MODE_NONINTERACTIVE;
break; break;
case 'g': case 'g':

View File

@@ -199,21 +199,9 @@ main(int argc, char *argv[], char *envp[])
} }
exit(ok != TRUE); exit(ok != TRUE);
case MODE_EDIT: case MODE_EDIT:
if (!policy_plugin.u.policy->check_sudoedit)
errorx(1, "policy plugin %s does not support sudoedit",
policy_plugin.name);
/* FALLTHROUGH */
case MODE_RUN: case MODE_RUN:
if (sudo_mode & MODE_EDIT) { ok = policy_plugin.u.policy->check_policy(nargc, nargv, env_add,
/* XXX - must be able to tell which are the files in argv */ &command_info, &argv_out, &user_env_out);
/* as opposed to editor flags; could use original argv */
/* and only use argv_out for the command path + args */
ok = policy_plugin.u.policy->check_sudoedit(nargc, nargv,
env_add, &command_info, &argv_out, &user_env_out);
} else {
ok = policy_plugin.u.policy->check_policy(nargc, nargv, env_add,
&command_info, &argv_out, &user_env_out);
}
sudo_debug(8, "policy plugin returns %d", ok); sudo_debug(8, "policy plugin returns %d", ok);
if (ok != TRUE) { if (ok != TRUE) {
if (ok == -2) if (ok == -2)
@@ -240,8 +228,6 @@ main(int argc, char *argv[], char *envp[])
} }
} }
command_info_to_details(command_info, &command_details); command_info_to_details(command_info, &command_details);
if (ISSET(sudo_mode, MODE_EDIT))
SET(command_details.flags, CD_SUDOEDIT);
/* Restore coredumpsize resource limit before running. */ /* Restore coredumpsize resource limit before running. */
#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL) #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
(void) setrlimit(RLIMIT_CORE, &corelimit); (void) setrlimit(RLIMIT_CORE, &corelimit);

View File

@@ -143,8 +143,10 @@ sudo_edit(struct command_details *command_details, char *argv[], char *envp[])
else else
editor_argc++; editor_argc++;
} }
if (nfiles == 0) if (nfiles == 0) {
warningx("plugin error: missing file list for sudoedit");
return 1; return 1;
}
/* /*
* For each file specified by the user, make a temporary version * For each file specified by the user, make a temporary version