Hello,

I'm using rmt in combination with the -d option for remote dumps from
multiple machines. It works fine, but the limitation on forward slashes
fills my backup directory with hundreds of dumpfiles in only a month.
I'd like to keep this a bit more organized in subdirectories based on
device or level.

So I added unveil and removed the limitation on forward slashes as well
as symbolic links when using the -d option. I believe (please correct me
if I'm wrong) these limitations were added to prevent breaking out of
the directory while keeping the code simple. With unveil this isn't
needed anymore.

While there I also made some additional changes to the -d option logic,
see separate diff at the bottom:

- Join if condition lines and fix indent for statements inside the block
- Always remove leading slashes from filenames, not just when stripping
a valid directory prefix

I've tested this with files but not an actual tape device.

Regards,
Andre

--- usr.sbin/rmt/rmt.8
+++ usr.sbin/rmt/rmt.8
@@ -57,7 +57,6 @@ The options are as follows:
 .It Fl d Ar directory
 Confine file access to
 .Ar directory .
-Forward slashes in filenames are disallowed and symlinks are not followed.
 .It Fl r
 Read-only mode, suitable for use with
 .Xr rrestore 8 .

--- usr.sbin/rmt/rmt.c
+++ usr.sbin/rmt/rmt.c
@@ -83,7 +83,7 @@ main(int argc, char *argv[])
        char *devp;
        size_t dirlen;
 
-       if (pledge("stdio rpath wpath cpath inet", NULL) == -1)
+       if (pledge("stdio rpath wpath cpath inet unveil", NULL) == -1)
                err(1, "pledge");
 
        while ((ch = getopt(argc, argv, "d:rw")) != -1) {
@@ -118,10 +118,27 @@ main(int argc, char *argv[])
        }
 
        if (dir) {
+               if (rflag) {
+                       if (unveil(dir, "r") == -1)
+                               err(1, "unveil %s", dir);
+               } else {
+                       if (unveil(dir, "rwc") == -1)
+                               err(1, "unveil %s", dir);
+               }
                if (chdir(dir) != 0)
                        err(1, "chdir");
                dirlen = strlen(dir);
+       } else {
+               if (rflag) {
+                       if (unveil("/", "r") == -1)
+                               err(1, "unveil /");
+               } else {
+                       if (unveil("/", "rwc") == -1)
+                               err(1, "unveil /");
+               }
        }
+       if (unveil(NULL, NULL) == -1)
+               err(1, "unveil");
 
 top:
        errno = 0;
@@ -150,12 +167,6 @@ top:
                             while (*devp == '/')
                                devp++;
                        }
-                       /* Don't allow directory traversal. */
-                       if (strchr(devp, '/')) {
-                               errno = EACCES;
-                               goto ioerror;
-                       }
-                       f |= O_NOFOLLOW;
                }
                if (rflag) {
                        /*

--- usr.sbin/rmt/rmt.c
+++ usr.sbin/rmt/rmt.c
@@ -161,12 +161,10 @@ top:
                if (dir) {
                        /* Strip away valid directory prefix. */
                        if (strncmp(dir, devp, dirlen) == 0 &&
-                           (devp[dirlen - 1] == '/' ||
-                            devp[dirlen] == '/')) {
-                            devp += dirlen;
-                            while (*devp == '/')
+                           (devp[dirlen - 1] == '/' || devp[dirlen] == '/'))
+                               devp += dirlen;
+                       while (*devp == '/')
                                devp++;
-                       }
                }
                if (rflag) {
                        /*

Reply via email to