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
main(int argc,char * argv[])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
getstring(char * bp,int size)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 *
checkbuf(char * record,int size)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
error(int num)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
usage(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