From fa717176b295191b944bb159cb870704e1119d16 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Mon, 17 May 2010 10:25:27 -0400 Subject: [PATCH] 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. --- doc/sudo_plugin.man.in | 88 +++++++++++++++++----------------- doc/sudo_plugin.pod | 83 ++++++++++++++++---------------- include/sudo_plugin.h | 3 -- plugins/sample/sample_plugin.c | 61 +++++++---------------- plugins/sudoers/sudoers.c | 19 +++----- src/parse_args.c | 9 +++- src/sudo.c | 18 +------ src/sudo_edit.c | 4 +- 8 files changed, 121 insertions(+), 164 deletions(-) diff --git a/doc/sudo_plugin.man.in b/doc/sudo_plugin.man.in index be3746340..4ae007749 100644 --- a/doc/sudo_plugin.man.in +++ b/doc/sudo_plugin.man.in @@ -139,7 +139,7 @@ .\" ======================================================================== .\" .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 .\" way too many mistakes in technical documents. .if n .ad l @@ -219,9 +219,6 @@ so that \fBsudo\fR can load it. \& const char *list_user); \& int (*validate)(void); \& 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 .PP @@ -348,6 +345,13 @@ systems where \s-1BSD\s0 authentication is supported. .IP "progname=string" 4 .IX Item "progname=string" 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 .RS 4 .Sp @@ -455,13 +459,31 @@ information, the verbose flag will be set. .Ve .Sp The \fIcheck_policy\fR function is called by \fBsudo\fR to determine -whether the user is allowed to run the specified commands. 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. +whether the user is allowed to run the specified commands. +.Sp +If the \fIsudoedit\fR option was enabled in the \fIsettings\fR array +passed to the \fIopen\fR function, the user has requested \fIsudoedit\fR +mode. \fIsudoedit\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. 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 The function arguments are as follows: .RS 4 @@ -550,6 +572,12 @@ SELinux type to use when executing the command. .IX Item "timeout=int" Command timeout. If non-zero then when the timeout expires the 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 .RS 4 .Sp @@ -638,38 +666,6 @@ the credentials instead of simply invalidating them. .Sp The \f(CW\*(C`invalidate\*(C'\fR function should be \f(CW\*(C`NULL\*(C'\fR if the plugin does not 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 \fIConversation \s-1API\s0\fR .IX Subsection "Conversation API" @@ -1035,3 +1031,9 @@ the plugin type. \& struct io_plugin io; \& }; .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' diff --git a/doc/sudo_plugin.pod b/doc/sudo_plugin.pod index fb8a1ac40..0c03beeb0 100644 --- a/doc/sudo_plugin.pod +++ b/doc/sudo_plugin.pod @@ -89,9 +89,6 @@ so that B can load it. const char *list_user); int (*validate)(void); 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: @@ -243,6 +240,14 @@ systems where BSD authentication is supported. 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. The plugin shall substitute an editor into I +in the I function or return C<-2> with a usage error +if the plugin does not support I. For more information, +see the I section. + =back 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[]); The I function is called by B to determine -whether the user is allowed to run the specified commands. 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 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 to present additional error information -to the user. +whether the user is allowed to run the specified commands. + +If the I option was enabled in the I array +passed to the I function, the user has requested I +mode. I 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 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, it should choose the editor to be +used, potentially from a variable in the user's environment, such +as C, and include it in I (note that environment +variables may include command line flags). The files to be edited +should be copied from I into I, separated from the +editor and its arguments by a C<"--"> element. The C<"--"> will +be removed by B before the editor is executed. The plugin +should also set I in the I list. + +The I 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 was specified but is unsupported by the plugin. +In the latter case, B 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 +to present additional error information to the user. 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 will be killed. +=item sudoedit=bool + +Set to true when in I mode. The plugin may enable +I mode even if B was not invoked as B. +This allows the plugin to perform command substitution and transparently +enable I when the user attempts to run an editor. + =back Unsupported values will be ignored. @@ -564,39 +594,6 @@ the credentials instead of simply invalidating them. The C function should be C if the plugin does not 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 function is called instead of I -when B is invoked as B or when the C<-e> flag is -specified. B 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 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, and include -it in I. The files to be edited should be copied from -I into I, separated from the editor and its arguments -by a C<"--"> element. The C<"--"> will be removed by B 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 -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 to present additional error -information to the user. - -The function arguments are the same as for I, except -that I will always be the string "sudoedit". - -=back - =head3 Conversation API If the plugin needs to interact with the user, it may do so via the diff --git a/include/sudo_plugin.h b/include/sudo_plugin.h index 0ad4e619f..26289f901 100644 --- a/include/sudo_plugin.h +++ b/include/sudo_plugin.h @@ -68,9 +68,6 @@ struct policy_plugin { const char *list_user); int (*validate)(void); 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 */ diff --git a/plugins/sample/sample_plugin.c b/plugins/sample/sample_plugin.c index e8fcaeb1e..07990a3d7 100644 --- a/plugins/sample/sample_plugin.c +++ b/plugins/sample/sample_plugin.c @@ -84,6 +84,7 @@ static sudo_printf_t sudo_log; static FILE *input, *output; static uid_t runas_uid = ROOT_UID; static gid_t runas_gid = -1; +static int use_sudoedit = FALSE; /* * 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); } #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 ((pw = getpwnam(runas_user)) == NULL) { @@ -230,7 +236,7 @@ check_passwd(void) } static char ** -build_command_info(char *command, int sudoedit) +build_command_info(char *command) { static char **command_info; int i = 0; @@ -250,7 +256,7 @@ build_command_info(char *command, int sudoedit) return NULL; } } - if (sudoedit) { + if (use_sudoedit) { command_info[i] = strdup("sudoedit=true"); if (command_info[i++] == NULL) return NULL; @@ -344,18 +350,20 @@ policy_check(int argc, char * const argv[], return FALSE; } - /* - * If "sudo vi" is run, auto-convert to sudoedit. - */ - if (strcmp(command, _PATH_VI) == 0) { + /* If "sudo vi" is run, auto-convert to sudoedit. */ + if (strcmp(command, _PATH_VI) == 0) + use_sudoedit = TRUE; + + if (use_sudoedit) { /* Rebuild argv using editor */ command = find_editor(argc - 1, argv + 1, argv_out); if (command == NULL) { sudo_log(SUDO_CONV_ERROR_MSG, "unable to find valid editor\n"); return ERROR; } + use_sudoedit = TRUE; } else { - /* No changes to argv */ + /* No changes needd to argv */ *argv_out = (char **)argv; } @@ -363,41 +371,7 @@ policy_check(int argc, char * const argv[], *user_env_out = plugin_state.envp; /* Setup command info. */ - *command_info_out = build_command_info(command, *argv_out != argv); - 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); + *command_info_out = build_command_info(command); if (*command_info_out == NULL) { sudo_log(SUDO_CONV_ERROR_MSG, "out of memory\n"); return ERROR; @@ -512,8 +486,7 @@ struct policy_plugin sample_policy = { policy_check, policy_list, NULL, /* validate */ - NULL, /* invalidate */ - policy_edit + NULL /* invalidate */ }; /* diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c index 1746a41eb..6c4b27f48 100644 --- a/plugins/sudoers/sudoers.c +++ b/plugins/sudoers/sudoers.c @@ -565,6 +565,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], if (!editor) goto done; command_info[info_len++] = fmt_string("command", editor); + command_info[info_len++] = estrdup("sudoedit=true"); } else { 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); } -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 sudoers_policy_validate(void) { @@ -1226,6 +1217,11 @@ deserialize_info(char * const settings[], char * const user_info[]) SET(flags, MODE_IGNORE_TICKET); continue; } + if (MATCHES(*cur, "sudoedit=")) { + if (atobool(*cur + sizeof("sudoedit=") - 1) == TRUE) + SET(flags, MODE_EDIT); + continue; + } if (MATCHES(*cur, "login_class=")) { login_class = *cur + sizeof("login_class=") - 1; def_use_loginclass = TRUE; @@ -1409,8 +1405,7 @@ struct policy_plugin sudoers_policy = { sudoers_policy_check, sudoers_policy_list, sudoers_policy_validate, - sudoers_policy_invalidate, - sudoers_policy_sudoedit + sudoers_policy_invalidate }; struct io_plugin sudoers_io = { diff --git a/src/parse_args.c b/src/parse_args.c index 584f71b2d..59e1595e2 100644 --- a/src/parse_args.c +++ b/src/parse_args.c @@ -107,7 +107,9 @@ static struct sudo_settings { { "preserve_groups" }, #define ARG_NONINTERACTIVE 15 { "noninteractive" }, -#define NUM_SETTINGS 16 +#define ARG_SUDOEDIT 16 + { "sudoedit" }, +#define NUM_SETTINGS 17 { NULL } }; @@ -135,8 +137,10 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp, sudo_settings[ARG_PROGNAME].value = getprogname(); /* First, check to see if we were invoked as "sudoedit". */ - if (strcmp(getprogname(), "sudoedit") == 0) + if (strcmp(getprogname(), "sudoedit") == 0) { mode = MODE_EDIT; + sudo_settings[ARG_SUDOEDIT].value = "true"; + } /* Returns true if the last option string was "--" */ #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) usage_excl(1); mode = MODE_EDIT; + sudo_settings[ARG_SUDOEDIT].value = "true"; valid_flags = MODE_NONINTERACTIVE; break; case 'g': diff --git a/src/sudo.c b/src/sudo.c index 7da8f798c..33df03b7f 100644 --- a/src/sudo.c +++ b/src/sudo.c @@ -199,21 +199,9 @@ main(int argc, char *argv[], char *envp[]) } exit(ok != TRUE); 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: - if (sudo_mode & MODE_EDIT) { - /* XXX - must be able to tell which are the files in argv */ - /* 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); - } + 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); if (ok != TRUE) { if (ok == -2) @@ -240,8 +228,6 @@ main(int argc, char *argv[], char *envp[]) } } 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. */ #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL) (void) setrlimit(RLIMIT_CORE, &corelimit); diff --git a/src/sudo_edit.c b/src/sudo_edit.c index 3cf8fa406..031a29838 100644 --- a/src/sudo_edit.c +++ b/src/sudo_edit.c @@ -143,8 +143,10 @@ sudo_edit(struct command_details *command_details, char *argv[], char *envp[]) else editor_argc++; } - if (nfiles == 0) + if (nfiles == 0) { + warningx("plugin error: missing file list for sudoedit"); return 1; + } /* * For each file specified by the user, make a temporary version -- 2.40.0