I'm going wide with this diff I've been pushing for quite some time now.

Is *anyone* but me using rdump(8) + rmt(8)?

*If you are currently using rdump/rrestore + rmt, I urge you to test
this diff to make sure it causes no regression. It shouldn't, but
you've been warned.

So, anyway, this diff allows running a restricted rmt(8), in my case
for remote dumps over ssh, a.k.a rdump(8).

For restricting rmt(8) when dumping/restoring to/from a remote machine:

  -d <directory>   confines rmt to operate within a single directory.
  -r               enforces read-only mode.
  -w               enforces write-only mode.

With this, rmt could be used with the following (simplified)
.ssh/authorized_keys entries

  command="/etc/rmt -wd /dumps/host/foo" ssh-ed25519 ...dumpkey...
  command="/etc/rmt -rd /dumps/host/foo" ssh-ed25519 ...restorekey...

This has the major advantage that a remote user cannot ever destroy or
manipulate former backups. A bit more detail is in the man page.

OK?

/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       9 Sep 2015 22:57:41 -0000
@@ -36,18 +36,38 @@
 .Nm rmt
 .Nd remote magtape protocol module
 .Sh SYNOPSIS
-.Nm rmt
+.Nm
+.Op Fl r | 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 missing files and refuses to open 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.15
diff -u -p -r1.15 rmt.c
--- rmt.c       16 Jan 2015 06:40:20 -0000      1.15
+++ rmt.c       9 Sep 2015 22:57:41 -0000
@@ -41,6 +41,7 @@
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <err.h>
 #include <errno.h>
 #include <string.h>
 #include <limits.h>
@@ -52,6 +53,7 @@ int   maxrecsize = -1;
 
 #define        STRSIZE 64
 char   device[PATH_MAX];
+char   lastdevice[PATH_MAX] = "";
 char   count[STRSIZE], mode[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;
@@ -93,10 +132,66 @@ top:
                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);
+
+               devp = device;
+               f = atoi(mode);
+               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 mode. */
+               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