xref: /original-bsd/libexec/tftpd/tftpd.c (revision 74639b6d)
1 /*
2  * Copyright (c) 1983, 1993 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1983, 1993 Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)tftpd.c	5.16 (Berkeley) 06/04/93";
16 #endif /* not lint */
17 
18 /*
19  * Trivial file transfer protocol server.
20  *
21  * This version includes many modifications by Jim Guyton
22  * <guyton@rand-unix>.
23  */
24 
25 #include <sys/param.h>
26 #include <sys/ioctl.h>
27 #include <sys/stat.h>
28 #include <sys/socket.h>
29 
30 #include <netinet/in.h>
31 #include <arpa/tftp.h>
32 #include <arpa/inet.h>
33 
34 #include <ctype.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <netdb.h>
38 #include <setjmp.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <syslog.h>
44 #include <unistd.h>
45 
46 #include "tftpsubs.h"
47 
48 #define	TIMEOUT		5
49 
50 int	peer;
51 int	rexmtval = TIMEOUT;
52 int	maxtimeout = 5*TIMEOUT;
53 
54 #define	PKTSIZE	SEGSIZE+4
55 char	buf[PKTSIZE];
56 char	ackbuf[PKTSIZE];
57 struct	sockaddr_in from;
58 int	fromlen;
59 
60 void	tftp __P((struct tftphdr *, int));
61 
62 /*
63  * Null-terminated directory prefix list for absolute pathname requests and
64  * search list for relative pathname requests.
65  *
66  * MAXDIRS should be at least as large as the number of arguments that
67  * inetd allows (currently 20).
68  */
69 #define MAXDIRS	20
70 static struct dirlist {
71 	char	*name;
72 	int	len;
73 } dirs[MAXDIRS+1];
74 static int	suppress_naks;
75 static int	logging;
76 
77 static char *errtomsg __P((int));
78 static void  nak __P((int));
79 static char *verifyhost __P((struct sockaddr_in *));
80 
81 int
82 main(argc, argv)
83 	int argc;
84 	char *argv[];
85 {
86 	register struct tftphdr *tp;
87 	register int n;
88 	int ch, on;
89 	struct sockaddr_in sin;
90 
91 	openlog("tftpd", LOG_PID, LOG_FTP);
92 	while ((ch = getopt(argc, argv, "ln")) != EOF) {
93 		switch (ch) {
94 		case 'l':
95 			logging = 1;
96 			break;
97 		case 'n':
98 			suppress_naks = 1;
99 			break;
100 		default:
101 			syslog(LOG_WARNING, "ignoring unknown option -%c", ch);
102 		}
103 	}
104 	if (optind < argc) {
105 		struct dirlist *dirp;
106 
107 		/* Get list of directory prefixes. Skip relative pathnames. */
108 		for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS];
109 		     optind++) {
110 			if (argv[optind][0] == '/') {
111 				dirp->name = argv[optind];
112 				dirp->len  = strlen(dirp->name);
113 				dirp++;
114 			}
115 		}
116 	}
117 
118 	on = 1;
119 	if (ioctl(0, FIONBIO, &on) < 0) {
120 		syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
121 		exit(1);
122 	}
123 	fromlen = sizeof (from);
124 	n = recvfrom(0, buf, sizeof (buf), 0,
125 	    (struct sockaddr *)&from, &fromlen);
126 	if (n < 0) {
127 		syslog(LOG_ERR, "recvfrom: %m\n");
128 		exit(1);
129 	}
130 	/*
131 	 * Now that we have read the message out of the UDP
132 	 * socket, we fork and exit.  Thus, inetd will go back
133 	 * to listening to the tftp port, and the next request
134 	 * to come in will start up a new instance of tftpd.
135 	 *
136 	 * We do this so that inetd can run tftpd in "wait" mode.
137 	 * The problem with tftpd running in "nowait" mode is that
138 	 * inetd may get one or more successful "selects" on the
139 	 * tftp port before we do our receive, so more than one
140 	 * instance of tftpd may be started up.  Worse, if tftpd
141 	 * break before doing the above "recvfrom", inetd would
142 	 * spawn endless instances, clogging the system.
143 	 */
144 	{
145 		int pid;
146 		int i, j;
147 
148 		for (i = 1; i < 20; i++) {
149 		    pid = fork();
150 		    if (pid < 0) {
151 				sleep(i);
152 				/*
153 				 * flush out to most recently sent request.
154 				 *
155 				 * This may drop some request, but those
156 				 * will be resent by the clients when
157 				 * they timeout.  The positive effect of
158 				 * this flush is to (try to) prevent more
159 				 * than one tftpd being started up to service
160 				 * a single request from a single client.
161 				 */
162 				j = sizeof from;
163 				i = recvfrom(0, buf, sizeof (buf), 0,
164 				    (struct sockaddr *)&from, &j);
165 				if (i > 0) {
166 					n = i;
167 					fromlen = j;
168 				}
169 		    } else {
170 				break;
171 		    }
172 		}
173 		if (pid < 0) {
174 			syslog(LOG_ERR, "fork: %m\n");
175 			exit(1);
176 		} else if (pid != 0) {
177 			exit(0);
178 		}
179 	}
180 	from.sin_family = AF_INET;
181 	alarm(0);
182 	close(0);
183 	close(1);
184 	peer = socket(AF_INET, SOCK_DGRAM, 0);
185 	if (peer < 0) {
186 		syslog(LOG_ERR, "socket: %m\n");
187 		exit(1);
188 	}
189 	memset(&sin, 0, sizeof(sin));
190 	sin.sin_family = AF_INET;
191 	if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
192 		syslog(LOG_ERR, "bind: %m\n");
193 		exit(1);
194 	}
195 	if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
196 		syslog(LOG_ERR, "connect: %m\n");
197 		exit(1);
198 	}
199 	tp = (struct tftphdr *)buf;
200 	tp->th_opcode = ntohs(tp->th_opcode);
201 	if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
202 		tftp(tp, n);
203 	exit(1);
204 }
205 
206 struct formats;
207 int	validate_access __P((char **, int));
208 void	sendfile __P((struct formats *));
209 void	recvfile __P((struct formats *));
210 
211 struct formats {
212 	char	*f_mode;
213 	int	(*f_validate) __P((char **, int));
214 	void	(*f_send) __P((struct formats *));
215 	void	(*f_recv) __P((struct formats *));
216 	int	f_convert;
217 } formats[] = {
218 	{ "netascii",	validate_access,	sendfile,	recvfile, 1 },
219 	{ "octet",	validate_access,	sendfile,	recvfile, 0 },
220 #ifdef notdef
221 	{ "mail",	validate_user,		sendmail,	recvmail, 1 },
222 #endif
223 	{ 0 }
224 };
225 
226 /*
227  * Handle initial connection protocol.
228  */
229 void
230 tftp(tp, size)
231 	struct tftphdr *tp;
232 	int size;
233 {
234 	register char *cp;
235 	int first = 1, ecode;
236 	register struct formats *pf;
237 	char *filename, *mode;
238 
239 	filename = cp = tp->th_stuff;
240 again:
241 	while (cp < buf + size) {
242 		if (*cp == '\0')
243 			break;
244 		cp++;
245 	}
246 	if (*cp != '\0') {
247 		nak(EBADOP);
248 		exit(1);
249 	}
250 	if (first) {
251 		mode = ++cp;
252 		first = 0;
253 		goto again;
254 	}
255 	for (cp = mode; *cp; cp++)
256 		if (isupper(*cp))
257 			*cp = tolower(*cp);
258 	for (pf = formats; pf->f_mode; pf++)
259 		if (strcmp(pf->f_mode, mode) == 0)
260 			break;
261 	if (pf->f_mode == 0) {
262 		nak(EBADOP);
263 		exit(1);
264 	}
265 	ecode = (*pf->f_validate)(&filename, tp->th_opcode);
266 	if (logging) {
267 		syslog(LOG_INFO, "%s: %s request for %s: %s",
268 			verifyhost(&from),
269 			tp->th_opcode == WRQ ? "write" : "read",
270 			filename, errtomsg(ecode));
271 	}
272 	if (ecode) {
273 		/*
274 		 * Avoid storms of naks to a RRQ broadcast for a relative
275 		 * bootfile pathname from a diskless Sun.
276 		 */
277 		if (suppress_naks && *filename != '/' && ecode == ENOTFOUND)
278 			exit(0);
279 		nak(ecode);
280 		exit(1);
281 	}
282 	if (tp->th_opcode == WRQ)
283 		(*pf->f_recv)(pf);
284 	else
285 		(*pf->f_send)(pf);
286 	exit(0);
287 }
288 
289 
290 FILE *file;
291 
292 /*
293  * Validate file access.  Since we
294  * have no uid or gid, for now require
295  * file to exist and be publicly
296  * readable/writable.
297  * If we were invoked with arguments
298  * from inetd then the file must also be
299  * in one of the given directory prefixes.
300  * Note also, full path name must be
301  * given as we have no login directory.
302  */
303 int
304 validate_access(filep, mode)
305 	char **filep;
306 	int mode;
307 {
308 	struct stat stbuf;
309 	int	fd;
310 	struct dirlist *dirp;
311 	static char pathname[MAXPATHLEN];
312 	char *filename = *filep;
313 
314 	/*
315 	 * Prevent tricksters from getting around the directory restrictions
316 	 */
317 	if (strstr(filename, "/../"))
318 		return (EACCESS);
319 
320 	if (*filename == '/') {
321 		/*
322 		 * Allow the request if it's in one of the approved locations.
323 		 * Special case: check the null prefix ("/") by looking
324 		 * for length = 1 and relying on the arg. processing that
325 		 * it's a /.
326 		 */
327 		for (dirp = dirs; dirp->name != NULL; dirp++) {
328 			if (dirp->len == 1 ||
329 			    (!strncmp(filename, dirp->name, dirp->len) &&
330 			     filename[dirp->len] == '/'))
331 				    break;
332 		}
333 		/* If directory list is empty, allow access to any file */
334 		if (dirp->name == NULL && dirp != dirs)
335 			return (EACCESS);
336 		if (stat(filename, &stbuf) < 0)
337 			return (errno == ENOENT ? ENOTFOUND : EACCESS);
338 		if ((stbuf.st_mode & S_IFMT) != S_IFREG)
339 			return (ENOTFOUND);
340 		if (mode == RRQ) {
341 			if ((stbuf.st_mode & S_IROTH) == 0)
342 				return (EACCESS);
343 		} else {
344 			if ((stbuf.st_mode & S_IWOTH) == 0)
345 				return (EACCESS);
346 		}
347 	} else {
348 		int err;
349 
350 		/*
351 		 * Relative file name: search the approved locations for it.
352 		 * Don't allow write requests or ones that avoid directory
353 		 * restrictions.
354 		 */
355 
356 		if (mode != RRQ || !strncmp(filename, "../", 3))
357 			return (EACCESS);
358 
359 		/*
360 		 * If the file exists in one of the directories and isn't
361 		 * readable, continue looking. However, change the error code
362 		 * to give an indication that the file exists.
363 		 */
364 		err = ENOTFOUND;
365 		for (dirp = dirs; dirp->name != NULL; dirp++) {
366 			sprintf(pathname, "%s/%s", dirp->name, filename);
367 			if (stat(pathname, &stbuf) == 0 &&
368 			    (stbuf.st_mode & S_IFMT) == S_IFREG) {
369 				if ((stbuf.st_mode & S_IROTH) != 0) {
370 					break;
371 				}
372 				err = EACCESS;
373 			}
374 		}
375 		if (dirp->name == NULL)
376 			return (err);
377 		*filep = filename = pathname;
378 	}
379 	fd = open(filename, mode == RRQ ? 0 : 1);
380 	if (fd < 0)
381 		return (errno + 100);
382 	file = fdopen(fd, (mode == RRQ)? "r":"w");
383 	if (file == NULL) {
384 		return errno+100;
385 	}
386 	return (0);
387 }
388 
389 int	timeout;
390 jmp_buf	timeoutbuf;
391 
392 void
393 timer()
394 {
395 
396 	timeout += rexmtval;
397 	if (timeout >= maxtimeout)
398 		exit(1);
399 	longjmp(timeoutbuf, 1);
400 }
401 
402 /*
403  * Send the requested file.
404  */
405 void
406 sendfile(pf)
407 	struct formats *pf;
408 {
409 	struct tftphdr *dp, *r_init();
410 	register struct tftphdr *ap;    /* ack packet */
411 	register int size, n;
412 	volatile int block;
413 
414 	signal(SIGALRM, timer);
415 	dp = r_init();
416 	ap = (struct tftphdr *)ackbuf;
417 	block = 1;
418 	do {
419 		size = readit(file, &dp, pf->f_convert);
420 		if (size < 0) {
421 			nak(errno + 100);
422 			goto abort;
423 		}
424 		dp->th_opcode = htons((u_short)DATA);
425 		dp->th_block = htons((u_short)block);
426 		timeout = 0;
427 		(void)setjmp(timeoutbuf);
428 
429 send_data:
430 		if (send(peer, dp, size + 4, 0) != size + 4) {
431 			syslog(LOG_ERR, "tftpd: write: %m\n");
432 			goto abort;
433 		}
434 		read_ahead(file, pf->f_convert);
435 		for ( ; ; ) {
436 			alarm(rexmtval);        /* read the ack */
437 			n = recv(peer, ackbuf, sizeof (ackbuf), 0);
438 			alarm(0);
439 			if (n < 0) {
440 				syslog(LOG_ERR, "tftpd: read: %m\n");
441 				goto abort;
442 			}
443 			ap->th_opcode = ntohs((u_short)ap->th_opcode);
444 			ap->th_block = ntohs((u_short)ap->th_block);
445 
446 			if (ap->th_opcode == ERROR)
447 				goto abort;
448 
449 			if (ap->th_opcode == ACK) {
450 				if (ap->th_block == block)
451 					break;
452 				/* Re-synchronize with the other side */
453 				(void) synchnet(peer);
454 				if (ap->th_block == (block -1))
455 					goto send_data;
456 			}
457 
458 		}
459 		block++;
460 	} while (size == SEGSIZE);
461 abort:
462 	(void) fclose(file);
463 }
464 
465 void
466 justquit()
467 {
468 	exit(0);
469 }
470 
471 
472 /*
473  * Receive a file.
474  */
475 void
476 recvfile(pf)
477 	struct formats *pf;
478 {
479 	struct tftphdr *dp, *w_init();
480 	register struct tftphdr *ap;    /* ack buffer */
481 	register int n, size;
482 	volatile int block;
483 
484 	signal(SIGALRM, timer);
485 	dp = w_init();
486 	ap = (struct tftphdr *)ackbuf;
487 	block = 0;
488 	do {
489 		timeout = 0;
490 		ap->th_opcode = htons((u_short)ACK);
491 		ap->th_block = htons((u_short)block);
492 		block++;
493 		(void) setjmp(timeoutbuf);
494 send_ack:
495 		if (send(peer, ackbuf, 4, 0) != 4) {
496 			syslog(LOG_ERR, "tftpd: write: %m\n");
497 			goto abort;
498 		}
499 		write_behind(file, pf->f_convert);
500 		for ( ; ; ) {
501 			alarm(rexmtval);
502 			n = recv(peer, dp, PKTSIZE, 0);
503 			alarm(0);
504 			if (n < 0) {            /* really? */
505 				syslog(LOG_ERR, "tftpd: read: %m\n");
506 				goto abort;
507 			}
508 			dp->th_opcode = ntohs((u_short)dp->th_opcode);
509 			dp->th_block = ntohs((u_short)dp->th_block);
510 			if (dp->th_opcode == ERROR)
511 				goto abort;
512 			if (dp->th_opcode == DATA) {
513 				if (dp->th_block == block) {
514 					break;   /* normal */
515 				}
516 				/* Re-synchronize with the other side */
517 				(void) synchnet(peer);
518 				if (dp->th_block == (block-1))
519 					goto send_ack;          /* rexmit */
520 			}
521 		}
522 		/*  size = write(file, dp->th_data, n - 4); */
523 		size = writeit(file, &dp, n - 4, pf->f_convert);
524 		if (size != (n-4)) {                    /* ahem */
525 			if (size < 0) nak(errno + 100);
526 			else nak(ENOSPACE);
527 			goto abort;
528 		}
529 	} while (size == SEGSIZE);
530 	write_behind(file, pf->f_convert);
531 	(void) fclose(file);            /* close data file */
532 
533 	ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
534 	ap->th_block = htons((u_short)(block));
535 	(void) send(peer, ackbuf, 4, 0);
536 
537 	signal(SIGALRM, justquit);      /* just quit on timeout */
538 	alarm(rexmtval);
539 	n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
540 	alarm(0);
541 	if (n >= 4 &&                   /* if read some data */
542 	    dp->th_opcode == DATA &&    /* and got a data block */
543 	    block == dp->th_block) {	/* then my last ack was lost */
544 		(void) send(peer, ackbuf, 4, 0);     /* resend final ack */
545 	}
546 abort:
547 	return;
548 }
549 
550 struct errmsg {
551 	int	e_code;
552 	char	*e_msg;
553 } errmsgs[] = {
554 	{ EUNDEF,	"Undefined error code" },
555 	{ ENOTFOUND,	"File not found" },
556 	{ EACCESS,	"Access violation" },
557 	{ ENOSPACE,	"Disk full or allocation exceeded" },
558 	{ EBADOP,	"Illegal TFTP operation" },
559 	{ EBADID,	"Unknown transfer ID" },
560 	{ EEXISTS,	"File already exists" },
561 	{ ENOUSER,	"No such user" },
562 	{ -1,		0 }
563 };
564 
565 static char *
566 errtomsg(error)
567 	int error;
568 {
569 	static char buf[20];
570 	register struct errmsg *pe;
571 	if (error == 0)
572 		return "success";
573 	for (pe = errmsgs; pe->e_code >= 0; pe++)
574 		if (pe->e_code == error)
575 			return pe->e_msg;
576 	sprintf(buf, "error %d", error);
577 	return buf;
578 }
579 
580 /*
581  * Send a nak packet (error message).
582  * Error code passed in is one of the
583  * standard TFTP codes, or a UNIX errno
584  * offset by 100.
585  */
586 static void
587 nak(error)
588 	int error;
589 {
590 	register struct tftphdr *tp;
591 	int length;
592 	register struct errmsg *pe;
593 
594 	tp = (struct tftphdr *)buf;
595 	tp->th_opcode = htons((u_short)ERROR);
596 	tp->th_code = htons((u_short)error);
597 	for (pe = errmsgs; pe->e_code >= 0; pe++)
598 		if (pe->e_code == error)
599 			break;
600 	if (pe->e_code < 0) {
601 		pe->e_msg = strerror(error - 100);
602 		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
603 	}
604 	strcpy(tp->th_msg, pe->e_msg);
605 	length = strlen(pe->e_msg);
606 	tp->th_msg[length] = '\0';
607 	length += 5;
608 	if (send(peer, buf, length, 0) != length)
609 		syslog(LOG_ERR, "nak: %m\n");
610 }
611 
612 static char *
613 verifyhost(fromp)
614 	struct sockaddr_in *fromp;
615 {
616 	struct hostent *hp;
617 
618 	hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (fromp->sin_addr),
619 			    fromp->sin_family);
620 	if (hp)
621 		return hp->h_name;
622 	else
623 		return inet_ntoa(fromp->sin_addr);
624 }
625