opendev(3) should not be called by pledged processes, so the pledge of
dumpfs(8) needs to be redone: opendev is called in a loop over argv.

As dumpfs spews a whole lot of potentially untrusted data to stdout,
fork, read the data in the child and pipe it to the pledged parent
that writes it to stdout. pledge the child before dumping the last fs.
Thus the common case of dumping a single fs runs almost entirely
under pledge.

The loop in printdata() is taken from raw_cat() in cat(1).

Index: sbin/dumpfs/dumpfs.c
===================================================================
RCS file: /var/cvs/src/sbin/dumpfs/dumpfs.c,v
retrieving revision 1.33
diff -u -p -r1.33 dumpfs.c
--- sbin/dumpfs/dumpfs.c        23 Nov 2015 19:19:29 -0000      1.33
+++ sbin/dumpfs/dumpfs.c        13 May 2016 20:40:08 -0000
@@ -40,6 +40,7 @@
 
 #include <sys/param.h> /* DEV_BSIZE MAXBSIZE isset */
 #include <sys/time.h>
+#include <sys/wait.h>
 
 #include <ufs/ufs/dinode.h>
 #include <ufs/ffs/fs.h>
@@ -73,6 +74,7 @@ int   dumpcg(const char *, int, int);
 int    marshal(const char *);
 int    open_disk(const char *);
 void   pbits(void *, int);
+void   printdata(void);
 __dead void    usage(void);
 
 int
@@ -80,7 +82,9 @@ main(int argc, char *argv[])
 {
        struct fstab *fs;
        const char *name;
-       int ch, domarshal, eval, fd;
+       pid_t pid, wpid;
+       int p[2];
+       int ch, domarshal, eval, fd, pstat;
 
        domarshal = eval = 0;
 
@@ -100,10 +104,34 @@ main(int argc, char *argv[])
        if (argc < 1)
                usage();
 
-       if (pledge("stdio rpath disklabel", NULL) == -1)
-               err(1, "pledge");
+       if (pipe(p) < 0)
+               err(1, "pipe");
 
-       for (; *argv != NULL; argv++) {
+       switch(pid = fork()) {
+       case -1:
+               err(1, "fork");
+
+       case 0:
+               close(p[0]);
+               dup2(p[1], STDOUT_FILENO);
+               close(p[1]);
+               break;  
+
+       default:
+               if (pledge("stdio", NULL) == -1)
+                       err(1, "pledge");
+               close(p[1]);
+               dup2(p[0], STDIN_FILENO);
+               close(p[0]);
+               printdata();
+               do {
+                       wpid = waitpid(pid, &pstat, 0);
+               } while (wpid == -1 && errno == EINTR);
+
+               return (wpid == -1 || WEXITSTATUS(pstat));
+       }
+
+       for (; *argv != NULL; argv++, argc--) {
                if ((fs = getfsfile(*argv)) != NULL)
                        name = fs->fs_spec;
                else
@@ -112,13 +140,19 @@ main(int argc, char *argv[])
                        eval |= 1;
                        continue;
                }
+               if (argc == 1)
+                       if (pledge("stdio", NULL) == -1)
+                               err(1, "pledge");
                if (domarshal)
                        eval |= marshal(name);
                else
                        eval |= dumpfs(fd, name);
                close(fd);
        }
-       exit(eval);
+
+       if (fclose(stdout))
+               err(1, "fclose");
+       _exit(eval);
 }
 
 int
@@ -458,6 +492,24 @@ pbits(void *vp, int max)
                                printf("-%d", i);
                }
        printf("\n");
+}
+
+void
+printdata(void)
+{
+       int nr, nw, off;
+       char buf[1024];
+       
+       while ((nr = read(STDIN_FILENO, buf, sizeof(buf))) != -1 && nr != 0) {
+               for (off = 0; nr; nr -= nw, off += nw) {
+                       if ((nw = write(STDOUT_FILENO, buf + off, nr)) == 0 ||
+                           nw == -1)
+                               err(1, "stdout");
+               }
+       }
+
+       if (nr < 0)
+               warn("read");
 }
 
 __dead void

Reply via email to