#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID)
+/*
+ * Editor temporary file name along with original name, mtime and size.
+ */
+struct tempfile {
+ char *tfile;
+ char *ofile;
+ struct timeval omtim;
+ off_t osize;
+};
+
+static char edit_tmpdir[MAX(sizeof(_PATH_VARTMP), sizeof(_PATH_TMP))];
+
+/*
+ * Find our temporary directory, one of /var/tmp, /usr/tmp, or /tmp
+ * Returns true on success, else false;
+ */
+static bool
+set_tmpdir(void)
+{
+ const char *tdir;
+ struct stat sb;
+ size_t len;
+ debug_decl(set_tmpdir, SUDO_DEBUG_EDIT)
+
+ if (stat(_PATH_VARTMP, &sb) == 0 && S_ISDIR(sb.st_mode)) {
+ tdir = _PATH_VARTMP;
+ }
+#ifdef _PATH_USRTMP
+ else if (stat(_PATH_USRTMP, &sb) == 0 && S_ISDIR(sb.st_mode)) {
+ tdir = _PATH_USRTMP;
+ }
+#endif
+ else {
+ tdir = _PATH_TMP;
+ }
+ len = strlcpy(edit_tmpdir, tdir, sizeof(edit_tmpdir));
+ if (len >= sizeof(edit_tmpdir)) {
+ errno = ENAMETOOLONG;
+ sudo_warn("%s", tdir);
+ debug_return_bool(false);
+ }
+ while (len > 0 && edit_tmpdir[--len] == '/')
+ edit_tmpdir[len] = '\0';
+ debug_return_bool(true);
+}
+
static void
switch_user(uid_t euid, gid_t egid, int ngroups, GETGROUPS_T *groups)
{
}
/*
- * Wrapper to allow users to edit privileged files with their own uid.
+ * Construct a temporary file name for file and return an
+ * open file descriptor. The temporary file name is stored
+ * in tfile which the caller is responsible for freeing.
*/
-int
-sudo_edit(struct command_details *command_details)
+static int
+sudo_edit_mktemp(const char *ofile, char **tfile)
{
- struct command_details saved_command_details;
- ssize_t nread, nwritten;
- const char *tmpdir;
- char *cp, *suff, **nargv = NULL, **ap, **files = NULL;
- char buf[BUFSIZ];
- int rc, i, j, ac, ofd, tfd, nargc, rval, tmplen;
- int editor_argc = 0, nfiles = 0;
- struct stat sb;
- struct timeval tv, times[2];
- struct tempfile {
- char *tfile;
- char *ofile;
- struct timeval omtim;
- off_t osize;
- } *tf = NULL;
- debug_decl(sudo_edit, SUDO_DEBUG_EDIT)
-
- /*
- * Set real, effective and saved uids to root.
- * We will change the euid as needed below.
- */
- if (setuid(ROOT_UID) != 0) {
- sudo_warn(U_("unable to change uid to root (%u)"), ROOT_UID);
- goto cleanup;
- }
+ const char *cp, *suff;
+ debug_decl(sudo_edit_mktemp, SUDO_DEBUG_EDIT)
- /*
- * Find our temporary directory, one of /var/tmp, /usr/tmp, or /tmp
- */
- if (stat(_PATH_VARTMP, &sb) == 0 && S_ISDIR(sb.st_mode))
- tmpdir = _PATH_VARTMP;
-#ifdef _PATH_USRTMP
- else if (stat(_PATH_USRTMP, &sb) == 0 && S_ISDIR(sb.st_mode))
- tmpdir = _PATH_USRTMP;
-#endif
+ if ((cp = strrchr(ofile, '/')) != NULL)
+ cp++;
else
- tmpdir = _PATH_TMP;
- tmplen = strlen(tmpdir);
- while (tmplen > 0 && tmpdir[tmplen - 1] == '/')
- tmplen--;
-
- /*
- * 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) {
- sudo_warnx(U_("plugin error: missing file list for sudoedit"));
- goto cleanup;
+ cp = ofile;
+ suff = strrchr(cp, '.');
+ if (suff != NULL) {
+ sudo_easprintf(tfile, "%s/%.*sXXXXXXXX%s", edit_tmpdir,
+ (int)(size_t)(suff - cp), cp, suff);
+ } else {
+ sudo_easprintf(tfile, "%s/%s.XXXXXXXX", edit_tmpdir, cp);
}
+ debug_return_int(mkstemps(*tfile, suff ? strlen(suff) : 0));
+}
+
+/*
+ * Create temporary copies of files[] and store the temporary path name
+ * along with the original name, size and mtime in tf.
+ * Returns the number of files copied (which may be less than nfiles)
+ * or -1 if a fatal error occurred.
+ */
+static int
+sudo_edit_create_tfiles(struct command_details *command_details,
+ struct tempfile *tf, char * const files[], int nfiles)
+{
+ int i, j, tfd, ofd, rc;
+ char buf[BUFSIZ];
+ ssize_t nwritten, nread;
+ struct timeval times[2];
+ struct stat sb;
+ debug_decl(sudo_edit_create_tfiles, SUDO_DEBUG_EDIT)
/*
* For each file specified by the user, make a temporary version
* and copy the contents of the original to it.
*/
- tf = sudo_emallocarray(nfiles, sizeof(*tf));
- memset(tf, 0, nfiles * sizeof(*tf));
for (i = 0, j = 0; i < nfiles; i++) {
rc = -1;
switch_user(command_details->euid, command_details->egid,
tf[j].ofile = files[i];
tf[j].osize = sb.st_size;
mtim_get(&sb, &tf[j].omtim);
- if ((cp = strrchr(tf[j].ofile, '/')) != NULL)
- cp++;
- else
- cp = tf[j].ofile;
- suff = strrchr(cp, '.');
- if (suff != NULL) {
- sudo_easprintf(&tf[j].tfile, "%.*s/%.*sXXXXXXXX%s", tmplen, tmpdir,
- (int)(size_t)(suff - cp), cp, suff);
- } else {
- sudo_easprintf(&tf[j].tfile, "%.*s/%s.XXXXXXXX", tmplen, tmpdir, cp);
- }
if (seteuid(user_details.uid) != 0)
sudo_fatal("seteuid(%d)", (int)user_details.uid);
- tfd = mkstemps(tf[j].tfile, suff ? strlen(suff) : 0);
+ tfd = sudo_edit_mktemp(tf[j].ofile, &tf[j].tfile);
if (seteuid(ROOT_UID) != 0)
sudo_fatal("seteuid(ROOT_UID)");
if (tfd == -1) {
sudo_warn("mkstemps");
- goto cleanup;
+ debug_return_int(-1);
}
if (ofd != -1) {
while ((nread = read(ofd, buf, sizeof(buf))) != 0) {
sudo_warn("%s", tf[j].tfile);
else
sudo_warnx(U_("%s: short write"), tf[j].tfile);
- goto cleanup;
+ debug_return_int(-1);
}
}
close(ofd);
close(tfd);
j++;
}
- if ((nfiles = j) == 0)
- goto cleanup; /* no files readable, you lose */
-
- /*
- * Allocate space for the new argument vector and fill it in.
- * We concatenate the editor with its args and the file list
- * to create a new argv.
- */
- nargc = editor_argc + nfiles;
- nargv = sudo_emallocarray(nargc + 1, sizeof(char *));
- for (ac = 0; ac < editor_argc; ac++)
- nargv[ac] = command_details->argv[ac];
- for (i = 0; i < nfiles && ac < nargc; )
- nargv[ac++] = tf[i++].tfile;
- nargv[ac] = NULL;
-
- /*
- * Run the editor with the invoking user's creds,
- * keeping track of the time spent in the editor.
- */
- gettimeofday(×[0], NULL);
- memcpy(&saved_command_details, command_details, sizeof(struct command_details));
- command_details->uid = user_details.uid;
- command_details->euid = user_details.uid;
- command_details->gid = user_details.gid;
- command_details->egid = user_details.gid;
- command_details->ngroups = user_details.ngroups;
- command_details->groups = user_details.groups;
- command_details->argv = nargv;
- rval = run_command(command_details);
- gettimeofday(×[1], NULL);
+ debug_return_int(j);
+}
- /* Restore saved command_details. */
- command_details->uid = saved_command_details.uid;
- command_details->euid = saved_command_details.uid;
- command_details->gid = saved_command_details.gid;
- command_details->egid = saved_command_details.gid;
- command_details->ngroups = saved_command_details.ngroups;
- command_details->groups = saved_command_details.groups;
- command_details->argv = saved_command_details.argv;
+/*
+ * Copy the temporary files specified in tf to the originals.
+ * Returns the number of copy errors or 0 if completely successful.
+ */
+static int
+sudo_edit_copy_tfiles(struct command_details *command_details,
+ struct tempfile *tf, int nfiles, struct timeval *times)
+{
+ int i, tfd, ofd, rc, errors = 0;
+ char buf[BUFSIZ];
+ ssize_t nwritten, nread;
+ struct timeval tv;
+ struct stat sb;
+ debug_decl(sudo_edit_copy_tfiles, SUDO_DEBUG_EDIT)
/* Copy contents of temp files to real ones. */
for (i = 0; i < nfiles; i++) {
sudo_warnx(U_("%s left unmodified"), tf[i].ofile);
if (tfd != -1)
close(tfd);
+ errors++;
continue;
}
mtim_get(&sb, &tv);
sudo_warnx(U_("%s unchanged"), tf[i].ofile);
unlink(tf[i].tfile);
close(tfd);
+ errors++;
continue;
}
}
sudo_warn(U_("unable to write to %s"), tf[i].ofile);
sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
close(tfd);
+ errors++;
continue;
}
while ((nread = read(tfd, buf, sizeof(buf))) > 0) {
}
close(ofd);
}
+ debug_return_int(errors);
+}
+
+/*
+ * Wrapper to allow users to edit privileged files with their own uid.
+ */
+int
+sudo_edit(struct command_details *command_details)
+{
+ struct command_details saved_command_details;
+ char **nargv = NULL, **ap, **files = NULL;
+ int i, ac, nargc, rval;
+ int editor_argc = 0, nfiles = 0;
+ struct timeval times[2];
+ struct tempfile *tf = NULL;
+ debug_decl(sudo_edit, SUDO_DEBUG_EDIT)
+
+ if (!set_tmpdir())
+ goto cleanup;
+
+ /*
+ * Set real, effective and saved uids to root.
+ * We will change the euid as needed below.
+ */
+ if (setuid(ROOT_UID) != 0) {
+ sudo_warn(U_("unable to change uid to root (%u)"), ROOT_UID);
+ 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) {
+ sudo_warnx(U_("plugin error: missing file list for sudoedit"));
+ goto cleanup;
+ }
+
+ /* Copy editor files to temporaries. */
+ tf = sudo_ecalloc(nfiles, sizeof(*tf));
+ nfiles = sudo_edit_create_tfiles(command_details, tf, files, nfiles);
+ if (nfiles <= 0)
+ goto cleanup;
+
+ /*
+ * Allocate space for the new argument vector and fill it in.
+ * We concatenate the editor with its args and the file list
+ * to create a new argv.
+ */
+ nargc = editor_argc + nfiles;
+ nargv = sudo_emallocarray(nargc + 1, sizeof(char *));
+ for (ac = 0; ac < editor_argc; ac++)
+ nargv[ac] = command_details->argv[ac];
+ for (i = 0; i < nfiles && ac < nargc; )
+ nargv[ac++] = tf[i++].tfile;
+ nargv[ac] = NULL;
+
+ /*
+ * Run the editor with the invoking user's creds,
+ * keeping track of the time spent in the editor.
+ */
+ gettimeofday(×[0], NULL);
+ memcpy(&saved_command_details, command_details, sizeof(struct command_details));
+ command_details->uid = user_details.uid;
+ command_details->euid = user_details.uid;
+ command_details->gid = user_details.gid;
+ command_details->egid = user_details.gid;
+ command_details->ngroups = user_details.ngroups;
+ command_details->groups = user_details.groups;
+ command_details->argv = nargv;
+ rval = run_command(command_details);
+ gettimeofday(×[1], NULL);
+
+ /* Restore saved command_details. */
+ command_details->uid = saved_command_details.uid;
+ command_details->euid = saved_command_details.uid;
+ command_details->gid = saved_command_details.gid;
+ command_details->egid = saved_command_details.gid;
+ command_details->ngroups = saved_command_details.ngroups;
+ command_details->groups = saved_command_details.groups;
+ command_details->argv = saved_command_details.argv;
+
+ /* Copy contents of temp files to real ones. */
+ if (sudo_edit_copy_tfiles(command_details, tf, nfiles, times) != 0)
+ rval = 1;
+
sudo_efree(tf);
sudo_efree(nargv);
debug_return_int(rval);