First, some generals remarks: - The debug feature (not documented) defeat the `-r' flag purpose.
I think the code should be either: - enclosed in #ifdef DEBUG (prefered way) - not permitted if `rflag' or `wflag' are setted - Adding tame(2) may be a good way to enforce the policyi, but it should be added later (when other userland tools gains it). Else, just some comments inline. On Thu, Sep 10, 2015 at 12:58:52AM +0200, Alexander Hall wrote: > > 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 > @@ -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); atoi(3) is a bit fragile. > + 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++; > + } the "strip away valid directory prefix" code is a bit complex. If I understand your purpose, you deal with possible trailing '/' in `dir' in the same code than removing the prefix from `devp'. maybe you should: 1. canonalize `dir' (by removing possible trailing '/') (should be done in if (dir) { } block before). >> if (dir[dirlen] == '/') { >> dir[dirlen] = '\0'; >> dirlen --; >> } 2. remove `dir' prefix from `devp' >> if (strncmp(dir, devp, dirlen) == 0 && devp[dirlen] == '/') >> devp += dirlen + 1; > + /* 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; > + } does when using -w, O_RDWR should be permitted ? I would expect O_WRONLY to be the only allowed mode. > + f |= O_CREAT | O_EXCL; anyway, O_CREAT | O_EXCL would mitigate the use of O_RDWR for reading existing file... > + } 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); we have checked if `devp' is safe, so we should use `devp' instead of `device'. > if (tape == -1) > goto ioerror; > + (void)strlcpy(lastdevice, devp, sizeof(lastdevice)); > goto respond; > > case 'C': -- Sebastien Marie