]>
git.saurik.com Git - apple/network_cmds.git/blob - tftpd.tproj/tftpd.c
20146c0843212d26e64e3594d44a739920775019
2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
35 static const char copyright
[] =
36 "@(#) Copyright (c) 1983, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
42 static char sccsid
[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93";
44 static const char rcsid
[] =
45 "$FreeBSD: src/libexec/tftpd/tftpd.c,v 1.18 2001/02/02 10:52:58 asmodai Exp $";
49 * Trivial file transfer protocol server.
51 * This version includes many modifications by Jim Guyton
55 #include <sys/param.h>
56 #include <sys/ioctl.h>
58 #include <sys/socket.h>
59 #include <sys/types.h>
61 #include <netinet/in.h>
62 #include <arpa/tftp.h>
63 #include <arpa/inet.h>
83 int rexmtval
= TIMEOUT
;
84 int maxtimeout
= 5*TIMEOUT
;
86 #define PKTSIZE SEGSIZE+4
89 struct sockaddr_in from
;
92 void tftp
__P((struct tftphdr
*, int));
95 * Null-terminated directory prefix list for absolute pathname requests and
96 * search list for relative pathname requests.
98 * MAXDIRS should be at least as large as the number of arguments that
99 * inetd allows (currently 20).
102 static struct dirlist
{
106 static int suppress_naks
;
110 static char *errtomsg
__P((int));
111 static void nak
__P((int));
112 static char * __P(verifyhost(struct sockaddr_in
*));
119 register struct tftphdr
*tp
;
122 struct sockaddr_in sin
;
123 char *chroot_dir
= NULL
;
124 struct passwd
*nobody
;
125 char *chuser
= "nobody";
127 openlog("tftpd", LOG_PID
| LOG_NDELAY
, LOG_FTP
);
128 while ((ch
= getopt(argc
, argv
, "cClns:u:")) != -1) {
149 syslog(LOG_WARNING
, "ignoring unknown option -%c", ch
);
153 struct dirlist
*dirp
;
155 /* Get list of directory prefixes. Skip relative pathnames. */
156 for (dirp
= dirs
; optind
< argc
&& dirp
< &dirs
[MAXDIRS
];
158 if (argv
[optind
][0] == '/') {
159 dirp
->name
= argv
[optind
];
160 dirp
->len
= strlen(dirp
->name
);
165 else if (chroot_dir
) {
169 if (ipchroot
&& chroot_dir
== NULL
) {
170 syslog(LOG_ERR
, "-c requires -s");
175 if (ioctl(0, FIONBIO
, &on
) < 0) {
176 syslog(LOG_ERR
, "ioctl(FIONBIO): %m");
179 fromlen
= sizeof (from
);
180 n
= recvfrom(0, buf
, sizeof (buf
), 0,
181 (struct sockaddr
*)&from
, &fromlen
);
183 syslog(LOG_ERR
, "recvfrom: %m");
187 * Now that we have read the message out of the UDP
188 * socket, we fork and exit. Thus, inetd will go back
189 * to listening to the tftp port, and the next request
190 * to come in will start up a new instance of tftpd.
192 * We do this so that inetd can run tftpd in "wait" mode.
193 * The problem with tftpd running in "nowait" mode is that
194 * inetd may get one or more successful "selects" on the
195 * tftp port before we do our receive, so more than one
196 * instance of tftpd may be started up. Worse, if tftpd
197 * break before doing the above "recvfrom", inetd would
198 * spawn endless instances, clogging the system.
204 for (i
= 1; i
< 20; i
++) {
209 * flush out to most recently sent request.
211 * This may drop some request, but those
212 * will be resent by the clients when
213 * they timeout. The positive effect of
214 * this flush is to (try to) prevent more
215 * than one tftpd being started up to service
216 * a single request from a single client.
219 i
= recvfrom(0, buf
, sizeof (buf
), 0,
220 (struct sockaddr
*)&from
, &j
);
230 syslog(LOG_ERR
, "fork: %m");
232 } else if (pid
!= 0) {
238 * Since we exit here, we should do that only after the above
239 * recvfrom to keep inetd from constantly forking should there
240 * be a problem. See the above comment about system clogging.
244 char tempchroot
[MAXPATHLEN
];
249 tempaddr
= inet_ntoa(from
.sin_addr
);
250 snprintf(tempchroot
, sizeof(tempchroot
), "%s/%s", chroot_dir
, tempaddr
);
251 statret
= stat(tempchroot
, &sb
);
252 if ((sb
.st_mode
& S_IFDIR
) &&
253 (statret
== 0 || (statret
== -1 && ipchroot
== 1)))
254 chroot_dir
= tempchroot
;
256 /* Must get this before chroot because /etc might go away */
257 if ((nobody
= getpwnam(chuser
)) == NULL
) {
258 syslog(LOG_ERR
, "%s: no such user", chuser
);
261 if (chroot(chroot_dir
)) {
262 syslog(LOG_ERR
, "chroot: %s: %m", chroot_dir
);
266 setuid(nobody
->pw_uid
);
269 from
.sin_family
= AF_INET
;
273 peer
= socket(AF_INET
, SOCK_DGRAM
, 0);
275 syslog(LOG_ERR
, "socket: %m");
278 memset(&sin
, 0, sizeof(sin
));
279 sin
.sin_family
= AF_INET
;
280 if (bind(peer
, (struct sockaddr
*)&sin
, sizeof (sin
)) < 0) {
281 syslog(LOG_ERR
, "bind: %m");
284 if (connect(peer
, (struct sockaddr
*)&from
, sizeof(from
)) < 0) {
285 syslog(LOG_ERR
, "connect: %m");
288 tp
= (struct tftphdr
*)buf
;
289 tp
->th_opcode
= ntohs(tp
->th_opcode
);
290 if (tp
->th_opcode
== RRQ
|| tp
->th_opcode
== WRQ
)
296 int validate_access
__P((char **, int));
297 void xmitfile
__P((struct formats
*));
298 void recvfile
__P((struct formats
*));
302 int (*f_validate
) __P((char **, int));
303 void (*f_send
) __P((struct formats
*));
304 void (*f_recv
) __P((struct formats
*));
307 { "netascii", validate_access
, xmitfile
, recvfile
, 1 },
308 { "octet", validate_access
, xmitfile
, recvfile
, 0 },
310 { "mail", validate_user
, sendmail
, recvmail
, 1 },
316 * Handle initial connection protocol.
324 int first
= 1, ecode
;
325 register struct formats
*pf
;
326 char *filename
, *mode
;
328 filename
= cp
= tp
->th_stuff
;
330 while (cp
< buf
+ size
) {
344 for (cp
= mode
; *cp
; cp
++)
347 for (pf
= formats
; pf
->f_mode
; pf
++)
348 if (strcmp(pf
->f_mode
, mode
) == 0)
350 if (pf
->f_mode
== 0) {
354 ecode
= (*pf
->f_validate
)(&filename
, tp
->th_opcode
);
356 syslog(LOG_INFO
, "%s: %s request for %s: %s", verifyhost(&from
),
357 tp
->th_opcode
== WRQ
? "write" : "read",
358 filename
, errtomsg(ecode
));
362 * Avoid storms of naks to a RRQ broadcast for a relative
363 * bootfile pathname from a diskless Sun.
365 if (suppress_naks
&& *filename
!= '/' && ecode
== ENOTFOUND
)
370 if (tp
->th_opcode
== WRQ
)
381 * Validate file access. Since we
382 * have no uid or gid, for now require
383 * file to exist and be publicly
385 * If we were invoked with arguments
386 * from inetd then the file must also be
387 * in one of the given directory prefixes.
388 * Note also, full path name must be
389 * given as we have no login directory.
392 validate_access(filep
, mode
)
398 struct dirlist
*dirp
;
399 static char pathname
[MAXPATHLEN
];
400 char *filename
= *filep
;
403 * Prevent tricksters from getting around the directory restrictions
405 if (strstr(filename
, "/../"))
408 if (*filename
== '/') {
410 * Allow the request if it's in one of the approved locations.
411 * Special case: check the null prefix ("/") by looking
412 * for length = 1 and relying on the arg. processing that
415 for (dirp
= dirs
; dirp
->name
!= NULL
; dirp
++) {
416 if (dirp
->len
== 1 ||
417 (!strncmp(filename
, dirp
->name
, dirp
->len
) &&
418 filename
[dirp
->len
] == '/'))
421 /* If directory list is empty, allow access to any file */
422 if (dirp
->name
== NULL
&& dirp
!= dirs
)
424 if (stat(filename
, &stbuf
) < 0)
425 return (errno
== ENOENT
? ENOTFOUND
: EACCESS
);
426 if ((stbuf
.st_mode
& S_IFMT
) != S_IFREG
)
429 if ((stbuf
.st_mode
& S_IROTH
) == 0)
432 if ((stbuf
.st_mode
& S_IWOTH
) == 0)
439 * Relative file name: search the approved locations for it.
440 * Don't allow write requests that avoid directory
444 if (!strncmp(filename
, "../", 3))
448 * If the file exists in one of the directories and isn't
449 * readable, continue looking. However, change the error code
450 * to give an indication that the file exists.
453 for (dirp
= dirs
; dirp
->name
!= NULL
; dirp
++) {
454 snprintf(pathname
, sizeof(pathname
), "%s/%s",
455 dirp
->name
, filename
);
456 if (stat(pathname
, &stbuf
) == 0 &&
457 (stbuf
.st_mode
& S_IFMT
) == S_IFREG
) {
458 if ((stbuf
.st_mode
& S_IROTH
) != 0) {
464 if (dirp
->name
== NULL
)
466 *filep
= filename
= pathname
;
468 fd
= open(filename
, mode
== RRQ
? O_RDONLY
: O_WRONLY
|O_TRUNC
);
470 return (errno
+ 100);
471 file
= fdopen(fd
, (mode
== RRQ
)? "r":"w");
486 if (timeout
>= maxtimeout
)
488 longjmp(timeoutbuf
, 1);
492 * Send the requested file.
498 struct tftphdr
*dp
, *r_init();
499 register struct tftphdr
*ap
; /* ack packet */
500 register int size
, n
;
501 volatile unsigned short block
;
503 signal(SIGALRM
, timer
);
505 ap
= (struct tftphdr
*)ackbuf
;
508 size
= readit(file
, &dp
, pf
->f_convert
);
513 dp
->th_opcode
= htons((u_short
)DATA
);
514 dp
->th_block
= htons((u_short
)block
);
516 (void)setjmp(timeoutbuf
);
519 if (send(peer
, dp
, size
+ 4, 0) != size
+ 4) {
520 syslog(LOG_ERR
, "write: %m");
523 read_ahead(file
, pf
->f_convert
);
525 alarm(rexmtval
); /* read the ack */
526 n
= recv(peer
, ackbuf
, sizeof (ackbuf
), 0);
529 syslog(LOG_ERR
, "read: %m");
532 ap
->th_opcode
= ntohs((u_short
)ap
->th_opcode
);
533 ap
->th_block
= ntohs((u_short
)ap
->th_block
);
535 if (ap
->th_opcode
== ERROR
)
538 if (ap
->th_opcode
== ACK
) {
539 if (ap
->th_block
== block
)
541 /* Re-synchronize with the other side */
542 (void) synchnet(peer
);
543 if (ap
->th_block
== (block
-1))
549 } while (size
== SEGSIZE
);
568 struct tftphdr
*dp
, *w_init();
569 register struct tftphdr
*ap
; /* ack buffer */
570 register int n
, size
;
571 volatile unsigned short block
;
573 signal(SIGALRM
, timer
);
575 ap
= (struct tftphdr
*)ackbuf
;
579 ap
->th_opcode
= htons((u_short
)ACK
);
580 ap
->th_block
= htons((u_short
)block
);
582 (void) setjmp(timeoutbuf
);
584 if (send(peer
, ackbuf
, 4, 0) != 4) {
585 syslog(LOG_ERR
, "write: %m");
588 write_behind(file
, pf
->f_convert
);
591 n
= recv(peer
, dp
, PKTSIZE
, 0);
593 if (n
< 0) { /* really? */
594 syslog(LOG_ERR
, "read: %m");
597 dp
->th_opcode
= ntohs((u_short
)dp
->th_opcode
);
598 dp
->th_block
= ntohs((u_short
)dp
->th_block
);
599 if (dp
->th_opcode
== ERROR
)
601 if (dp
->th_opcode
== DATA
) {
602 if (dp
->th_block
== block
) {
605 /* Re-synchronize with the other side */
606 (void) synchnet(peer
);
607 if (dp
->th_block
== (block
-1))
608 goto send_ack
; /* rexmit */
611 /* size = write(file, dp->th_data, n - 4); */
612 size
= writeit(file
, &dp
, n
- 4, pf
->f_convert
);
613 if (size
!= (n
-4)) { /* ahem */
614 if (size
< 0) nak(errno
+ 100);
618 } while (size
== SEGSIZE
);
619 write_behind(file
, pf
->f_convert
);
620 (void) fclose(file
); /* close data file */
622 ap
->th_opcode
= htons((u_short
)ACK
); /* send the "final" ack */
623 ap
->th_block
= htons((u_short
)(block
));
624 (void) send(peer
, ackbuf
, 4, 0);
626 signal(SIGALRM
, justquit
); /* just quit on timeout */
628 n
= recv(peer
, buf
, sizeof (buf
), 0); /* normally times out and quits */
630 if (n
>= 4 && /* if read some data */
631 dp
->th_opcode
== DATA
&& /* and got a data block */
632 block
== dp
->th_block
) { /* then my last ack was lost */
633 (void) send(peer
, ackbuf
, 4, 0); /* resend final ack */
643 { EUNDEF
, "Undefined error code" },
644 { ENOTFOUND
, "File not found" },
645 { EACCESS
, "Access violation" },
646 { ENOSPACE
, "Disk full or allocation exceeded" },
647 { EBADOP
, "Illegal TFTP operation" },
648 { EBADID
, "Unknown transfer ID" },
649 { EEXISTS
, "File already exists" },
650 { ENOUSER
, "No such user" },
659 register struct errmsg
*pe
;
662 for (pe
= errmsgs
; pe
->e_code
>= 0; pe
++)
663 if (pe
->e_code
== error
)
665 snprintf(buf
, sizeof(buf
), "error %d", error
);
670 * Send a nak packet (error message).
671 * Error code passed in is one of the
672 * standard TFTP codes, or a UNIX errno
679 register struct tftphdr
*tp
;
681 register struct errmsg
*pe
;
683 tp
= (struct tftphdr
*)buf
;
684 tp
->th_opcode
= htons((u_short
)ERROR
);
685 tp
->th_code
= htons((u_short
)error
);
686 for (pe
= errmsgs
; pe
->e_code
>= 0; pe
++)
687 if (pe
->e_code
== error
)
689 if (pe
->e_code
< 0) {
690 pe
->e_msg
= strerror(error
- 100);
691 tp
->th_code
= EUNDEF
; /* set 'undef' errorcode */
693 strcpy(tp
->th_msg
, pe
->e_msg
);
694 length
= strlen(pe
->e_msg
);
695 tp
->th_msg
[length
] = '\0';
697 if (send(peer
, buf
, length
, 0) != length
)
698 syslog(LOG_ERR
, "nak: %m");
703 struct sockaddr_in
*fromp
;
707 hp
= gethostbyaddr((char *)&fromp
->sin_addr
, sizeof(fromp
->sin_addr
),
712 return inet_ntoa(fromp
->sin_addr
);