xref: /netbsd-src/libexec/tftpd/tftpd.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
37  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 /*static char sccsid[] = "from: @(#)tftpd.c	5.13 (Berkeley) 2/26/91";*/
42 static char rcsid[] = "$Id: tftpd.c,v 1.7 1995/06/04 20:48:22 jtc Exp $";
43 #endif /* not lint */
44 
45 /*
46  * Trivial file transfer protocol server.
47  *
48  * This version includes many modifications by Jim Guyton <guyton@rand-unix>
49  */
50 
51 #include <sys/types.h>
52 #include <sys/ioctl.h>
53 #include <sys/stat.h>
54 #include <signal.h>
55 #include <fcntl.h>
56 
57 #include <sys/socket.h>
58 #include <netinet/in.h>
59 #include <arpa/tftp.h>
60 #include <netdb.h>
61 
62 #include <setjmp.h>
63 #include <syslog.h>
64 #include <stdio.h>
65 #include <errno.h>
66 #include <ctype.h>
67 #include <string.h>
68 #include <stdlib.h>
69 
70 /* XXX svr4 defines UID_NOBODY and GID_NOBODY constants in <sys/param.h> */
71 #define UID_NOBODY	32767
72 #define GID_NOBODY	32766
73 
74 #define	TIMEOUT		5
75 
76 extern	int errno;
77 extern	char *__progname;
78 struct	sockaddr_in s_in = { AF_INET };
79 int	peer;
80 int	rexmtval = TIMEOUT;
81 int	maxtimeout = 5*TIMEOUT;
82 
83 #define	PKTSIZE	SEGSIZE+4
84 char	buf[PKTSIZE];
85 char	ackbuf[PKTSIZE];
86 struct	sockaddr_in from;
87 int	fromlen;
88 
89 #define MAXARG	4
90 char	*dirs[MAXARG+1];
91 
92 int	secure = 0;
93 
94 static void
95 usage()
96 {
97 	syslog(LOG_ERR, "Usage: %s [-s] [directory ...]\n", __progname);
98 	exit(1);
99 }
100 
101 main(argc, argv)
102 	int    argc;
103 	char **argv;
104 {
105 	register struct tftphdr *tp;
106 	register int n = 0;
107 	int on = 1;
108 	int fd = 0;
109 	int c;
110 
111 	openlog("tftpd", LOG_PID, LOG_DAEMON);
112 
113 	while ((c = getopt(argc, argv, "s")) != -1)
114 		switch (c) {
115 		case 's':
116 			secure = 1;
117 			break;
118 
119 		default:
120 			usage();
121 			break;
122 		}
123 
124 	for (; optind != argc; optind++) {
125 		if (!secure) {
126 			if (n >= MAXARG) {
127 				syslog(LOG_ERR, "too many directories\n");
128 				exit(1);
129 			} else
130 				dirs[n++] = argv[optind];
131 		}
132 		if (chdir(argv[optind])) {
133 			syslog(LOG_ERR, "%s: %m\n", argv[optind]);
134 			exit(1);
135 		}
136 	}
137 
138 	if (secure && chroot(".")) {
139 		syslog(LOG_ERR, "chroot: %m\n");
140 		exit(1);
141 	}
142 
143 	if (setgid(GID_NOBODY)) {
144 		syslog(LOG_ERR, "setgid: %m");
145 		exit(1);
146 	}
147 
148 	if (setuid(UID_NOBODY)) {
149 		syslog(LOG_ERR, "setuid: %m");
150 		exit(1);
151 	}
152 
153 	if (ioctl(fd, FIONBIO, &on) < 0) {
154 		syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
155 		exit(1);
156 	}
157 	fromlen = sizeof (from);
158 	n = recvfrom(fd, buf, sizeof (buf), 0,
159 	    (struct sockaddr *)&from, &fromlen);
160 	if (n < 0) {
161 		syslog(LOG_ERR, "recvfrom: %m\n");
162 		exit(1);
163 	}
164 	/*
165 	 * Now that we have read the message out of the UDP
166 	 * socket, we fork and exit.  Thus, inetd will go back
167 	 * to listening to the tftp port, and the next request
168 	 * to come in will start up a new instance of tftpd.
169 	 *
170 	 * We do this so that inetd can run tftpd in "wait" mode.
171 	 * The problem with tftpd running in "nowait" mode is that
172 	 * inetd may get one or more successful "selects" on the
173 	 * tftp port before we do our receive, so more than one
174 	 * instance of tftpd may be started up.  Worse, if tftpd
175 	 * break before doing the above "recvfrom", inetd would
176 	 * spawn endless instances, clogging the system.
177 	 */
178 	{
179 		int pid;
180 		int i, j;
181 
182 		for (i = 1; i < 20; i++) {
183 		    pid = fork();
184 		    if (pid < 0) {
185 				sleep(i);
186 				/*
187 				 * flush out to most recently sent request.
188 				 *
189 				 * This may drop some request, but those
190 				 * will be resent by the clients when
191 				 * they timeout.  The positive effect of
192 				 * this flush is to (try to) prevent more
193 				 * than one tftpd being started up to service
194 				 * a single request from a single client.
195 				 */
196 				j = sizeof from;
197 				i = recvfrom(fd, buf, sizeof (buf), 0,
198 				    (struct sockaddr *)&from, &j);
199 				if (i > 0) {
200 					n = i;
201 					fromlen = j;
202 				}
203 		    } else {
204 				break;
205 		    }
206 		}
207 		if (pid < 0) {
208 			syslog(LOG_ERR, "fork: %m\n");
209 			exit(1);
210 		} else if (pid != 0) {
211 			exit(0);
212 		}
213 	}
214 	from.sin_len = sizeof(struct sockaddr_in);
215 	from.sin_family = AF_INET;
216 	alarm(0);
217 	close(fd);
218 	close(1);
219 	peer = socket(AF_INET, SOCK_DGRAM, 0);
220 	if (peer < 0) {
221 		syslog(LOG_ERR, "socket: %m\n");
222 		exit(1);
223 	}
224 	if (bind(peer, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) {
225 		syslog(LOG_ERR, "bind: %m\n");
226 		exit(1);
227 	}
228 	if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
229 		syslog(LOG_ERR, "connect: %m\n");
230 		exit(1);
231 	}
232 	tp = (struct tftphdr *)buf;
233 	tp->th_opcode = ntohs(tp->th_opcode);
234 	if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
235 		tftp(tp, n);
236 	exit(1);
237 }
238 
239 int	validate_access();
240 int	sendfile(), recvfile();
241 
242 struct formats {
243 	char	*f_mode;
244 	int	(*f_validate)();
245 	int	(*f_send)();
246 	int	(*f_recv)();
247 	int	f_convert;
248 } formats[] = {
249 	{ "netascii",	validate_access,	sendfile,	recvfile, 1 },
250 	{ "octet",	validate_access,	sendfile,	recvfile, 0 },
251 #ifdef notdef
252 	{ "mail",	validate_user,		sendmail,	recvmail, 1 },
253 #endif
254 	{ 0 }
255 };
256 
257 /*
258  * Handle initial connection protocol.
259  */
260 tftp(tp, size)
261 	struct tftphdr *tp;
262 	int size;
263 {
264 	register char *cp;
265 	int first = 1, ecode;
266 	register struct formats *pf;
267 	char *filename, *mode;
268 
269 	filename = cp = tp->th_stuff;
270 again:
271 	while (cp < buf + size) {
272 		if (*cp == '\0')
273 			break;
274 		cp++;
275 	}
276 	if (*cp != '\0') {
277 		nak(EBADOP);
278 		exit(1);
279 	}
280 	if (first) {
281 		mode = ++cp;
282 		first = 0;
283 		goto again;
284 	}
285 	for (cp = mode; *cp; cp++)
286 		if (isupper(*cp))
287 			*cp = tolower(*cp);
288 	for (pf = formats; pf->f_mode; pf++)
289 		if (strcmp(pf->f_mode, mode) == 0)
290 			break;
291 	if (pf->f_mode == 0) {
292 		nak(EBADOP);
293 		exit(1);
294 	}
295 	ecode = (*pf->f_validate)(filename, tp->th_opcode);
296 	if (ecode) {
297 		nak(ecode);
298 		exit(1);
299 	}
300 	if (tp->th_opcode == WRQ)
301 		(*pf->f_recv)(pf);
302 	else
303 		(*pf->f_send)(pf);
304 	exit(0);
305 }
306 
307 
308 FILE *file;
309 
310 /*
311  * Validate file access.  Since we
312  * have no uid or gid, for now require
313  * file to exist and be publicly
314  * readable/writable.
315  * If we were invoked with arguments
316  * from inetd then the file must also be
317  * in one of the given directory prefixes.
318  * Note also, full path name must be
319  * given as we have no login directory.
320  */
321 validate_access(filename, mode)
322 	char *filename;
323 	int mode;
324 {
325 	struct stat stbuf;
326 	int	fd;
327 	char *cp, **dirp;
328 
329 	if (!secure) {
330 		if (*filename != '/')
331 			return (EACCESS);
332 		/*
333 		 * prevent tricksters from getting around the directory
334 		 * restrictions
335 		 */
336 		for (cp = filename + 1; *cp; cp++)
337 			if(*cp == '.' && strncmp(cp-1, "/../", 4) == 0)
338 				return(EACCESS);
339 		for (dirp = dirs; *dirp; dirp++)
340 			if (strncmp(filename, *dirp, strlen(*dirp)) == 0)
341 				break;
342 		if (*dirp==0 && dirp!=dirs)
343 			return (EACCESS);
344 	}
345 	if (stat(filename, &stbuf) < 0)
346 		return (errno == ENOENT ? ENOTFOUND : EACCESS);
347 	if (mode == RRQ) {
348 		if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
349 			return (EACCESS);
350 	} else {
351 		if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
352 			return (EACCESS);
353 	}
354 	fd = open(filename, mode == RRQ ? 0 : 1);
355 	if (fd < 0)
356 		return (errno + 100);
357 	file = fdopen(fd, (mode == RRQ)? "r":"w");
358 	if (file == NULL) {
359 		return errno+100;
360 	}
361 	return (0);
362 }
363 
364 int	timeout;
365 jmp_buf	timeoutbuf;
366 
367 void
368 timer()
369 {
370 
371 	timeout += rexmtval;
372 	if (timeout >= maxtimeout)
373 		exit(1);
374 	longjmp(timeoutbuf, 1);
375 }
376 
377 /*
378  * Send the requested file.
379  */
380 sendfile(pf)
381 	struct formats *pf;
382 {
383 	struct tftphdr *dp, *r_init();
384 	register struct tftphdr *ap;    /* ack packet */
385 	register int block = 1, size, n;
386 
387 	signal(SIGALRM, timer);
388 	dp = r_init();
389 	ap = (struct tftphdr *)ackbuf;
390 	do {
391 		size = readit(file, &dp, pf->f_convert);
392 		if (size < 0) {
393 			nak(errno + 100);
394 			goto abort;
395 		}
396 		dp->th_opcode = htons((u_short)DATA);
397 		dp->th_block = htons((u_short)block);
398 		timeout = 0;
399 		(void) setjmp(timeoutbuf);
400 
401 send_data:
402 		if (send(peer, dp, size + 4, 0) != size + 4) {
403 			syslog(LOG_ERR, "tftpd: write: %m\n");
404 			goto abort;
405 		}
406 		read_ahead(file, pf->f_convert);
407 		for ( ; ; ) {
408 			alarm(rexmtval);        /* read the ack */
409 			n = recv(peer, ackbuf, sizeof (ackbuf), 0);
410 			alarm(0);
411 			if (n < 0) {
412 				syslog(LOG_ERR, "tftpd: read: %m\n");
413 				goto abort;
414 			}
415 			ap->th_opcode = ntohs((u_short)ap->th_opcode);
416 			ap->th_block = ntohs((u_short)ap->th_block);
417 
418 			if (ap->th_opcode == ERROR)
419 				goto abort;
420 
421 			if (ap->th_opcode == ACK) {
422 				if (ap->th_block == block) {
423 					break;
424 				}
425 				/* Re-synchronize with the other side */
426 				(void) synchnet(peer);
427 				if (ap->th_block == (block -1)) {
428 					goto send_data;
429 				}
430 			}
431 
432 		}
433 		block++;
434 	} while (size == SEGSIZE);
435 abort:
436 	(void) fclose(file);
437 }
438 
439 void
440 justquit()
441 {
442 	exit(0);
443 }
444 
445 
446 /*
447  * Receive a file.
448  */
449 recvfile(pf)
450 	struct formats *pf;
451 {
452 	struct tftphdr *dp, *w_init();
453 	register struct tftphdr *ap;    /* ack buffer */
454 	register int block = 0, n, size;
455 
456 	signal(SIGALRM, timer);
457 	dp = w_init();
458 	ap = (struct tftphdr *)ackbuf;
459 	do {
460 		timeout = 0;
461 		ap->th_opcode = htons((u_short)ACK);
462 		ap->th_block = htons((u_short)block);
463 		block++;
464 		(void) setjmp(timeoutbuf);
465 send_ack:
466 		if (send(peer, ackbuf, 4, 0) != 4) {
467 			syslog(LOG_ERR, "tftpd: write: %m\n");
468 			goto abort;
469 		}
470 		write_behind(file, pf->f_convert);
471 		for ( ; ; ) {
472 			alarm(rexmtval);
473 			n = recv(peer, dp, PKTSIZE, 0);
474 			alarm(0);
475 			if (n < 0) {            /* really? */
476 				syslog(LOG_ERR, "tftpd: read: %m\n");
477 				goto abort;
478 			}
479 			dp->th_opcode = ntohs((u_short)dp->th_opcode);
480 			dp->th_block = ntohs((u_short)dp->th_block);
481 			if (dp->th_opcode == ERROR)
482 				goto abort;
483 			if (dp->th_opcode == DATA) {
484 				if (dp->th_block == block) {
485 					break;   /* normal */
486 				}
487 				/* Re-synchronize with the other side */
488 				(void) synchnet(peer);
489 				if (dp->th_block == (block-1))
490 					goto send_ack;          /* rexmit */
491 			}
492 		}
493 		/*  size = write(file, dp->th_data, n - 4); */
494 		size = writeit(file, &dp, n - 4, pf->f_convert);
495 		if (size != (n-4)) {                    /* ahem */
496 			if (size < 0) nak(errno + 100);
497 			else nak(ENOSPACE);
498 			goto abort;
499 		}
500 	} while (size == SEGSIZE);
501 	write_behind(file, pf->f_convert);
502 	(void) fclose(file);            /* close data file */
503 
504 	ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
505 	ap->th_block = htons((u_short)(block));
506 	(void) send(peer, ackbuf, 4, 0);
507 
508 	signal(SIGALRM, justquit);      /* just quit on timeout */
509 	alarm(rexmtval);
510 	n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
511 	alarm(0);
512 	if (n >= 4 &&                   /* if read some data */
513 	    dp->th_opcode == DATA &&    /* and got a data block */
514 	    block == dp->th_block) {	/* then my last ack was lost */
515 		(void) send(peer, ackbuf, 4, 0);     /* resend final ack */
516 	}
517 abort:
518 	return;
519 }
520 
521 struct errmsg {
522 	int	e_code;
523 	char	*e_msg;
524 } errmsgs[] = {
525 	{ EUNDEF,	"Undefined error code" },
526 	{ ENOTFOUND,	"File not found" },
527 	{ EACCESS,	"Access violation" },
528 	{ ENOSPACE,	"Disk full or allocation exceeded" },
529 	{ EBADOP,	"Illegal TFTP operation" },
530 	{ EBADID,	"Unknown transfer ID" },
531 	{ EEXISTS,	"File already exists" },
532 	{ ENOUSER,	"No such user" },
533 	{ -1,		0 }
534 };
535 
536 /*
537  * Send a nak packet (error message).
538  * Error code passed in is one of the
539  * standard TFTP codes, or a UNIX errno
540  * offset by 100.
541  */
542 nak(error)
543 	int error;
544 {
545 	register struct tftphdr *tp;
546 	int length;
547 	register struct errmsg *pe;
548 
549 	tp = (struct tftphdr *)buf;
550 	tp->th_opcode = htons((u_short)ERROR);
551 	tp->th_code = htons((u_short)error);
552 	for (pe = errmsgs; pe->e_code >= 0; pe++)
553 		if (pe->e_code == error)
554 			break;
555 	if (pe->e_code < 0) {
556 		pe->e_msg = strerror(error - 100);
557 		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
558 	}
559 	strcpy(tp->th_msg, pe->e_msg);
560 	length = strlen(pe->e_msg);
561 	tp->th_msg[length] = '\0';
562 	length += 5;
563 	if (send(peer, buf, length, 0) != length)
564 		syslog(LOG_ERR, "nak: %m\n");
565 }
566