Blob Blame History Raw
diff -up sudo-1.8.6p3/src/sesh.c.sudoedit-selinux sudo-1.8.6p3/src/sesh.c
--- sudo-1.8.6p3/src/sesh.c.sudoedit-selinux	2012-09-18 15:56:30.000000000 +0200
+++ sudo-1.8.6p3/src/sesh.c	2012-09-25 16:06:33.408584649 +0200
@@ -34,6 +34,10 @@
 # include "compat/stdbool.h"
 #endif /* HAVE_STDBOOL_H */
 
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
 #include "missing.h"
 #include "alloc.h"
 #include "error.h"
@@ -43,6 +47,16 @@
 #include "sudo_exec.h"
 #include "sudo_plugin.h"
 
+/*
+ * Return codes:
+ * EXIT_FAILURE ... unspecified error
+ *    0 ... everything ok
+ *   30 ... invalid -e arg value
+ *   31 ... odd number of paths
+ *   32 ... copy operation failed, no files copied
+ *   33 ... copy operation failed, some files copied
+ */
+
 sudo_conv_t sudo_conv;  /* NULL in non-plugin */
 
 /*
@@ -77,19 +91,134 @@ main(int argc, char *argv[], char *envp[
     if ((cp = strrchr(argv[0], '-')) != NULL && cp != argv[0])
 	noexec = strcmp(cp, "-noexec") == 0;
 
-    /* Shift argv and make a copy of the command to execute. */
-    argv++;
-    argc--;
-    cmnd = estrdup(argv[0]);
-
-    /* If invoked as a login shell, modify argv[0] accordingly. */
-    if (argv[-1][0] == '-') {
-	if ((cp = strrchr(argv[0], '/')) == NULL)
-	    cp = argv[0];
-	*cp = '-';
+    /* check the first argument, if it's `-e' then we are in sudoedit mode */
+    if (strncmp(argv[1], "-e", 3) == 0) {
+        int fd_src, fd_dst, post, n, ret = -1;
+        ssize_t nread, nwritten;
+        char *path_src, *path_dst, buf[BUFSIZ];
+
+        if (argc < 3)
+            return EXIT_FAILURE;
+
+        /*
+         * We need to know whether we are performing the copy operation
+         * before or after the editing. Without this we would not know
+         * which files are temporary and which are the originals.
+         *  post = 0 ... before
+         *  post = 1 ... after
+         */
+        if (strncmp(argv[2], "0", 2) == 0)
+            post = 0;
+        else if (strncmp(argv[2], "1", 2) == 0)
+            post = 1;
+        else /* invalid value */
+            return 30;
+
+        /* align argv & argc to the beggining of the file list */
+        argv += 3;
+        argc -= 3;
+
+        /* no files specified, nothing to do */
+        if (argc == 0)
+            return 0;
+        /* odd number of paths specified */
+        if (argc % 2 == 1)
+            return 31;
+
+        for (n = 0; n < argc - 1; n += 2) {
+            path_src = argv[n];
+            path_dst = argv[n+1];
+            /*
+            * Try to open the source file for reading. If it
+            * doesn't exist, it's ok, we'll create an empty
+            * destination file.
+            */
+            if ((fd_src = open(path_src, O_RDONLY, 0600)) < 0) {
+                if (errno == ENOENT) {
+                    /* new file */
+                } else {
+                    warning(_("open(%s)"), path_src);
+                    if (post) {
+                        ret = 33;
+                        goto nocleanup;
+                    } else
+                        goto cleanup_0;
+                }
+            }
+
+            /*
+            * Use O_EXCL if we are not in the post editing stage
+            * so that it's ensured that the temporary files are
+            * created by us and that we are not opening any sym-
+            * links.
+            */
+            if ((fd_dst = open(path_dst, (post ? 0 : O_EXCL) |
+                               O_WRONLY|O_TRUNC|O_CREAT, post ? 0644 : 0600)) < 0)
+            {
+                /* error - cleanup */
+                warning(_("open(%s%s)"), path_dst, post ? "" : ", O_EXCL");
+                if (post) {
+                    ret = 33;
+                    goto nocleanup;
+                } else
+                    goto cleanup_0;
+            }
+
+            if (fd_src != -1) {
+                while ((nread = read(fd_src, buf, sizeof(buf))) > 0) {
+                    if ((nwritten = write(fd_dst, buf, nread)) != nread) {
+                        warning(_("write"));
+                        if (post) {
+                            ret = 33;
+                            goto nocleanup;
+                        } else
+                            goto cleanup_0;
+                    }
+                }
+            }
+
+            if (fd_dst != -1)
+                close(fd_dst);
+            if (fd_src != -1)
+                close(fd_src);
+            fd_dst = fd_src = -1;
+        }
+
+        ret = 0;
+        /* remove temporary files (post=1) */
+        for (n = 0; n < argc - 1; n += 2)
+            unlink(argv[n]);
+nocleanup:
+        if (fd_dst != -1)
+            close(fd_dst);
+        if (fd_src != -1)
+            close(fd_src);
+        _exit(ret);
+cleanup_0:
+        /* remove temporary files (post=0) */
+        for (n = 0; n < argc - 1; n += 2)
+            unlink(argv[n+1]);
+        if (fd_dst != -1)
+            close(fd_dst);
+        if (fd_src != -1)
+            close(fd_src);
+        _exit(32);
+    } else {
+
+        /* Shift argv and make a copy of the command to execute. */
+        argv++;
+        argc--;
+        cmnd = estrdup(argv[0]);
+
+        /* If invoked as a login shell, modify argv[0] accordingly. */
+        if (argv[-1][0] == '-') {
+            if ((cp = strrchr(argv[0], '/')) == NULL)
+                cp = argv[0];
+            *cp = '-';
+        }
+        sudo_execve(cmnd, argv, envp, noexec);
+        warning(_("unable to execute %s"), argv[0]);
+        sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, EXIT_FAILURE);
     }
