From 0865e61d9edd9e1b91e0b0c044fce4064b0fb3b8 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Wed, 18 Jan 2023 13:38:15 -0700 Subject: [PATCH] Pass back the number of files to edit when using sudoedit. The sudo front-end can use this to determine where the list of files to edit begins. --- docs/sudo_plugin.man.in | 53 +++++++++++++++++++++++++++++++-------- docs/sudo_plugin.mdoc.in | 51 +++++++++++++++++++++++++++++-------- include/sudo_plugin.h | 4 +-- plugins/sudoers/policy.c | 9 ++++++- plugins/sudoers/sudoers.c | 3 ++- plugins/sudoers/sudoers.h | 1 + src/sudo.c | 11 +++++++- src/sudo.h | 2 ++ src/sudo_edit.c | 43 +++++++++++++++++++++---------- 9 files changed, 139 insertions(+), 38 deletions(-) diff --git a/docs/sudo_plugin.man.in b/docs/sudo_plugin.man.in index d4e003628..bd30403e7 100644 --- a/docs/sudo_plugin.man.in +++ b/docs/sudo_plugin.man.in @@ -2,7 +2,7 @@ .\" .\" SPDX-License-Identifier: ISC .\" -.\" Copyright (c) 2009-2022 Todd C. Miller +.\" Copyright (c) 2009-2023 Todd C. Miller .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -16,7 +16,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.TH "SUDO_PLUGIN" "5" "October 7, 2022" "Sudo @PACKAGE_VERSION@" "File Formats Manual" +.TH "SUDO_PLUGIN" "5" "January 18, 2023" "Sudo @PACKAGE_VERSION@" "File Formats Manual" .nh .if n .ad l .SH "NAME" @@ -909,15 +909,20 @@ 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 \fIsudoedit\fR, -it should choose the editor to be used, potentially from a variable -in the user's environment, such as +it must set +\fIsudoedit=true\fR +in the +\fIcommand_info\fR +list. +The plugin is responsible for choosing the editor to be used, +potentially from a variable in the user's environment, such as \fREDITOR\fR, -and include it in +and should be stored in \fIargv_out\fR (environment variables may include command line options). The files to be edited should be copied from \fIargv\fR -into +to \fIargv_out\fR, separated from the editor and its arguments by a @@ -928,11 +933,13 @@ The will be removed by \fBsudo\fR before the editor is executed. -The plugin should also set -\fIsudoedit=true\fR -in the +The plugin may also set +\fIsudoedit_nfiles\fR +to the number of files to be edited in the \fIcommand_info\fR -list. +list; this will only be used by the +\fBsudo\fR +front-end starting with API version 1.21. .sp The \fBcheck_policy\fR() @@ -1536,6 +1543,25 @@ option can be used to restore the older behavior and allow to open symbolic links. Only available starting with API version 1.8. .TP 6n +sudoedit_nfiles=number +The number of files to be edited by the user. +If present, this is will be used by the +\fBsudo\fR +front-end to determine which elements of the +\fIargv_out\fR +vector are files to be edited. +The +\(oq--\(cq +element must immediately precede the first file to be editied. +If +\fIsudoedit_nfiles\fR +is not specified, the +\fBsudo\fR +front-end will use the position of the +\(oq--\(cq +element to determine where the file list begins. +Only available starting with API version 1.21. +.TP 6n timeout=int Command timeout. If non-zero then when the timeout expires the command will be killed. @@ -5441,6 +5467,13 @@ The entry was added to the \fIcommand_info\fR list. +.TP 6n +Version 1.21 (sudo 1.9.13) +The +\fIsudoedit_nfiles\fR +entry was added to the +\fIcommand_info\fR +list. .SH "SEE ALSO" sudo.conf(@mansectform@), sudoers(@mansectform@), diff --git a/docs/sudo_plugin.mdoc.in b/docs/sudo_plugin.mdoc.in index bca8fcbae..b6d3b9352 100644 --- a/docs/sudo_plugin.mdoc.in +++ b/docs/sudo_plugin.mdoc.in @@ -1,7 +1,7 @@ .\" .\" SPDX-License-Identifier: ISC .\" -.\" Copyright (c) 2009-2022 Todd C. Miller +.\" Copyright (c) 2009-2023 Todd C. Miller .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd October 7, 2022 +.Dd January 18, 2023 .Dt SUDO_PLUGIN @mansectform@ .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -807,15 +807,20 @@ 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 .Em sudoedit , -it should choose the editor to be used, potentially from a variable -in the user's environment, such as +it must set +.Em sudoedit=true +in the +.Fa command_info +list. +The plugin is responsible for choosing the editor to be used, +potentially from a variable in the user's environment, such as .Ev EDITOR , -and include it in +and should be stored in .Fa argv_out (environment variables may include command line options). The files to be edited should be copied from .Fa argv -into +to .Fa argv_out , separated from the editor and its arguments by a @@ -826,11 +831,13 @@ The will be removed by .Nm sudo before the editor is executed. -The plugin should also set -.Em sudoedit=true -in the +The plugin may also set +.Em sudoedit_nfiles +to the number of files to be edited in the .Fa command_info -list. +list; this will only be used by the +.Nm sudo +front-end starting with API version 1.21. .Pp The .Fn check_policy @@ -1377,6 +1384,24 @@ option can be used to restore the older behavior and allow .Nm sudoedit to open symbolic links. Only available starting with API version 1.8. +.It sudoedit_nfiles=number +The number of files to be edited by the user. +If present, this is will be used by the +.Nm sudo +front-end to determine which elements of the +.Fa argv_out +vector are files to be edited. +The +.Ql -- +element must immediately precede the first file to be editied. +If +.Em sudoedit_nfiles +is not specified, the +.Nm sudo +front-end will use the position of the +.Ql -- +element to determine where the file list begins. +Only available starting with API version 1.21. .It timeout=int Command timeout. If non-zero then when the timeout expires the command will be killed. @@ -4826,6 +4851,12 @@ The entry was added to the .Fa command_info list. +.It Version 1.21 (sudo 1.9.13) +The +.Em sudoedit_nfiles +entry was added to the +.Fa command_info +list. .El .Sh SEE ALSO .Xr sudo.conf @mansectform@ , diff --git a/include/sudo_plugin.h b/include/sudo_plugin.h index 261763833..e363637a2 100644 --- a/include/sudo_plugin.h +++ b/include/sudo_plugin.h @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2009-2022 Todd C. Miller + * Copyright (c) 2009-2023 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,7 +21,7 @@ /* API version major/minor */ #define SUDO_API_VERSION_MAJOR 1 -#define SUDO_API_VERSION_MINOR 20 +#define SUDO_API_VERSION_MINOR 21 #define SUDO_API_MKVERSION(x, y) (((x) << 16) | (y)) #define SUDO_API_VERSION SUDO_API_MKVERSION(SUDO_API_VERSION_MAJOR, SUDO_API_VERSION_MINOR) diff --git a/plugins/sudoers/policy.c b/plugins/sudoers/policy.c index 2a6a76e0a..403ee523d 100644 --- a/plugins/sudoers/policy.c +++ b/plugins/sudoers/policy.c @@ -58,6 +58,7 @@ struct sudo_plugin_event * (*plugin_event_alloc)(void); const char *path_ldap_conf = _PATH_LDAP_CONF; const char *path_ldap_secret = _PATH_LDAP_SECRET; static bool session_opened; +int sudoedit_nfiles; extern sudo_dso_public struct policy_plugin sudoers_policy; @@ -189,6 +190,7 @@ sudoers_policy_deserialize_info(void *v, struct defaults_list *defaults) /* Parse command line settings. */ sudo_user.flags = 0; user_closefrom = -1; + sudoedit_nfiles = 0; sudo_mode = 0; for (cur = info->settings; *cur != NULL; cur++) { if (MATCHES(*cur, "closefrom=")) { @@ -653,7 +655,7 @@ sudoers_policy_store_result(bool accepted, char *argv[], char *envp[], } /* Increase the length of command_info as needed, it is *not* checked. */ - command_info = calloc(72, sizeof(char *)); + command_info = calloc(73, sizeof(char *)); if (command_info == NULL) goto oom; @@ -715,6 +717,11 @@ sudoers_policy_store_result(bool accepted, char *argv[], char *envp[], if (ISSET(sudo_mode, MODE_EDIT)) { if ((command_info[info_len++] = strdup("sudoedit=true")) == NULL) goto oom; + if (sudoedit_nfiles > 0) { + if (asprintf(&command_info[info_len++], "sudoedit_nfiles=%d", + sudoedit_nfiles) == -1) + goto oom; + } if (!def_sudoedit_checkdir) { if ((command_info[info_len++] = strdup("sudoedit_checkdir=false")) == NULL) goto oom; diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c index 1f22853ff..9ae5f96c6 100644 --- a/plugins/sudoers/sudoers.c +++ b/plugins/sudoers/sudoers.c @@ -806,8 +806,9 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], char **edit_argv; int edit_argc; + sudoedit_nfiles = NewArgc - 1; free(safe_cmnd); - safe_cmnd = find_editor(NewArgc - 1, NewArgv + 1, &edit_argc, + safe_cmnd = find_editor(sudoedit_nfiles, NewArgv + 1, &edit_argc, &edit_argv, NULL, &env_editor); if (safe_cmnd == NULL) { switch (errno) { diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index 814a1318c..df60627c1 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -433,6 +433,7 @@ extern struct sudo_user sudo_user; extern struct passwd *list_pw; extern bool force_umask; extern int sudo_mode; +extern int sudoedit_nfiles; extern uid_t timestamp_uid; extern gid_t timestamp_gid; extern sudo_conv_t sudo_conv; diff --git a/src/sudo.c b/src/sudo.c index 2405cb0f3..a8a18bbb5 100644 --- a/src/sudo.c +++ b/src/sudo.c @@ -287,7 +287,8 @@ main(int argc, char *argv[], char *envp[]) /* Setup command details and run command/edit. */ command_info_to_details(command_info, &command_details); command_details.tty = user_details.tty; - command_details.argv = argv_out; + command_details.argv = nargv; + command_details.argc = nargc; command_details.envp = run_envp; command_details.evbase = sudo_event_base; if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) @@ -833,6 +834,14 @@ command_info_to_details(char * const info[], struct command_details *details) SET_FLAG("sudoedit=", CD_SUDOEDIT) SET_FLAG("sudoedit_checkdir=", CD_SUDOEDIT_CHECKDIR) SET_FLAG("sudoedit_follow=", CD_SUDOEDIT_FOLLOW) + if (strncmp("sudoedit_nfiles=", info[i], sizeof("sudoedit_nfiles=") - 1) == 0) { + cp = info[i] + sizeof("sudoedit_nfiles=") - 1; + details->nfiles = sudo_strtonum(cp, 1, INT_MAX, + &errstr); + if (errstr != NULL) + sudo_fatalx(U_("%s: %s"), info[i], U_(errstr)); + break; + } break; case 't': if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) { diff --git a/src/sudo.h b/src/sudo.h index bbf874fc8..795662adf 100644 --- a/src/sudo.h +++ b/src/sudo.h @@ -189,11 +189,13 @@ TAILQ_HEAD(preserved_fd_list, preserved_fd); struct command_details { struct sudo_cred cred; mode_t umask; + int argc; int priority; int timeout; int closefrom; int flags; int execfd; + int nfiles; struct preserved_fd_list preserved_fds; struct passwd *pw; const char *command; diff --git a/src/sudo_edit.c b/src/sudo_edit.c index c62644e07..21ac97f2e 100644 --- a/src/sudo_edit.c +++ b/src/sudo_edit.c @@ -628,9 +628,10 @@ int sudo_edit(struct command_details *command_details) { struct command_details saved_command_details; - char **nargv = NULL, **ap, **files = NULL; + char **nargv = NULL, **files = NULL; + int nfiles = command_details->nfiles; int errors, i, ac, nargc, ret; - int editor_argc = 0, nfiles = 0; + int editor_argc = 0; struct timespec times[2]; struct tempfile *tf = NULL; debug_decl(sudo_edit, SUDO_DEBUG_EDIT); @@ -650,17 +651,31 @@ sudo_edit(struct command_details *command_details) if (!set_tmpdir(&user_details.cred)) goto cleanup; - /* - * The user's editor must be separated from the files to be - * edited by a "--" option. - */ - for (ap = command_details->argv; *ap != NULL; ap++) { - if (files) - nfiles++; - else if (strcmp(*ap, "--") == 0) - files = ap + 1; - else - editor_argc++; + if (nfiles > 0) { + /* + * The plugin specified the number of files to edit, use it. + */ + editor_argc = command_details->argc - nfiles; + if (editor_argc < 2 || strcmp(command_details->argv[editor_argc - 1], "--") != 0) { + sudo_warnx("%s", U_("plugin error: invalid file list for sudoedit")); + goto cleanup; + } + + /* We don't include the "--" when running the user's editor. */ + files = &command_details->argv[editor_argc--]; + } else { + /* + * Compute the number of files to edit by looking for the "--" + * option which separate the editor from the files. + */ + for (i = 0; command_details->argv[i] != NULL; i++) { + if (strcmp(command_details->argv[i], "--") == 0) { + editor_argc = i; + files = &command_details->argv[i + 1]; + nfiles = command_details->argc - (i + 1); + break; + } + } } if (nfiles == 0) { sudo_warnx("%s", U_("plugin error: missing file list for sudoedit")); @@ -717,6 +732,7 @@ sudo_edit(struct command_details *command_details) command_details->cred = user_details.cred; command_details->cred.euid = user_details.cred.uid; command_details->cred.egid = user_details.cred.gid; + command_details->argc = nargc; command_details->argv = nargv; ret = run_command(command_details); if (sudo_gettime_real(×[1]) == -1) { @@ -726,6 +742,7 @@ sudo_edit(struct command_details *command_details) /* Restore saved command_details. */ command_details->cred = saved_command_details.cred; + command_details->argc = saved_command_details.argc; command_details->argv = saved_command_details.argv; /* Copy contents of temp files to real ones. */