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