Hi,

Here's a diff that's been in my tree for quite some time, getting
polished back and forth. I think I'm quite satisfied as is now, and
would like to get opinions and/or OK's to commit it.

This diff gives rmt the following abilities:
- limit the (remote) file operations to a specific directory
- write mode (creates missing files and protects existing ones)
- readonly mode

Some people argue that just piping the output through SSH covers this,
but since that provides no feedback, you can actually in a worst-case
scenario end up losing data without dump(8) realizing so. Apart from
that single dump not working out as expected, /etc/dumpdates will also
be erroneously updated.

/Alexander


Index: rmt.8
===================================================================
RCS file: /cvs/src/usr.sbin/rmt/rmt.8,v
retrieving revision 1.12
diff -u -p -r1.12 rmt.8
--- rmt.8       23 Jul 2011 15:40:13 -0000      1.12
+++ rmt.8       18 May 2014 22:18:46 -0000
@@ -36,18 +36,38 @@
 .Nm rmt
 .Nd remote magtape protocol module
 .Sh SYNOPSIS
-.Nm rmt
+.Nm
+.Op Fl r | Fl w
+.Op Fl d Ar directory
 .Sh DESCRIPTION
 .Nm
 is a program used by the remote dump and restore programs
-in manipulating a magnetic tape drive through an interprocess
-communication connection.
+through an interprocess communication connection.
+Traditionally it is used for manipulating a magnetic tape drive but it may
+be used for regular file access as well.
 .Nm
 is normally started up with an
 .Xr rcmd 3
 or
 .Xr rcmdsh 3
 call.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.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 .
+.It Fl w
+File write mode, suitable for use with
+.Xr rdump 8
+for dumping to regular files.
+Creates nonexisting files and disallows opening existing ones.
+The file permission bits are set to readonly.
+.El
 .Pp
 The
 .Nm
Index: rmt.c
===================================================================
RCS file: /cvs/src/usr.sbin/rmt/rmt.c,v
retrieving revision 1.14
diff -u -p -r1.14 rmt.c
--- rmt.c       3 Dec 2013 00:20:03 -0000       1.14
+++ rmt.c       18 May 2014 22:18:46 -0000
@@ -42,6 +42,7 @@
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <err.h>
 #include <errno.h>
 #include <string.h>
 
@@ -52,7 +53,8 @@ int   maxrecsize = -1;
 
 #define        STRSIZE 64
 char   device[MAXPATHLEN];
-char   count[STRSIZE], mode[STRSIZE], pos[STRSIZE], op[STRSIZE];
+char   lastdevice[MAXPATHLEN] = "";
+char   count[STRSIZE], flags[STRSIZE], pos[STRSIZE], op[STRSIZE];
 
 char   resp[BUFSIZ];
 
@@ -61,9 +63,10 @@ FILE *debug;
 #define        DEBUG1(f,a)     if (debug) fprintf(debug, f, a)
 #define        DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2)
 
-char   *checkbuf(char *, int);
-void   getstring(char *, int);
-void   error(int);
+char           *checkbuf(char *, int);
+void           getstring(char *, int);
+void           error(int);
+__dead void    usage(void);
 
 int
 main(int argc, char *argv[])
@@ -72,14 +75,50 @@ main(int argc, char *argv[])
        int rval;
        char c;
        int n, i, cc;
+       int ch, rflag = 0, wflag = 0;
+       int f, acc;
+       mode_t m;
+       char *dir = NULL;
+       char *devp;
+       size_t dirlen;
+
+       while ((ch = getopt(argc, argv, "d:rw")) != -1) {
+               switch (ch) {
+               case 'd':
+                       dir = optarg;
+                       if (*dir != '/')
+                               errx(1, "directory must be absolute");
+                       break;
+               case 'r':
+                       rflag = 1;
+                       break;
+               case 'w':
+                       wflag = 1;
+                       break;
+               default:
+                       usage();
+                       /* NOTREACHED */
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (rflag && wflag)
+               usage();
 
-       argc--, argv++;
        if (argc > 0) {
                debug = fopen(*argv, "w");
                if (debug == 0)
-                       exit(1);
+                       err(1, "cannot open debug file");
                (void) setbuf(debug, (char *)0);
        }
+
+       if (dir) {
+               if (chdir(dir) != 0)
+                       err(1, "chdir");
+               dirlen = strlen(dir);
+       }
+
 top:
        errno = 0;
        rval = 0;
@@ -91,12 +130,68 @@ top:
                if (tape >= 0)
                        (void) close(tape);
                getstring(device, sizeof(device));
-               getstring(mode, sizeof(mode));
-               DEBUG2("rmtd: O %s %s\n", device, mode);
-               tape = open(device, atoi(mode),
-                   S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+               getstring(flags, sizeof(flags));
+               DEBUG2("rmtd: O %s %s\n", device, flags);
+
+               devp = device;
+               f = atoi(flags);
+               m = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+               acc = f & O_ACCMODE;
+               if (dir) {
+                       /* Strip away valid directory prefix. */
+                       if (strncmp(dir, devp, dirlen) == 0 &&
+                           (devp[dirlen - 1] == '/' ||
+                            devp[dirlen] == '/')) {
+                            devp += dirlen;
+                            while (*devp == '/')
+                               devp++;
+                       }
+                       /* Don't allow directory traversal. */
+                       if (strchr(devp, '/')) {
+                               errno = EACCES;
+                               goto ioerror;
+                       }
+                       f |= O_NOFOLLOW;
+               }
+               if (rflag) {
+                       /*
+                        * Only allow readonly open and ignore file
+                        * creation requests.
+                        */
+                       if (acc != O_RDONLY) {
+                               errno = EPERM;
+                               goto ioerror;
+                       }
+                       f &= ~O_CREAT;
+               } else if (wflag) {
+                       /*
+                        * Require, and force creation of, a nonexistant file,
+                        * unless we are reopening the last opened file again,
+                        * in which case it is opened read-only.
+                        */
+                       if (strcmp(devp, lastdevice) != 0) {
+                               /*
+                                * Disallow read-only open since that would
+                                * only result in an empty file.
+                                */
+                               if (acc == O_RDONLY) {
+                                       errno = EPERM;
+                                       goto ioerror;
+                               }
+                               f |= O_CREAT | O_EXCL;
+                       } else {
+                               acc = O_RDONLY;
+                       }
+                       /* Create readonly file */
+                       m = S_IRUSR|S_IRGRP|S_IROTH;
+               }
+               /* Apply new access flags. */
+               f = (f & ~O_ACCMODE) | acc;
+
+               tape = open(device, f, m);
                if (tape == -1)
                        goto ioerror;
+               (void)strlcpy(lastdevice, devp, sizeof(lastdevice));
                goto respond;
 
        case 'C':
@@ -224,4 +319,14 @@ error(int num)
        DEBUG2("rmtd: E %d (%s)\n", num, strerror(num));
        (void) snprintf(resp, sizeof (resp), "E%d\n%s\n", num, strerror(num));
        (void) write(STDOUT_FILENO, resp, strlen(resp));
+}
+
+__dead void
+usage(void)
+{
+       extern char *__progname;
+
+       (void)fprintf(stderr, "usage: %s [-r | -w] [-d directory]\n",
+           __progname);
+       exit(1);
 }

Reply via email to