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