-    sudo_execve(cmnd, argv, envp, noexec);
-    warning(_("unable to execute %s"), argv[0]);
-    sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, EXIT_FAILURE);                
     _exit(EXIT_FAILURE);
 }
diff -up sudo-1.8.6p3/src/sudo.c.sudoedit-selinux sudo-1.8.6p3/src/sudo.c
--- sudo-1.8.6p3/src/sudo.c.sudoedit-selinux	2012-09-18 15:57:43.000000000 +0200
+++ sudo-1.8.6p3/src/sudo.c	2012-09-25 16:04:36.687422997 +0200
@@ -915,6 +915,10 @@ exec_setup(struct command_details *detai
 	if (selinux_setup(details->selinux_role, details->selinux_type,
 	    ptyname ? ptyname : user_details.tty, ptyfd) == -1)
 	    goto done;
+	if (details->flags & CD_SUDOEDIT_COPY) {
+	    rval = true;
+	    goto done;
+	}
     }
 #endif
 
@@ -1116,6 +1120,8 @@ run_command(struct command_details *deta
 	break;
     case CMD_WSTATUS:
 	/* Command ran, exited or was killed. */
+	if (details->flags & CD_SUDOEDIT_COPY)
+	  break;
 	sudo_debug_printf(SUDO_DEBUG_DEBUG,
 	    "calling policy close with wait status %d", cstat.val);
 	policy_close(&policy_plugin, cstat.val, 0);
diff -up sudo-1.8.6p3/src/sudo_edit.c.sudoedit-selinux sudo-1.8.6p3/src/sudo_edit.c
--- sudo-1.8.6p3/src/sudo_edit.c.sudoedit-selinux	2012-09-18 15:56:30.000000000 +0200
+++ sudo-1.8.6p3/src/sudo_edit.c	2012-09-25 16:06:19.108564255 +0200
@@ -49,11 +49,284 @@
 #if TIME_WITH_SYS_TIME
 # include <time.h>
 #endif
+#ifdef HAVE_SELINUX
+# include <selinux/selinux.h>
+#endif
 
 #include "sudo.h"
 
 #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID)
 
+struct tempfile {
+    char *tfile;
+    char *ofile;
+    struct timeval omtim;
+    off_t osize;
+};
+
+static int
+selinux_edit_copy(struct command_details *command_details, struct tempfile *tf, char **files, int nfiles, const char *tmpdir, int tmplen, int tval_isset)
+{
+    char **sesh_args;
+    int i, sesh_nargs, ret;
+    struct command_details sesh_details;
+    debug_decl(selinux_edit_copy, SUDO_DEBUG_EDIT);
+    
+    /* Prepare selinux stuff (setexeccon) */
+    if (selinux_setup(command_details->selinux_role,
+		      command_details->selinux_type, NULL, -1) != 0)
+        return -1;
+
+    if (nfiles < 1)
+        return 1;
+
+    /* Construct common args for sesh */
+    memcpy(&sesh_details, command_details, sizeof(sesh_details));
+    sesh_details.command = _PATH_SUDO_SESH;
+    sesh_details.flags |= CD_SUDOEDIT_COPY;
+    
+    sesh_nargs = (nfiles * 2) + 4 + 1;
+    sesh_args  = (char **)emalloc2(sesh_nargs, sizeof(char *));
+    sesh_args++;
+    sesh_args[0] = "sesh";
+    sesh_args[1] = "-e";
+
+    if (files != NULL) {
+        sesh_args[2] = "0";
+
+        for (i = 2; i < nfiles+2; ++i) {
+            sesh_args[2*i-1] = files[i-2];
+            tf[i-2].ofile    = files[i-2];
+            /*
+            * O_CREAT | O_EXCL is used in the sesh helper, so the
+            * usage of the tempnam function here is safe.
+            */
+            sesh_args[2*i] = tempnam(tmpdir, "sudo.");
+            tf[i-2].tfile = sesh_args[2*i];
+            //tf[i-2].omtim = 0;
+            tf[i-2].osize = 0;
+        }
+
+        sesh_args[2*i-1]   = NULL;
+
+        /* Run sesh -e 0 <o1> <t1> ... <on> <tn> */
+	sesh_details.argv = sesh_args;
+	switch(run_command(&sesh_details)) {
+	  case 0:
+	    break;
+	  case 31:
+	    error(1, _("sesh: internal error: odd number of paths"));
+	  case 32:
+	    error(1, _("sesh: unable to create temporary files"));
+	}
+	
+        /* Chown to user's UID so he can edit the temporary files */
+        for (i = 2; i < nfiles+2; ++i) {
+            if (chown(tf[i-2].tfile, user_details.uid, user_details.gid) != 0) {
+                warning("Unable to chown(%s) to %d:%d for editing",
+                        tf[i-2].tfile, user_details.uid, user_details.gid);
+            }
+        }
+    } else {
+        sesh_args[2] = "1";
+
+        /* Construct args for sesh -e 1 */
+        for (i = 2; i < nfiles+2; ++i) {
+            sesh_args[2*i-1] = tf[i-2].tfile;
+            sesh_args[2*i]   = tf[i-2].ofile;
+
+            if (chown(tf[i-2].tfile, sesh_details.uid, sesh_details.gid) != 0) {
+                warning("Unable to chown(%s) back to %d:%d",
+                        tf[i-2].tfile, sesh_details.uid, sesh_details.gid);
+            }
+        }
+
+        sesh_args[2*i-1] = NULL;
+
+        /* Run sesh -e 1 <t1> <o1> ... <tn> <on> */
+	sesh_details.argv = sesh_args;
+	switch(run_command(&sesh_details)) {
+	  case 0:
+	    break;
+	  case 32:
+	    warning(_("Copying the temporary files back to its original place failed. The files were left in %s"), tmpdir);
+	    break;
+	  case 33:
+	    warning(_("Copying of some of the temporary files back to its original place failed and they were left in %s"),
+		    tmpdir);
+	    break;
+	}
+      }
+
+    return (nfiles);
+}
+
+static void switch_user(uid_t euid, gid_t egid, int ngroups, GETGROUPS_T *groups);
+
+static int sudo_edit_copy(struct command_details *command_details, struct tempfile *tf, char **files, int nfiles, const char *tmpdir, int tmplen, int tval_isset)
+{
+    int i, j, tfd, ofd, rc;
+    char *cp, *suff, buf[BUFSIZ];
+    ssize_t nwritten, nread;
+    struct stat sb;
+    struct timeval tv;
+    debug_decl(sudo_edit_copy, SUDO_DEBUG_EDIT);
+    
+    if (files != NULL) {
+        /* Create temporary copies */
+        for (i = 0, j = 0; i < nfiles; i++) {
+            rc = -1;
+            switch_user(command_details->euid, command_details->egid,
+                        command_details->ngroups, command_details->groups);
+            if ((ofd = open(files[i], O_RDONLY, 0644)) != -1 || errno == ENOENT) {
+                if (ofd == -1) {
+                    zero_bytes(&sb, sizeof(sb));		/* new file */
+                    rc = 0;
+                } else {
+                    rc = fstat(ofd, &sb);
+                }
+            }
+            switch_user(ROOT_UID, user_details.egid,
+                        user_details.ngroups, user_details.groups);
+            if (rc || (ofd != -1 && !S_ISREG(sb.st_mode))) {
+                if (rc)
+                    warning("%s", files[i]);
+                else
+                    warningx(_("%s: not a regular file"), files[i]);
+                if (ofd != -1)
+                    close(ofd);
+                continue;
+            }
+            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) {
+                easprintf(&tf[j].tfile, "%.*s/%.*sXXXXXXXX%s", tmplen, tmpdir,
+                          (int)(size_t)(suff - cp), cp, suff);
+            } else {
+                easprintf(&tf[j].tfile, "%.*s/%s.XXXXXXXX", tmplen, tmpdir, cp);
+            }
+            if (seteuid(user_details.uid) != 0)
+                error(1, "seteuid(%d)", (int)user_details.uid);
+            tfd = mkstemps(tf[j].tfile, suff ? strlen(suff) : 0);
+            if (seteuid(ROOT_UID) != 0)
+                error(1, "seteuid(ROOT_UID)");
+            if (tfd == -1) {
+                warning("mkstemps");
+                goto cleanup;
+            }
+            if (ofd != -1) {
+                while ((nread = read(ofd, buf, sizeof(buf))) != 0) {
+                    if ((nwritten = write(tfd, buf, nread)) != nread) {
+                        if (nwritten == -1)
+                            warning("%s", tf[j].tfile);
+                        else
+                            warningx(_("%s: short write"), tf[j].tfile);
+                        goto cleanup;
+                    }
+                }
+                close(ofd);
+            }
+            /*
+             * We always update the stashed mtime because the time
+             * resolution of the filesystem the temporary file is on may
+             * not match that of the filesystem where the file to be edited
+             * resides.  It is OK if touch() fails since we only use the info
+             * to determine whether or not a file has been modified.
+             */
+            (void) touch(tfd, NULL, &tf[j].omtim);
+            rc = fstat(tfd, &sb);
+            if (!rc)
+                mtim_get(&sb, &tf[j].omtim);
+            close(tfd);
+            j++;
+        }
+        if ((nfiles = j) == 0)
+            goto cleanup;		/* no files readable, you lose */
+    } else {
+        /* Copy contents of temp files to real ones */
+        for (i = 0; i < nfiles; i++) {
+            rc = -1;
+            if (seteuid(user_details.uid) != 0)
+                error(1, "seteuid(%d)", (int)user_details.uid);
+            if ((tfd = open(tf[i].tfile, O_RDONLY, 0644)) != -1) {
+                rc = fstat(tfd, &sb);
+            }
+            if (seteuid(ROOT_UID) != 0)
+                error(1, "seteuid(ROOT_UID)");
+            if (rc || !S_ISREG(sb.st_mode)) {
+                if (rc)
+                    warning("%s", tf[i].tfile);
+                else
+                    warningx(_("%s: not a regular file"), tf[i].tfile);
+                warningx(_("%s left unmodified"), tf[i].ofile);
+                if (tfd != -1)
+                    close(tfd);
+                continue;
+            }
+            mtim_get(&sb, &tv);
+            if (tf[i].osize == sb.st_size && timevalcmp(&tf[i].omtim, &tv, ==)) {
+                /*
+                 * If mtime and size match but the user spent no measurable
+                 * time in the editor we can't tell if the file was changed.
+                 */
+                if (tval_isset) {
+                    warningx(_("%s unchanged"), tf[i].ofile);
+                    unlink(tf[i].tfile);
+                    close(tfd);
+                    continue;
+                }
+            }
+            switch_user(command_details->euid, command_details->egid,
+                        command_details->ngroups, command_details->groups);
+            ofd = open(tf[i].ofile, O_WRONLY|O_TRUNC|O_CREAT, 0644);
+            switch_user(ROOT_UID, user_details.egid,
+                        user_details.ngroups, user_details.groups);
+            if (ofd == -1) {
+                warning(_("unable to write to %s"), tf[i].ofile);
+                warningx(_("contents of edit session left in %s"), tf[i].tfile);
+                close(tfd);
+                continue;
+            }
+            while ((nread = read(tfd, buf, sizeof(buf))) > 0) {
+                if ((nwritten = write(ofd, buf, nread)) != nread) {
+                    if (nwritten == -1)
+                        warning("%s", tf[i].ofile);
+                    else
+                        warningx(_("%s: short write"), tf[i].ofile);
+                    break;
+                }
+            }
+            if (nread == 0) {
+                /* success, got EOF */
+                unlink(tf[i].tfile);
+            } else if (nread < 0) {
+                warning(_("unable to read temporary file"));
+                warningx(_("contents of edit session left in %s"), tf[i].tfile);
+            } else {
+                warning(_("unable to write to %s"), tf[i].ofile);
+                warningx(_("contents of edit session left in %s"), tf[i].tfile);
+            }
+            close(ofd);
+        }
+        j = 0;
+    }
+
+    debug_return_int(j);
+cleanup:
+    for (i = 0; i < nfiles; i++) {
+        if (tf[i].tfile != NULL)
+            unlink(tf[i].tfile);
+    }
+
+    debug_return_int(-1);
+}
+
 static void
 switch_user(uid_t euid, gid_t egid, int ngroups, GETGROUPS_T *groups)
 {
@@ -87,20 +360,17 @@ int
 sudo_edit(struct command_details *command_details)
 {
     struct command_details editor_details;
-    ssize_t nread, nwritten;
     const char *tmpdir;
-    char *cp, *suff, **nargv, **ap, **files = NULL;
-    char buf[BUFSIZ];
-    int rc, i, j, ac, ofd, tfd, nargc, rval, tmplen;
-    int editor_argc = 0, nfiles = 0;
+    char **ap;
+    char **nargv, **files = NULL;
+    int editor_argc = 0;
+    int i, ac, nargc, rval, nfiles = 0, tmplen;
     struct stat sb;
-    struct timeval tv, tv1, tv2;
-    struct tempfile {
-	char *tfile;
-	char *ofile;
-	struct timeval omtim;
-	off_t osize;
-    } *tf = NULL;
+    struct timeval tv1, tv2;
+    struct tempfile *tf;
+#ifdef HAVE_SELINUX
+    int rbac_enabled;
+#endif
     debug_decl(sudo_edit, SUDO_DEBUG_EDIT)
 
     /*
@@ -109,7 +379,7 @@ sudo_edit(struct command_details *comman
      */
     if (setuid(ROOT_UID) != 0) {
 	warning(_("unable to change uid to root (%u)"), ROOT_UID);
-	goto cleanup;
+	return 1;
     }
 
     /*
@@ -127,6 +397,9 @@ sudo_edit(struct command_details *comman
     while (tmplen > 0 && tmpdir[tmplen - 1] == '/')
 	tmplen--;
 
+#ifdef HAVE_SELINUX
+    rbac_enabled = is_selinux_enabled() > 0 && command_details->selinux_role != NULL;
+#endif
     /*
      * The user's editor must be separated from the files to be
      * edited by a "--" option.
@@ -141,7 +414,7 @@ sudo_edit(struct command_details *comman
     }
     if (nfiles == 0) {
 	warningx(_("plugin error: missing file list for sudoedit"));
-	goto cleanup;
+	return 1;
     }
 
     /*
@@ -150,81 +423,18 @@ sudo_edit(struct command_details *comman
      */
     tf = emalloc2(nfiles, sizeof(*tf));
     zero_bytes(tf, nfiles * sizeof(*tf));
-    for (i = 0, j = 0; i < nfiles; i++) {
-	rc = -1;
-	switch_user(command_details->euid, command_details->egid,
-	    command_details->ngroups, command_details->groups);
-	if ((ofd = open(files[i], O_RDONLY, 0644)) != -1 || errno == ENOENT) {
-	    if (ofd == -1) {
-		zero_bytes(&sb, sizeof(sb));		/* new file */
-		rc = 0;
-	    } else {
-		rc = fstat(ofd, &sb);
-	    }
-	}
-	switch_user(ROOT_UID, user_details.egid,
-	    user_details.ngroups, user_details.groups);
-	if (rc || (ofd != -1 && !S_ISREG(sb.st_mode))) {
-	    if (rc)
-		warning("%s", files[i]);
-	    else
-		warningx(_("%s: not a regular file"), files[i]);
-	    if (ofd != -1)
-		close(ofd);
-	    continue;
-	}
-	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) {
-	    easprintf(&tf[j].tfile, "%.*s/%.*sXXXXXXXX%s", tmplen, tmpdir,
-		(int)(size_t)(suff - cp), cp, suff);
-	} else {
-	    easprintf(&tf[j].tfile, "%.*s/%s.XXXXXXXX", tmplen, tmpdir, cp);
-	}
-	if (seteuid(user_details.uid) != 0)
-	    error(1, "seteuid(%d)", (int)user_details.uid);
-	tfd = mkstemps(tf[j].tfile, suff ? strlen(suff) : 0);
-	if (seteuid(ROOT_UID) != 0)
-	    error(1, "seteuid(ROOT_UID)");
-	if (tfd == -1) {
-	    warning("mkstemps");
-	    goto cleanup;
-	}
-	if (ofd != -1) {
-	    while ((nread = read(ofd, buf, sizeof(buf))) != 0) {
-		if ((nwritten = write(tfd, buf, nread)) != nread) {
-		    if (nwritten == -1)
-			warning("%s", tf[j].tfile);
-		    else
-			warningx(_("%s: short write"), tf[j].tfile);
-		    goto cleanup;
-		}
-	    }
-	    close(ofd);
-	}
-	/*
-	 * We always update the stashed mtime because the time
-	 * resolution of the filesystem the temporary file is on may
-	 * not match that of the filesystem where the file to be edited
-	 * resides.  It is OK if touch() fails since we only use the info
-	 * to determine whether or not a file has been modified.
-	 */
-	(void) touch(tfd, NULL, &tf[j].omtim);
-	rc = fstat(tfd, &sb);
-	if (!rc)
-	    mtim_get(&sb, &tf[j].omtim);
-	close(tfd);
-	j++;
-    }
-    if ((nfiles = j) == 0)
-	goto cleanup;		/* no files readable, you lose */
+    
+    /* Make temporary copies of the original files */
+    if (!rbac_enabled)
+        nfiles = sudo_edit_copy(command_details, tf, files, nfiles, tmpdir, tmplen, 0);
+    else
+        nfiles = selinux_edit_copy(command_details, tf, files, nfiles, tmpdir, tmplen, 0);
 
+    if (nfiles <= 0)
+        return 1;
+    
+    switch_user(ROOT_UID, user_details.egid,
+                        user_details.ngroups, user_details.groups);
     /*
      * Allocate space for the new argument vector and fill it in.
      * We concatenate the editor with its args and the file list
@@ -253,84 +463,18 @@ sudo_edit(struct command_details *comman
     editor_details.argv = nargv;
     rval = run_command(&editor_details);
     gettimeofday(&tv2, NULL);
+    timevalsub(&tv1, &tv2);
 
-    /* Copy contents of temp files to real ones */
-    for (i = 0; i < nfiles; i++) {
-	rc = -1;
-	if (seteuid(user_details.uid) != 0)
-	    error(1, "seteuid(%d)", (int)user_details.uid);
-	if ((tfd = open(tf[i].tfile, O_RDONLY, 0644)) != -1) {
-	    rc = fstat(tfd, &sb);
-	}
-	if (seteuid(ROOT_UID) != 0)
-	    error(1, "seteuid(ROOT_UID)");
-	if (rc || !S_ISREG(sb.st_mode)) {
-	    if (rc)
-		warning("%s", tf[i].tfile);
-	    else
-		warningx(_("%s: not a regular file"), tf[i].tfile);
-	    warningx(_("%s left unmodified"), tf[i].ofile);
-	    if (tfd != -1)
-		close(tfd);
-	    continue;
-	}
-	mtim_get(&sb, &tv);
-	if (tf[i].osize == sb.st_size && timevalcmp(&tf[i].omtim, &tv, ==)) {
-	    /*
-	     * If mtime and size match but the user spent no measurable
-	     * time in the editor we can't tell if the file was changed.
-	     */
-	    timevalsub(&tv1, &tv2);
-	    if (timevalisset(&tv2)) {
-		warningx(_("%s unchanged"), tf[i].ofile);
-		unlink(tf[i].tfile);
-		close(tfd);
-		continue;
-	    }
-	}
-	switch_user(command_details->euid, command_details->egid,
-	    command_details->ngroups, command_details->groups);
-	ofd = open(tf[i].ofile, O_WRONLY|O_TRUNC|O_CREAT, 0644);
-	switch_user(ROOT_UID, user_details.egid,
-	    user_details.ngroups, user_details.groups);
-	if (ofd == -1) {
-	    warning(_("unable to write to %s"), tf[i].ofile);
-	    warningx(_("contents of edit session left in %s"), tf[i].tfile);
-	    close(tfd);
-	    continue;
-	}
-	while ((nread = read(tfd, buf, sizeof(buf))) > 0) {
-	    if ((nwritten = write(ofd, buf, nread)) != nread) {
-		if (nwritten == -1)
-		    warning("%s", tf[i].ofile);
-		else
-		    warningx(_("%s: short write"), tf[i].ofile);
-		break;
-	    }
-	}
-	if (nread == 0) {
-	    /* success, got EOF */
-	    unlink(tf[i].tfile);
-	} else if (nread < 0) {
-	    warning(_("unable to read temporary file"));
-	    warningx(_("contents of edit session left in %s"), tf[i].tfile);
-	} else {
-	    warning(_("unable to write to %s"), tf[i].ofile);
-	    warningx(_("contents of edit session left in %s"), tf[i].tfile);
-	}
-	close(ofd);
-    }
+    switch_user(ROOT_UID, user_details.egid,
+                        user_details.ngroups, user_details.groups);
+    
+    /* Copy the temporary files back to originals */
+    if (!rbac_enabled)
+	 nfiles = sudo_edit_copy(command_details, tf, NULL, nfiles, NULL, 0, timevalisset(&tv2));
+    else
+	 nfiles = selinux_edit_copy(command_details, tf, NULL, nfiles, NULL, 0, timevalisset(&tv2));
+    
     debug_return_int(rval);
-
-cleanup:
-    /* Clean up temp files and return. */
-    if (tf != NULL) {
-	for (i = 0; i < nfiles; i++) {
-	    if (tf[i].tfile != NULL)
-		unlink(tf[i].tfile);
-	}
-    }
-    debug_return_int(1);
 }
 
 #else /* HAVE_SETRESUID || HAVE_SETREUID || HAVE_SETEUID */
diff -up sudo-1.8.6p3/src/sudo.h.sudoedit-selinux sudo-1.8.6p3/src/sudo.h
--- sudo-1.8.6p3/src/sudo.h.sudoedit-selinux	2012-09-18 15:56:30.000000000 +0200
+++ sudo-1.8.6p3/src/sudo.h	2012-09-25 16:04:36.690423001 +0200
@@ -130,6 +130,7 @@ struct user_details {
 #define CD_RBAC_ENABLED		0x0800
 #define CD_USE_PTY		0x1000
 #define CD_SET_UTMP		0x2000
+#define CD_SUDOEDIT_COPY	0x4000
 
 struct command_details {
     uid_t uid;