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