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