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