]> granicus.if.org Git - sudo/commitdiff
Refactor code that copies temp files into separate functions.
authorTodd C. Miller <Todd.Miller@courtesan.com>
Thu, 21 Aug 2014 21:28:35 +0000 (15:28 -0600)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Thu, 21 Aug 2014 21:28:35 +0000 (15:28 -0600)
src/sudo_edit.c

index d46d16ae71d9c31386db9878eb1777bdcb1eeba0..5d4d59328561a09d33d844205fd6b1cbf3ae27a5 100644 (file)
 
 #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)
 {
@@ -80,75 +126,51 @@ 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,
@@ -175,25 +197,14 @@ sudo_edit(struct command_details *command_details)
        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) {
@@ -202,7 +213,7 @@ sudo_edit(struct command_details *command_details)
                        sudo_warn("%s", tf[j].tfile);
                    else
                        sudo_warnx(U_("%s: short write"), tf[j].tfile);
-                   goto cleanup;
+                   debug_return_int(-1);
                }
            }
            close(ofd);
@@ -227,46 +238,23 @@ sudo_edit(struct command_details *command_details)
        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(&times[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(&times[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++) {
@@ -286,6 +274,7 @@ sudo_edit(struct command_details *command_details)
            sudo_warnx(U_("%s left unmodified"), tf[i].ofile);
            if (tfd != -1)
                close(tfd);
+           errors++;
            continue;
        }
        mtim_get(&sb, &tv);
@@ -298,6 +287,7 @@ sudo_edit(struct command_details *command_details)
                sudo_warnx(U_("%s unchanged"), tf[i].ofile);
                unlink(tf[i].tfile);
                close(tfd);
+               errors++;
                continue;
            }
        }
@@ -310,6 +300,7 @@ sudo_edit(struct command_details *command_details)
            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) {
@@ -333,6 +324,100 @@ sudo_edit(struct command_details *command_details)
        }
        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(&times[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(&times[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);