1 /* $OpenBSD: rmt.c,v 1.23 2019/06/28 13:32:50 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 1983 Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * rmt 34 */ 35 #include <sys/types.h> 36 #include <sys/socket.h> 37 #include <sys/stat.h> 38 #include <sys/ioctl.h> 39 #include <sys/mtio.h> 40 41 #include <unistd.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <string.h> 48 #include <limits.h> 49 50 int tape = -1; 51 52 char *record; 53 int maxrecsize = -1; 54 55 #define STRSIZE 64 56 char device[PATH_MAX]; 57 char lastdevice[PATH_MAX] = ""; 58 char count[STRSIZE], mode[STRSIZE], pos[STRSIZE], op[STRSIZE]; 59 60 char resp[BUFSIZ]; 61 62 FILE *debug; 63 #define DEBUG(f) if (debug) fprintf(debug, f) 64 #define DEBUG1(f,a) if (debug) fprintf(debug, f, a) 65 #define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2) 66 67 char *checkbuf(char *, int); 68 void getstring(char *, int); 69 void error(int); 70 __dead void usage(void); 71 72 int 73 main(int argc, char *argv[]) 74 { 75 off_t orval; 76 int rval; 77 char c; 78 int n, i, cc; 79 int ch, rflag = 0, wflag = 0; 80 int f, acc; 81 mode_t m; 82 char *dir = NULL; 83 char *devp; 84 size_t dirlen; 85 86 if (pledge("stdio rpath wpath cpath inet", NULL) == -1) 87 err(1, "pledge"); 88 89 while ((ch = getopt(argc, argv, "d:rw")) != -1) { 90 switch (ch) { 91 case 'd': 92 dir = optarg; 93 if (*dir != '/') 94 errx(1, "directory must be absolute"); 95 break; 96 case 'r': 97 rflag = 1; 98 break; 99 case 'w': 100 wflag = 1; 101 break; 102 default: 103 usage(); 104 /* NOTREACHED */ 105 } 106 } 107 argc -= optind; 108 argv += optind; 109 110 if (rflag && wflag) 111 usage(); 112 113 if (argc > 0) { 114 debug = fopen(*argv, "w"); 115 if (debug == 0) 116 err(1, "cannot open debug file"); 117 setvbuf(debug, NULL, _IONBF, 0); 118 } 119 120 if (dir) { 121 if (chdir(dir) != 0) 122 err(1, "chdir"); 123 dirlen = strlen(dir); 124 } 125 126 top: 127 errno = 0; 128 rval = 0; 129 if (read(STDIN_FILENO, &c, 1) != 1) 130 exit(0); 131 switch (c) { 132 133 case 'O': 134 if (tape >= 0) 135 (void) close(tape); 136 getstring(device, sizeof(device)); 137 getstring(mode, sizeof(mode)); 138 DEBUG2("rmtd: O %s %s\n", device, mode); 139 140 devp = device; 141 f = atoi(mode); 142 m = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; 143 acc = f & O_ACCMODE; 144 if (dir) { 145 /* Strip away valid directory prefix. */ 146 if (strncmp(dir, devp, dirlen) == 0 && 147 (devp[dirlen - 1] == '/' || 148 devp[dirlen] == '/')) { 149 devp += dirlen; 150 while (*devp == '/') 151 devp++; 152 } 153 /* Don't allow directory traversal. */ 154 if (strchr(devp, '/')) { 155 errno = EACCES; 156 goto ioerror; 157 } 158 f |= O_NOFOLLOW; 159 } 160 if (rflag) { 161 /* 162 * Only allow readonly open and ignore file 163 * creation requests. 164 */ 165 if (acc != O_RDONLY) { 166 errno = EPERM; 167 goto ioerror; 168 } 169 f &= ~O_CREAT; 170 } else if (wflag) { 171 /* 172 * Require, and force creation of, a nonexistent file, 173 * unless we are reopening the last opened file again, 174 * in which case it is opened read-only. 175 */ 176 if (strcmp(devp, lastdevice) != 0) { 177 /* 178 * Disallow read-only open since that would 179 * only result in an empty file. 180 */ 181 if (acc == O_RDONLY) { 182 errno = EPERM; 183 goto ioerror; 184 } 185 f |= O_CREAT | O_EXCL; 186 } else { 187 acc = O_RDONLY; 188 } 189 /* Create readonly file */ 190 m = S_IRUSR|S_IRGRP|S_IROTH; 191 } 192 /* Apply new access mode. */ 193 f = (f & ~O_ACCMODE) | acc; 194 195 tape = open(devp, f, m); 196 if (tape == -1) 197 goto ioerror; 198 (void)strlcpy(lastdevice, devp, sizeof(lastdevice)); 199 goto respond; 200 201 case 'C': 202 DEBUG("rmtd: C\n"); 203 getstring(device, sizeof(device)); /* discard */ 204 if (close(tape) == -1) 205 goto ioerror; 206 tape = -1; 207 goto respond; 208 209 case 'L': 210 getstring(count, sizeof(count)); 211 getstring(pos, sizeof(pos)); 212 DEBUG2("rmtd: L %s %s\n", count, pos); 213 orval = lseek(tape, strtoll(count, NULL, 0), atoi(pos)); 214 if (orval == -1) 215 goto ioerror; 216 goto respond; 217 218 case 'W': 219 getstring(count, sizeof(count)); 220 n = atoi(count); 221 DEBUG1("rmtd: W %s\n", count); 222 record = checkbuf(record, n); 223 for (i = 0; i < n; i += cc) { 224 cc = read(STDIN_FILENO, &record[i], n - i); 225 if (cc <= 0) { 226 DEBUG("rmtd: premature eof\n"); 227 exit(2); 228 } 229 } 230 rval = write(tape, record, n); 231 if (rval == -1) 232 goto ioerror; 233 goto respond; 234 235 case 'R': 236 getstring(count, sizeof(count)); 237 DEBUG1("rmtd: R %s\n", count); 238 n = atoi(count); 239 record = checkbuf(record, n); 240 rval = read(tape, record, n); 241 if (rval == -1) 242 goto ioerror; 243 (void) snprintf(resp, sizeof resp, "A%d\n", rval); 244 (void) write(STDOUT_FILENO, resp, strlen(resp)); 245 (void) write(STDOUT_FILENO, record, rval); 246 goto top; 247 248 case 'I': 249 getstring(op, sizeof(op)); 250 getstring(count, sizeof(count)); 251 DEBUG2("rmtd: I %s %s\n", op, count); 252 { struct mtop mtop; 253 mtop.mt_op = atoi(op); 254 mtop.mt_count = atoi(count); 255 if (ioctl(tape, MTIOCTOP, (char *)&mtop) == -1) 256 goto ioerror; 257 rval = mtop.mt_count; 258 } 259 goto respond; 260 261 case 'S': /* status */ 262 DEBUG("rmtd: S\n"); 263 { struct mtget mtget; 264 if (ioctl(tape, MTIOCGET, (char *)&mtget) == -1) 265 goto ioerror; 266 rval = sizeof (mtget); 267 (void) snprintf(resp, sizeof resp, "A%d\n", rval); 268 (void) write(STDOUT_FILENO, resp, strlen(resp)); 269 (void) write(STDOUT_FILENO, (char *)&mtget, sizeof (mtget)); 270 goto top; 271 } 272 273 default: 274 DEBUG1("rmtd: garbage command %c\n", c); 275 exit(3); 276 } 277 respond: 278 DEBUG1("rmtd: A %d\n", rval); 279 (void) snprintf(resp, sizeof resp, "A%d\n", rval); 280 (void) write(STDOUT_FILENO, resp, strlen(resp)); 281 goto top; 282 ioerror: 283 error(errno); 284 goto top; 285 } 286 287 void 288 getstring(char *bp, int size) 289 { 290 char *cp = bp; 291 char *ep = bp + size - 1; 292 293 do { 294 if (read(STDIN_FILENO, cp, 1) != 1) 295 exit(0); 296 } while (*cp != '\n' && ++cp < ep); 297 *cp = '\0'; 298 } 299 300 char * 301 checkbuf(char *record, int size) 302 { 303 if (size <= maxrecsize) 304 return (record); 305 if (record != 0) 306 free(record); 307 record = malloc(size); 308 if (record == 0) { 309 DEBUG("rmtd: cannot allocate buffer space\n"); 310 exit(4); 311 } 312 maxrecsize = size; 313 while (size > 1024 && 314 setsockopt(0, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) == -1) 315 size -= 1024; 316 return (record); 317 } 318 319 void 320 error(int num) 321 { 322 323 DEBUG2("rmtd: E %d (%s)\n", num, strerror(num)); 324 (void) snprintf(resp, sizeof (resp), "E%d\n%s\n", num, strerror(num)); 325 (void) write(STDOUT_FILENO, resp, strlen(resp)); 326 } 327 328 __dead void 329 usage(void) 330 { 331 extern char *__progname; 332 333 (void)fprintf(stderr, "usage: %s [-r | -w] [-d directory]\n", 334 __progname); 335 exit(1); 336 } 337