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); }