Blame SOURCES/sudo-1.8.6p3-sudoedit-selinux.patch

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