1 /* @(#)rmt.c 1.38 18/06/09 Copyright 1994,2000-2018 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)rmt.c 1.38 18/06/09 Copyright 1994,2000-2018 J. Schilling";
6 #endif
7 /*
8 * Remote tape server
9 * Supports both the old BSD format and the new abstract Sun format
10 * which is called RMT V1 protocol.
11 *
12 * A client that likes to use the enhanced features of the RMT V1 protocol
13 * needs to send a "I-1\n0\n" request directly after opening the remote
14 * file using the 'O' rmt command.
15 * If the client requests the new protocol, MTIOCTOP ioctl opcodes
16 * in the range 0..7 are mapped to the BSD values to prevent problems
17 * from Linux opcode incompatibility.
18 *
19 * The open modes support an abstract string notation found in rmt.c from
20 * GNU. This makes it possible to use more than O_RDONLY|O_WRONLY|O_RDWR
21 * with open(). MTIOCTOP tape ops could be enhanced the same way, but it
22 * seems that the current interface supports all what we need over the
23 * wire.
24 *
25 * Copyright (c) 1994,2000-2018 J. Schilling
26 */
27 /*
28 * The contents of this file are subject to the terms of the
29 * Common Development and Distribution License, Version 1.0 only
30 * (the "License"). You may not use this file except in compliance
31 * with the License.
32 *
33 * See the file CDDL.Schily.txt in this distribution for details.
34 * A copy of the CDDL is also available via the Internet at
35 * http://www.opensource.org/licenses/cddl1.txt
36 *
37 * When distributing Covered Code, include this CDDL HEADER in each
38 * file and include the License file CDDL.Schily.txt from this distribution.
39 */
40
41 /*#define FORCE_DEBUG*/
42
43 #include <schily/stdio.h>
44 #include <schily/stdlib.h>
45 #include <schily/unistd.h> /* includes sys/types.h */
46 #include <schily/fcntl.h>
47 #include <schily/stat.h>
48 #include <schily/string.h>
49 #include <schily/socket.h>
50 #include <schily/param.h> /* BSD-4.2 & Linux need this for MAXHOSTNAMELEN */
51 #include <schily/ioctl.h>
52 #include <schily/mtio.h>
53 #include <schily/errno.h>
54 #include <schily/pwd.h>
55
56 #include <schily/utypes.h>
57 #include <schily/standard.h>
58 #include <schily/deflts.h>
59 #include <schily/patmatch.h>
60 #include <schily/schily.h>
61 #include <schily/in.h>
62 #include <schily/inet.h> /* BeOS does not have <arpa/inet.h> */
63 /* but inet_ntaoa() is in <netdb.h> */
64 #include <schily/netdb.h>
65
66 #if (!defined(HAVE_NETDB_H) || !defined(HAVE_SYS_SOCKET_H))
67 #undef USE_REMOTE
68 #endif
69
70 EXPORT int main __PR((int argc, char **argv));
71 #ifdef USE_REMOTE
72 LOCAL void checkuser __PR((void));
73 LOCAL char *getpeer __PR((void));
74 LOCAL BOOL checkaccess __PR((char *device));
75 LOCAL BOOL strmatch __PR((char *str, char *pat));
76 LOCAL void dormt __PR((void));
77 LOCAL void opentape __PR((void));
78 LOCAL int rmtoflags __PR((char *omode));
79 LOCAL void closetape __PR((void));
80 LOCAL void readtape __PR((void));
81 LOCAL void writetape __PR((void));
82 LOCAL void ioctape __PR((int cmd));
83 #ifdef HAVE_SYS_MTIO_H
84 LOCAL int rmtmapold __PR((int cmd));
85 LOCAL int rmtmapnew __PR((int cmd));
86 #endif
87 LOCAL void statustape __PR((int cmd));
88 LOCAL void seektape __PR((void));
89 LOCAL void doversion __PR((void));
90 LOCAL int fillrdbuf __PR((void));
91 LOCAL void tryfillrdbuf __PR((void));
92 LOCAL int readchar __PR((char *cp));
93 LOCAL void readbuf __PR((char *buf, int n));
94 LOCAL int readarg __PR((char *buf, int n));
95 LOCAL char * preparebuffer __PR((int size));
96 LOCAL int checktape __PR((char *device));
97 LOCAL BOOL has_dotdot __PR((char *name));
98 LOCAL void rmtrespond __PR((long ret, int err));
99 LOCAL void rmterror __PR((char *str));
100
101 #define CMD_SIZE 80
102
103 LOCAL char *username;
104 LOCAL char *peername;
105
106 LOCAL int tape_fd = -1;
107
108 LOCAL char *debug_name;
109 LOCAL FILE *debug_file;
110 LOCAL BOOL found_dfltfile;
111
112 #define DEBUG(fmt) if (debug_file) js_fprintf(debug_file, fmt)
113 #define DEBUG1(fmt, a) if (debug_file) js_fprintf(debug_file, fmt, a)
114 #define DEBUG2(fmt, a1, a2) if (debug_file) js_fprintf(debug_file, fmt, a1, a2)
115 #define DEBUG3(fmt, a1, a2, a3) if (debug_file) js_fprintf(debug_file, fmt, a1, a2, a3)
116 #define DEBUG4(fmt, a1, a2, a3, a4) if (debug_file) js_fprintf(debug_file, fmt, a1, a2, a3, a4)
117 #endif /* USE_REMOTE */
118
119 EXPORT int
main(argc,argv)120 main(argc, argv)
121 int argc;
122 char **argv;
123 {
124 save_args(argc, argv);
125 #ifndef USE_REMOTE
126 comerrno(EX_BAD, "No remote TAPE support on this platform.\n");
127 #else
128 argc--, argv++;
129 if (argc > 0 && strcmp(*argv, "-c") == 0) {
130 /*
131 * Skip params in case we have been installed/called as shell.
132 */
133 argc--, argv++;
134 argc--, argv++;
135 }
136 /*
137 * If we are running as root (uid 0), the existence of /etc/default/rmt
138 * is required. If our uid is != 0 and there is no /etc/default/rmt
139 * we will only allow to access files in /dev (see below).
140 *
141 * WARNING you are only allowed to change the defaults configuration
142 * filename if you also change the documentation and add a statement
143 * that makes clear where the official location of the file is, why you
144 * did choose a nonstandard location and that the nonstandard location
145 * only refers to inofficial rmt versions.
146 *
147 * I was forced to add this because some people change cdrecord without
148 * rational reason and then publish the result. As those people
149 * don't contribute work and don't give support, they are causing extra
150 * work for me and this way slow down the development.
151 */
152 if (defltopen("/etc/default/rmt") < 0) {
153 if (geteuid() == 0) {
154 rmterror("Remote configuration error: Cannot open /etc/default/rmt");
155 exit(EX_BAD);
156 }
157 } else {
158 found_dfltfile = TRUE;
159 }
160 debug_name = defltread("DEBUG="); /* Get debug file name */
161 #ifdef FORCE_DEBUG
162 if (debug_name == NULL && argc <= 0)
163 debug_name = "/tmp/RMT";
164 #endif
165 #ifdef NONONO
166 /*
167 * Allowing to write arbitrary files may be a security risk.
168 */
169 if (argc > 0)
170 debug_name = *argv;
171 #endif
172
173 if (debug_name != NULL)
174 debug_file = fopen(debug_name, "w");
175
176 if (argc > 0) {
177 if (debug_file == 0) {
178 rmtrespond((long)-1, geterrno());
179 exit(EX_BAD);
180 }
181 (void) setbuf(debug_file, (char *)0);
182 }
183 checkuser(); /* Check if we are called by a bad guy */
184 peername = getpeer(); /* Get host name of caller */
185 dormt();
186 #endif /* USE_REMOTE */
187 return (0);
188 }
189
190 #ifdef USE_REMOTE
191 LOCAL void
checkuser()192 checkuser()
193 {
194 uid_t uid = getuid();
195 char *uname;
196 struct passwd *pw;
197
198 pw = getpwuid(uid);
199 if (pw == NULL)
200 goto notfound;
201
202 username = pw->pw_name;
203
204 /*
205 * If no /etc/default/rmt could be found allow general access.
206 */
207 if (!found_dfltfile)
208 return;
209
210 defltfirst();
211 while ((uname = defltnext("USER=")) != NULL) {
212 if (strmatch(username, uname))
213 return;
214 }
215 notfound:
216 rmterror("Illegal user id for RMT server");
217 exit(EX_BAD);
218 }
219
220 #ifndef NI_MAXHOST
221 #ifdef MAXHOSTNAMELEN /* XXX remove this and sys/param.h */
222 #define NI_MAXHOST MAXHOSTNAMELEN
223 #else
224 #define NI_MAXHOST 64
225 #endif
226 #endif
227
228 LOCAL char *
getpeer()229 getpeer()
230 {
231 #ifdef HAVE_GETNAMEINFO
232 #ifdef HAVE_SOCKADDR_STORAGE
233 struct sockaddr_storage sa;
234 #else
235 char sa[256];
236 #endif
237 #else
238 struct sockaddr sa;
239 struct hostent *he;
240 #endif
241 struct sockaddr *sap;
242 struct sockaddr_in *s;
243 socklen_t sasize = sizeof (sa);
244 static char buffer[NI_MAXHOST];
245
246 sap = (struct sockaddr *)&sa;
247 if (getpeername(STDIN_FILENO, sap, &sasize) < 0) {
248 int errsav = geterrno();
249 struct stat sb;
250
251 if (fstat(STDIN_FILENO, &sb) >= 0) {
252 if (S_ISFIFO(sb.st_mode)) {
253 DEBUG("rmt: stdin is a PIPE\n");
254 return ("PIPE");
255 }
256 DEBUG1("rmt: stdin st_mode %0llo\n", (Llong)sb.st_mode);
257 }
258
259 DEBUG1("rmt: peername %s\n", errmsgstr(errsav));
260 return ("ILLEGAL_SOCKET");
261 } else {
262 s = (struct sockaddr_in *)&sa;
263 #ifdef AF_INET6
264 if (s->sin_family != AF_INET && s->sin_family != AF_INET6) {
265 #else
266 if (s->sin_family != AF_INET) {
267 #endif
268 #ifdef AF_UNIX
269 /*
270 * AF_UNIX is not defined on BeOS
271 */
272 if (s->sin_family == AF_UNIX) {
273 DEBUG("rmt: stdin is a PIPE (UNIX domain socket)\n");
274 return ("PIPE");
275 }
276 #endif
277 DEBUG1("rmt: stdin NOT_IP socket (sin_family: %d)\n",
278 s->sin_family);
279 return ("NOT_IP");
280 }
281
282 #ifdef HAVE_GETNAMEINFO
283 buffer[0] = '\0';
284 if (debug_file &&
285 getnameinfo(sap, sasize, buffer, sizeof (buffer), NULL, 0,
286 NI_NUMERICHOST) == 0) {
287 DEBUG1("rmt: peername %s\n", buffer);
288 }
289 buffer[0] = '\0';
290 if (getnameinfo(sap, sasize, buffer, sizeof (buffer), NULL, 0,
291 0) == 0) {
292 DEBUG1("rmt: peername %s\n", buffer);
293 return (buffer);
294 }
295 return ("CANNOT_MAP_ADDRESS");
296 #else /* HAVE_GETNAMEINFO */
297 #ifdef HAVE_INET_NTOA
298 (void) js_snprintf(buffer, sizeof (buffer), "%s",
299 inet_ntoa(s->sin_addr));
300 #else
301 (void) js_snprintf(buffer, sizeof (buffer), "%x",
302 s->sin_addr.s_addr);
303 #endif
304 DEBUG1("rmt: peername %s\n", buffer);
305 he = gethostbyaddr((char *)&s->sin_addr.s_addr, 4, AF_INET);
306 DEBUG1("rmt: peername %s\n", he != NULL ? he->h_name:buffer);
307 if (he != NULL)
308 return (he->h_name);
309 return (buffer);
310 #endif /* HAVE_GETNAMEINFO */
311 }
312 }
313
314 LOCAL BOOL
315 checkaccess(device)
316 char *device;
317 {
318 char *target;
319 char *user;
320 char *host;
321 char *fname;
322 char *p;
323
324 if (peername == NULL)
325 return (FALSE);
326 defltfirst();
327 while ((target = defltnext("ACCESS=")) != NULL) {
328 p = target;
329 while (*p == '\t')
330 p++;
331 user = p;
332 if ((p = strchr(p, '\t')) != NULL)
333 *p++ = '\0';
334 else
335 continue;
336 if (!strmatch(username, user))
337 continue;
338
339 while (*p == '\t')
340 p++;
341 host = p;
342 if ((p = strchr(p, '\t')) != NULL)
343 *p++ = '\0';
344 else
345 continue;
346 if (!strmatch(peername, host))
347 continue;
348
349 fname = p;
350 if ((p = strchr(p, '\t')) != NULL)
351 *p++ = '\0';
352
353 DEBUG3("ACCESS %s %s %s\n", user, host, fname);
354
355 if (has_dotdot(device)) /* Do not allow ".." in name */
356 continue;
357 if (!strmatch(device, fname))
358 continue;
359 return (TRUE);
360 }
361 return (FALSE);
362 }
363
364 LOCAL BOOL
365 strmatch(str, pat)
366 char *str;
367 char *pat;
368 {
369 int *aux;
370 int *state;
371 int alt;
372 int plen;
373 char *p;
374
375 plen = strlen(pat);
376 aux = malloc(plen*sizeof (int));
377 state = malloc((plen+1)*sizeof (int));
378 if (aux == NULL || state == NULL) {
379 if (aux) free(aux);
380 if (state) free(state);
381 return (FALSE);
382 }
383
384 if ((alt = patcompile((const unsigned char *)pat, plen, aux)) == 0) {
385 /* Bad pattern */
386 free(aux);
387 free(state);
388 return (FALSE);
389 }
390
391 p = (char *)patmatch((const unsigned char *)pat, aux,
392 (const unsigned char *)str, 0,
393 strlen(str), alt, state);
394 free(aux);
395 free(state);
396
397 if (p != NULL && *p == '\0')
398 return (TRUE);
399 return (FALSE);
400 }
401
402 /*
403 * The main work loop
404 */
405 LOCAL void
406 dormt()
407 {
408 char c;
409
410 while (readchar(&c) == 1) {
411 seterrno(0);
412
413 switch (c) {
414
415 case 'O':
416 opentape();
417 break;
418 case 'C':
419 closetape();
420 break;
421 case 'R':
422 readtape();
423 break;
424 case 'W':
425 writetape();
426 break;
427 case 'I':
428 case 'i':
429 ioctape(c);
430 break;
431 case 'S':
432 case 's':
433 statustape(c);
434 break;
435 case 'L':
436 seektape();
437 break;
438 /*
439 * It would be nice to have something like 'V' for retrieving
440 * Version information. But unfortunately newer BSD rmt version
441 * implement this command in a way that is not useful at all.
442 */
443 case 'v':
444 doversion();
445 break;
446 default:
447 DEBUG1("rmtd: garbage command %c\n", c);
448 rmterror("Garbage command");
449 exit(EX_BAD);
450 }
451 }
452 exit(0);
453 }
454
455 LOCAL void
456 opentape()
457 {
458 char device[4096];
459 char omode[CMD_SIZE];
460 int omodes;
461 int n;
462
463 if (tape_fd >= 0)
464 (void) close(tape_fd);
465
466 n = readarg(device, sizeof (device));
467 if (n < 0 || n >= sizeof (device)) { /* Try to recover */
468 readarg(omode, sizeof (omode)); /* honor protocol */
469 DEBUG2("rmtd: O %s %s\n", device, omode);
470 #ifdef ENAMETOOLONG
471 seterrno(ENAMETOOLONG);
472 #else
473 seterrno(EINVAL);
474 #endif
475 goto out;
476 }
477 readarg(omode, sizeof (omode));
478 omodes = rmtoflags(omode);
479 if (omodes == -1) {
480 /*
481 * Mask off all bits that differ between operating systems.
482 */
483 omodes = atoi(omode);
484 omodes &= (O_RDONLY|O_WRONLY|O_RDWR);
485 }
486 #ifdef O_TEXT
487 /*
488 * Default to O_BINARY the client may not know that we need it.
489 */
490 if ((omodes & O_TEXT) == 0)
491 omodes |= O_BINARY;
492 #endif
493 DEBUG2("rmtd: O %s %s\n", device, omode);
494 if (!checktape(device)) {
495 tape_fd = -1;
496 seterrno(EACCES);
497 } else {
498 tape_fd = open(device, omodes, (mode_t)0666);
499 }
500 out:
501 rmtrespond((long)tape_fd, geterrno());
502 }
503
504 LOCAL struct oflags {
505 char *fname;
506 int fval;
507 } oflags[] = {
508 { "O_RDONLY", O_RDONLY },
509 { "O_RDWR", O_RDWR },
510 { "O_WRONLY", O_WRONLY },
511 #ifdef O_TEXT
512 { "O_TEXT", O_TEXT },
513 #endif
514 #ifdef O_NDELAY
515 { "O_NDELAY", O_NDELAY },
516 #endif
517 #ifdef O_APPEND
518 { "O_APPEND", O_APPEND },
519 #endif
520 #ifdef O_SYNC
521 { "O_SYNC", O_SYNC },
522 #endif
523 #ifdef O_DSYNC
524 { "O_DSYNC", O_DSYNC },
525 #endif
526 #ifdef O_RSYNC
527 { "O_RSYNC", O_RSYNC },
528 #endif
529 #ifdef O_NONBLOCK
530 { "O_NONBLOCK", O_NONBLOCK },
531 #endif
532 #ifdef O_PRIV
533 { "O_PRIV", O_PRIV },
534 #endif
535 #ifdef O_LARGEFILE
536 { "O_LARGEFILE",O_LARGEFILE },
537 #endif
538 #ifdef O_CREAT
539 { "O_CREAT", O_CREAT },
540 #endif
541 #ifdef O_TRUNC
542 { "O_TRUNC", O_TRUNC },
543 #endif
544 #ifdef O_EXCL
545 { "O_EXCL", O_EXCL },
546 #endif
547 #ifdef O_NOCTTY
548 { "O_NOCTTY", O_NOCTTY },
549 #endif
550 { NULL, 0 }
551 };
552
553 LOCAL int
554 rmtoflags(omode)
555 char *omode;
556 {
557 register char *p = omode;
558 register struct oflags *op;
559 register int slen;
560 register int nmodes = 0;
561
562 /*
563 * First skip numeric open modes...
564 */
565 while (*p != '\0' && *p == ' ')
566 p++;
567 if (*p != 'O') while (*p != '\0' && *p != ' ')
568 p++;
569 while (*p != '\0' && *p != 'O')
570 p++;
571 do {
572 if (p[0] != 'O' || p[1] != '_')
573 return (-1);
574
575 for (op = oflags; op->fname; op++) {
576 slen = strlen(op->fname);
577 if ((strncmp(op->fname, p, slen) == 0) &&
578 (p[slen] == '|' || p[slen] == ' ' ||
579 p[slen] == '\0')) {
580 nmodes |= op->fval;
581 break;
582 }
583 }
584 p = strchr(p, '|');
585 } while (p && *p++ == '|');
586
587 return (nmodes);
588 }
589
590 LOCAL void
591 closetape()
592 {
593 int ret;
594 char device[CMD_SIZE];
595
596 DEBUG("rmtd: C\n");
597 readarg(device, sizeof (device));
598 ret = close(tape_fd);
599 rmtrespond((long)ret, geterrno());
600 tape_fd = -1;
601 }
602
603 LOCAL void
604 readtape()
605 {
606 int n;
607 long ret;
608 char *buf;
609 char count[CMD_SIZE];
610
611 readarg(count, sizeof (count));
612 DEBUG1("rmtd: R %s\n", count);
613 n = atoi(count); /* Only an int because of setsockopt */
614 buf = preparebuffer(n);
615 ret = _niread(tape_fd, buf, n);
616 rmtrespond(ret, geterrno());
617 if (ret >= 0) {
618 (void) _nixwrite(STDOUT_FILENO, buf, ret);
619 }
620 }
621
622 LOCAL void
623 writetape()
624 {
625 int n;
626 long ret;
627 char *buf;
628 char count[CMD_SIZE];
629
630 readarg(count, sizeof (count));
631 n = atoi(count); /* Only an int because of setsockopt */
632 DEBUG1("rmtd: W %s\n", count);
633 buf = preparebuffer(n);
634 readbuf(buf, n);
635 ret = _niwrite(tape_fd, buf, n);
636 rmtrespond(ret, geterrno());
637 }
638
639 /*
640 * Definitions for the new RMT Protocol version 1
641 *
642 * The new Protocol version tries to make the use
643 * of rmtioctl() more portable between different platforms.
644 */
645 #define RMTIVERSION -1
646 #define RMT_VERSION 1
647
648 /*
649 * Support for commands beyond MTWEOF..MTNOP (0..7)
650 */
651 #define RMTICACHE 0
652 #define RMTINOCACHE 1
653 #define RMTIRETEN 2
654 #define RMTIERASE 3
655 #define RMTIEOM 4
656 #define RMTINBSF 5
657
658 #ifndef HAVE_SYS_MTIO_H
659 LOCAL void
660 ioctape(cmd)
661 int cmd;
662 {
663 char count[CMD_SIZE];
664 char opcode[CMD_SIZE];
665
666 readarg(opcode, sizeof (opcode));
667 readarg(count, sizeof (count));
668 DEBUG3("rmtd: %c %s %s\n", cmd, opcode, count);
669 if (atoi(opcode) == RMTIVERSION) {
670 rmtrespond((long)RMT_VERSION, 0);
671 } else {
672 rmtrespond((long)-1, ENOTTY);
673 }
674 }
675 #else
676
677 LOCAL void
678 ioctape(cmd)
679 int cmd;
680 {
681 long ret = 0;
682 int i;
683 char count[CMD_SIZE];
684 char opcode[CMD_SIZE];
685 struct mtop mtop;
686 static BOOL version_seen = FALSE;
687
688 readarg(opcode, sizeof (opcode));
689 readarg(count, sizeof (count));
690 DEBUG3("rmtd: %c %s %s\n", cmd, opcode, count);
691 mtop.mt_op = atoi(opcode);
692 ret = atol(count);
693 mtop.mt_count = ret;
694 if (mtop.mt_count != ret) {
695 rmtrespond((long)-1, EINVAL);
696 return;
697 }
698
699 /*
700 * Only Opcodes 0..7 are unique across different architectures.
701 * But as in many cases Linux does not even follow this rule.
702 * If we know that we have been called by a VERSION 1 client,
703 * we may safely assume that the client is not using Linux mapping
704 * but the standard mapping when sending mt_op numbers over the wire.
705 */
706 ret = 0;
707 if (cmd == 'I' && version_seen && (mtop.mt_op != RMTIVERSION)) {
708 i = rmtmapold(mtop.mt_op);
709 if (i < 0) {
710 /*
711 * Should we rather give it a chance instead
712 * of aborting the command?
713 */
714 rmtrespond((long)-1, EINVAL);
715 return;
716 }
717 mtop.mt_op = i;
718 }
719 if (cmd == 'i') {
720 i = rmtmapnew(mtop.mt_op);
721 if (i < 0) {
722 ret = -1;
723 seterrno(EINVAL);
724 } else {
725 mtop.mt_op = i;
726 }
727 }
728 DEBUG4("rmtd: %c %d %ld ret: %ld (mapped)\n", cmd, mtop.mt_op,
729 (long)mtop.mt_count, ret);
730 if (ret == 0) {
731 if (mtop.mt_op == RMTIVERSION) {
732 /*
733 * Client must retrieve RMTIVERSION directly after
734 * opening the drive using the 'O' rmt command.
735 */
736 ret = mtop.mt_count = RMT_VERSION;
737 version_seen = TRUE;
738 } else {
739 ret = ioctl(tape_fd, MTIOCTOP, (char *)&mtop);
740 }
741 }
742 if (ret < 0) {
743 rmtrespond(ret, geterrno());
744 } else {
745 ret = mtop.mt_count;
746 rmtrespond(ret, geterrno());
747 }
748 }
749
750 /*
751 * Map all old /etc/rmt (over the wire) opcodes that should be in range 0..7
752 * to numbers that are understood by the local driver.
753 * This is needed because Linux does not follow the UNIX conventions and
754 * uses an incompatible opcode mapping even in the range 0..7.
755 */
756 LOCAL int
757 rmtmapold(cmd)
758 int cmd;
759 {
760 switch (cmd) {
761
762 case 0:
763 #ifdef MTWEOF
764 return (MTWEOF);
765 #else
766 return (-1);
767 #endif
768
769 case 1:
770 #ifdef MTFSF
771 return (MTFSF);
772 #else
773 return (-1);
774 #endif
775
776 case 2:
777 #ifdef MTBSF
778 return (MTBSF);
779 #else
780 return (-1);
781 #endif
782
783 case 3:
784 #ifdef MTFSR
785 return (MTFSR);
786 #else
787 return (-1);
788 #endif
789
790 case 4:
791 #ifdef MTBSR
792 return (MTBSR);
793 #else
794 return (-1);
795 #endif
796
797 case 5:
798 #ifdef MTREW
799 return (MTREW);
800 #else
801 return (-1);
802 #endif
803
804 case 6:
805 #ifdef MTOFFL
806 return (MTOFFL);
807 #else
808 return (-1);
809 #endif
810
811 case 7:
812 #ifdef MTNOP
813 return (MTNOP);
814 #else
815 return (-1);
816 #endif
817
818 }
819 return (-1);
820 }
821
822 /*
823 * Map all new /etc/rmt (over the wire) opcodes from 'i' command
824 * to numbers that are understood by the local driver.
825 */
826 LOCAL int
827 rmtmapnew(cmd)
828 int cmd;
829 {
830 switch (cmd) {
831
832 #ifdef MTCACHE
833 case RMTICACHE: return (MTCACHE);
834 #endif
835 #ifdef MTNOCACHE
836 case RMTINOCACHE: return (MTNOCACHE);
837 #endif
838 #ifdef MTRETEN
839 case RMTIRETEN: return (MTRETEN);
840 #endif
841 #ifdef MTERASE
842 case RMTIERASE: return (MTERASE);
843 #endif
844 #ifdef MTEOM
845 case RMTIEOM: return (MTEOM);
846 #endif
847 #ifdef MTNBSF
848 case RMTINBSF: return (MTNBSF);
849 #endif
850 }
851 return (-1);
852 }
853 #endif
854
855 /*
856 * Old MTIOCGET copies a binary version of struct mtget back
857 * over the wire. This is highly non portable.
858 * MTS_* retrieves ascii versions (%d format) of a single
859 * field in the struct mtget.
860 * NOTE: MTS_ERREG may only be valid on the first call and
861 * must be retrived first.
862 */
863 #define MTS_TYPE 'T' /* mtget.mt_type */
864 #define MTS_DSREG 'D' /* mtget.mt_dsreg */
865 #define MTS_ERREG 'E' /* mtget.mt_erreg */
866 #define MTS_RESID 'R' /* mtget.mt_resid */
867 #define MTS_FILENO 'F' /* mtget.mt_fileno */
868 #define MTS_BLKNO 'B' /* mtget.mt_blkno */
869 #define MTS_FLAGS 'f' /* mtget.mt_flags */
870 #define MTS_BF 'b' /* mtget.mt_bf */
871
872 #ifndef HAVE_SYS_MTIO_H
873 LOCAL void
874 statustape(cmd)
875 int cmd;
876 {
877 char subcmd;
878
879 if (cmd == 's') {
880 if (readchar(&subcmd) != 1)
881 return;
882 DEBUG2("rmtd: %c%c\n", cmd, subcmd);
883 } else {
884 DEBUG1("rmtd: %c\n", cmd);
885 }
886 rmtrespond((long)-1, ENOTTY);
887 }
888 #else
889
890 LOCAL void
891 statustape(cmd)
892 int cmd;
893 {
894 int ret;
895 char subcmd;
896 struct mtget mtget;
897
898 /*
899 * Only the first three fields of the struct mtget (mt_type, mt_dsreg
900 * and mt_erreg) are identical on all platforms. The original struct
901 * mtget is 16 bytes. All client implementations except the one from
902 * star will overwrite other data and probably die if the remote struct
903 * mtget is bigger than the local one.
904 * In addition, there are byte order problems.
905 */
906 if (cmd == 's') {
907 if (readchar(&subcmd) != 1)
908 return;
909 DEBUG2("rmtd: %c%c\n", cmd, subcmd);
910 } else {
911 DEBUG1("rmtd: %c\n", cmd);
912 }
913 ret = ioctl(tape_fd, MTIOCGET, (char *)&mtget);
914 if (ret < 0) {
915 rmtrespond((long)ret, geterrno());
916 } else {
917 if (cmd == 's') switch (subcmd) {
918
919 #ifdef HAVE_MTGET_TYPE
920 case MTS_TYPE:
921 rmtrespond(mtget.mt_type, geterrno()); break;
922 #endif
923 #ifdef HAVE_MTGET_DSREG
924 case MTS_DSREG:
925 rmtrespond(mtget.mt_dsreg, geterrno()); break;
926 #endif
927 #ifdef HAVE_MTGET_ERREG
928 /*
929 * This must be retrieved first, as it contains an error
930 * code that is cleared after issuing the ioctl().
931 */
932 case MTS_ERREG:
933 rmtrespond(mtget.mt_erreg, geterrno()); break;
934 #endif
935 #ifdef HAVE_MTGET_RESID
936 case MTS_RESID:
937 rmtrespond(mtget.mt_resid, geterrno()); break;
938 #endif
939 #ifdef HAVE_MTGET_FILENO
940 case MTS_FILENO:
941 rmtrespond(mtget.mt_fileno, geterrno()); break;
942 #endif
943 #ifdef HAVE_MTGET_BLKNO
944 case MTS_BLKNO:
945 rmtrespond(mtget.mt_blkno, geterrno()); break;
946 #endif
947 #ifdef HAVE_MTGET_FLAGS
948 case MTS_FLAGS:
949 rmtrespond(mtget.mt_flags, geterrno()); break;
950 #endif
951 #ifdef HAVE_MTGET_BF
952 case MTS_BF:
953 rmtrespond(mtget.mt_bf, geterrno()); break;
954 #endif
955 default:
956 rmtrespond((long)-1, EINVAL); break;
957 } else {
958 /*
959 * Do not expect that this interface makes any sense.
960 * With UNIX, you may at least trust the first two
961 * struct members, but Linux is completely incompatible
962 */
963 ret = sizeof (mtget);
964 rmtrespond((long)ret, geterrno());
965 (void) _nixwrite(STDOUT_FILENO, (char *)&mtget,
966 sizeof (mtget));
967 }
968 }
969 }
970 #endif
971
972 LOCAL void
973 seektape()
974 {
975 off_t ret;
976 char count[CMD_SIZE];
977 char whence[CMD_SIZE];
978 Llong offset = (Llong)0;
979 int iwhence;
980
981 readarg(count, sizeof (count));
982 readarg(whence, sizeof (whence));
983 DEBUG2("rmtd: L %s %s\n", count, whence);
984 (void) astoll(count, &offset);
985 iwhence = atoi(whence);
986 switch (iwhence) {
987
988 case 0: iwhence = SEEK_SET; break;
989 case 1: iwhence = SEEK_CUR; break;
990 case 2: iwhence = SEEK_END; break;
991 #ifdef SEEK_DATA
992 case 3: iwhence = SEEK_DATA; break;
993 #endif
994 #ifdef SEEK_HOLE
995 case 4: iwhence = SEEK_HOLE; break;
996 #endif
997
998 default:
999 DEBUG1("rmtd: Illegal lseek() whence %d\n", iwhence);
1000 rmtrespond((long)-1, EINVAL);
1001 return;
1002 }
1003 ret = (off_t)offset;
1004 if (ret != offset) {
1005 DEBUG1("rmtd: Illegal seek offset %lld\n", offset);
1006 rmtrespond((long)-1, EINVAL);
1007 return;
1008 }
1009 ret = lseek(tape_fd, (off_t)offset, iwhence);
1010 if ((ret != (off_t)-1) && (sizeof (ret) > sizeof (long))) {
1011 DEBUG1("rmtd: A %lld\n", (Llong)ret);
1012 (void) js_snprintf(count, sizeof (count), "A%lld\n",
1013 (Llong)ret);
1014 (void) _nixwrite(STDOUT_FILENO, count, strlen(count));
1015 return;
1016 }
1017 rmtrespond((long)ret, geterrno());
1018 }
1019
1020 LOCAL void
1021 doversion()
1022 {
1023 char arg[CMD_SIZE];
1024
1025 readarg(arg, sizeof (arg)); /* We may like to add an arg later */
1026 DEBUG1("rmtd: v %s\n", arg);
1027 rmtrespond((long)RMT_VERSION, 0);
1028 }
1029
1030 #define READB_SIZE 128
1031 LOCAL char readb[READB_SIZE];
1032 LOCAL char *readbptr;
1033 LOCAL int readbcnt;
1034
1035 LOCAL int
1036 fillrdbuf()
1037 {
1038 readbptr = readb;
1039
1040 return (readbcnt = _niread(STDIN_FILENO, readb, READB_SIZE));
1041 }
1042
1043 /*
1044 * This function is used for error recovery, it thus may be slow.
1045 * We try to fill the read buffer in case there is something to read.
1046 * We will not block here, if the OS does not support O_NONBLOCK we
1047 * will just do nothing.
1048 */
1049 LOCAL void
1050 tryfillrdbuf()
1051 {
1052 #if defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK)
1053 int fl;
1054
1055 fl = fcntl(STDIN_FILENO, F_GETFL, 0);
1056 fcntl(STDIN_FILENO, F_SETFL, fl|O_NONBLOCK);
1057
1058 fillrdbuf();
1059
1060 fcntl(STDIN_FILENO, F_SETFL, fl);
1061 #endif
1062 }
1063
1064 LOCAL int
1065 readchar(cp)
1066 char *cp;
1067 {
1068 if (--readbcnt < 0) {
1069 if (fillrdbuf() <= 0)
1070 return (readbcnt);
1071 --readbcnt;
1072 }
1073 *cp = *readbptr++;
1074 return (1);
1075 }
1076
1077 LOCAL void
1078 readbuf(buf, n)
1079 register char *buf;
1080 register int n;
1081 {
1082 register int i = 0;
1083 register int amt;
1084
1085 if (readbcnt > 0) {
1086 amt = readbcnt;
1087 if (amt > n)
1088 amt = n;
1089 movebytes(readbptr, buf, amt);
1090 readbptr += amt;
1091 readbcnt -= amt;
1092 i += amt;
1093 }
1094
1095 for (; i < n; i += amt) {
1096 amt = _niread(STDIN_FILENO, &buf[i], n - i);
1097 if (amt <= 0) {
1098 DEBUG("rmtd: premature eof\n");
1099 rmterror("Premature eof");
1100 exit(EX_BAD);
1101 }
1102 }
1103 }
1104
1105 LOCAL int
1106 readarg(buf, n)
1107 char *buf;
1108 int n;
1109 {
1110 int i;
1111 char c;
1112
1113 for (i = 0; i < n; i++) {
1114 if (readchar(&buf[i]) != 1)
1115 exit(0);
1116 if (buf[i] == '\n')
1117 break;
1118 }
1119 if (buf[i] == '\n') {
1120 buf[i] = '\0';
1121 return (--i); /* Do not include null byte */
1122 }
1123 buf[n-1] = '\0';
1124
1125 /*
1126 * The following code is for error recovery.
1127 * We come here if the client send us too long parameters.
1128 * We try to recover from the problem by reading a reasonable
1129 * amount of data in hope to find the newline which is the
1130 * argument terminator.
1131 */
1132 if (readbcnt <= 0)
1133 tryfillrdbuf();
1134 for (i = 0; readbcnt > 0 && i < 10000; i++) {
1135 if (readchar(&c) != 1)
1136 exit(0);
1137 if (c == '\n')
1138 break;
1139 if (readbcnt <= 0)
1140 tryfillrdbuf();
1141 }
1142 return (n);
1143 }
1144
1145 LOCAL char *
1146 preparebuffer(size)
1147 int size;
1148 {
1149 static char *buffer = 0;
1150 static int buffersize = 0;
1151
1152 if (buffer != 0 && size <= buffersize)
1153 return (buffer);
1154 if (buffer != 0)
1155 free(buffer);
1156 buffer = malloc(size);
1157 if (buffer == 0) {
1158 DEBUG("rmtd: cannot allocate buffer space\n");
1159 rmterror("Cannot allocate buffer space");
1160 exit(EX_BAD);
1161 }
1162 buffersize = size;
1163
1164 #ifdef SO_SNDBUF
1165 while (size > 512 &&
1166 setsockopt(STDOUT_FILENO, SOL_SOCKET, SO_SNDBUF,
1167 (char *)&size, sizeof (size)) < 0)
1168 size -= 512;
1169 DEBUG1("rmtd: sndsize: %d\n", size);
1170 #endif
1171 #ifdef SO_RCVBUF
1172 while (size > 512 &&
1173 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_RCVBUF,
1174 (char *)&size, sizeof (size)) < 0)
1175 size -= 512;
1176 DEBUG1("rmtd: rcvsize: %d\n", size);
1177 #endif
1178 return (buffer);
1179 }
1180
1181 /*
1182 * If we are not root and there is no /etc/default/rmt
1183 * we will only allow to access files in /dev.
1184 * We do this because we may assume that non-root access to files
1185 * in /dev is only granted if it does not open security holes.
1186 * Accessing files (e.g. /etc/passwd) is not possible.
1187 * Otherwise permissions depend on the content of /etc/default/rmt.
1188 */
1189 LOCAL int
1190 checktape(device)
1191 char *device;
1192 {
1193 if (!found_dfltfile) {
1194 if (has_dotdot(device))
1195 return (0);
1196 if (strncmp(device, "/dev/", 5) == 0)
1197 return (1);
1198 return (0);
1199 }
1200 return (checkaccess(device));
1201 }
1202
1203 LOCAL BOOL
1204 has_dotdot(name)
1205 char *name;
1206 {
1207 register char *p = name;
1208
1209 while (*p) {
1210 if ((p[0] == '.' && p[1] == '.') &&
1211 (p[2] == '/' || p[2] == '\0')) {
1212 return (TRUE);
1213 }
1214 do {
1215 if (*p++ == '\0')
1216 return (FALSE);
1217 } while (*p != '/');
1218 p++;
1219 while (*p == '/') /* Skip multiple slashes */
1220 p++;
1221 }
1222 return (FALSE);
1223 }
1224 LOCAL void
1225 rmtrespond(ret, err)
1226 long ret;
1227 int err;
1228 {
1229 char rbuf[2*CMD_SIZE];
1230
1231 if (ret >= 0) {
1232 DEBUG1("rmtd: A %ld\n", ret);
1233 (void) js_snprintf(rbuf, sizeof (rbuf), "A%ld\n", ret);
1234 } else {
1235 DEBUG2("rmtd: E %d (%s)\n", err, errmsgstr(err));
1236 (void) js_snprintf(rbuf, sizeof (rbuf), "E%d\n%s\n", err,
1237 errmsgstr(err));
1238 }
1239 (void) _nixwrite(STDOUT_FILENO, rbuf, strlen(rbuf));
1240 }
1241
1242 LOCAL void
1243 rmterror(str)
1244 char *str;
1245 {
1246 char rbuf[2*CMD_SIZE];
1247
1248 DEBUG1("rmtd: E 0 (%s)\n", str);
1249 (void) js_snprintf(rbuf, sizeof (rbuf), "E0\n%s\n", str);
1250 (void) _nixwrite(STDOUT_FILENO, rbuf, strlen(rbuf));
1251 }
1252 #endif /* USE_REMOTE */
1253