summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhpa <hpa>2001-04-23 22:50:47 +0000
committerhpa <hpa>2001-04-23 22:50:47 +0000
commit4d0dd611aed4c0340d0a5bdf433dc12872788452 (patch)
treeae7d9682e5f3b5df84ee8f203e01790513cbc593
parentc9f28745f50560fb945c16d5cb3f5e2942d1d1f8 (diff)
downloadtftp-hpa-4d0dd611aed4c0340d0a5bdf433dc12872788452.tar.gz
Document new options and new configuration setup.tftp-hpa-0.17
-rw-r--r--README12
-rw-r--r--README.security33
-rw-r--r--tftpd/tftpd.834
-rw-r--r--tftpd/tftpd.c136
4 files changed, 136 insertions, 79 deletions
diff --git a/README b/README
index 130fd8b..4e62864 100644
--- a/README
+++ b/README
@@ -1,6 +1,9 @@
This is tftp-hpa-0.17; this version was put out by H. Peter Anvin
<hpa@zytor.com>.
+===> IMPORTANT: SEE THE FILE "README.security" FOR IMPORTANT SECURITY
+===> CHANGES IN THIS VERSION!!!!!!!!!
+
Changes in 0.17:
Add support for tcpwrapper checking (/etc/hosts.allow;
@@ -8,6 +11,15 @@ Changes in 0.17:
Compile correctly on glibc 2.1.2.
+ Add -u option to specify the user id to run as (default
+ "nobody".)
+
+ Operate in "daemon mode" as long as we keep getting requests.
+ This should speed up handling large amounts of requests at
+ once, as can happen when a client starts up, and avoids inetd
+ misconfiguration problems.
+
+
Changes in 0.16:
Correct massive lossage from 0.15: apparently 0.15 was based
diff --git a/README.security b/README.security
new file mode 100644
index 0000000..5627601
--- /dev/null
+++ b/README.security
@@ -0,0 +1,33 @@
+Starting in version 0.17, tftp-hpa operates in genuine "wait" mode,
+which means that an in.tftpd process hangs around for some time after
+the last service request has arrived. This speeds up servicing a
+subsequent request, which apparently has been a problem in the past,
+resulting in "request storms" as the client keeps retrying, resulting
+in multiple connections on the server which the client has already
+abandoned.
+
+This also means that spawning tftp via tcpd is useless (in fact, this
+indirection seems to be part of the reason for these "request
+storms.") Instead, tftp-hpa supports calling the tcpwrapper library
+directly. Thus, if your /etc/inetd.conf looks like this (all on one
+line):
+
+tftp dgram udp wait root /usr/sbin/tcpd
+/usr/sbin/in.tftpd -s /tftpboot -r blksize
+
+... it's better to change to ...
+
+tftp dgram udp wait root /usr/sbin/in.tftpd
+in.tftpd -s /tftpboot -r blksize
+
+You should make sure that you are using "wait" option in tftpd; you
+also need to have tftpd spawned as root in order for chroot (-s) to
+work. tftpd automatically drops privilege and changes user ID to
+"nobody" by default; the appropriate user ID for tftpd can be
+specified with the -u option (e.g. "-u tftpuser").
+
+If you are running a very busy boot server in a secure (firewalled!)
+configuration, you may want to compile tftpd without tcpwrapper
+support, in order to provide significantly better performance. To do
+so, specify the --without-tcpwrappers option to configure when
+compiling.
diff --git a/tftpd/tftpd.8 b/tftpd/tftpd.8
index 0d2c281..086c760 100644
--- a/tftpd/tftpd.8
+++ b/tftpd/tftpd.8
@@ -1,3 +1,4 @@
+.\" tftp-hpa: $Id$
.\" $OpenBSD: tftpd.8,v 1.7 1999/07/09 13:35:51 aaron Exp $
.\"
.\" Copyright (c) 1983, 1991 The Regents of the University of California.
@@ -34,18 +35,19 @@
.\" from: @(#)tftpd.8 6.7 (Berkeley) 5/13/91
.\" $OpenBSD: tftpd.8,v 1.7 1999/07/09 13:35:51 aaron Exp $
.\"
-.Dd July 9, 2000
+.Dd Apr 23, 2001
.Dt TFTPD 8
.Os
.Sh NAME
.Nm tftpd
.Nd
-.Tn DARPA
-Trivial File Transfer Protocol server
+IPv4 Trivial File Transfer Protocol server
.Sh SYNOPSIS
-.Nm tftpd
-.Op Fl cs
+.Nm in.tftpd
+.Op Fl c
+.Op Fl u Ar userid
.Op Fl r Ar option...
+.Op Fl s
.Op Ar directory
.Sh DESCRIPTION
.Nm
@@ -78,7 +80,10 @@ to include
all users on all hosts that can be reached through the network;
this may not be appropriate on all systems, and its implications
should be considered before enabling tftp service.
-The server should have the user ID with the lowest possible privilege.
+The server should have the user ID with the lowest possible privilege
+(see the
+.Fl u
+flag below.)
.Pp
Access to files may be restricted by invoking
.Nm
@@ -102,9 +107,16 @@ flag with a directory name,
.Nm
will
.Xr chroot 2
-on startup; therefore the remote host is not expected to pass the directory
-as part of the file name to transfer. This option is intended primarily for
-compatibility with SunOS boot ROMs which do not include a directory name.
+on startup; therefore the remote host is not expected to pass the
+directory as part of the file name to transfer. This option is
+recommended for security, as well as compatibility with boot ROMs
+which do not include a directory name.
+.Pp
+The
+.Fl u
+flag can be used to specify a user ID which
+.Nm
+will run as; the default is ``nobody''.
.Pp
This version of
.Nm
@@ -139,3 +151,7 @@ The
.Fl r
flag and RFC 2347 options were added by H. Peter Anvin based on
patches by Markus Gutschke and Gero Kulhman.
+.Pp
+The
+.Fl u
+flag was added by H. Peter Anvin.
diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c
index c154b09..9d878ab 100644
--- a/tftpd/tftpd.c
+++ b/tftpd/tftpd.c
@@ -53,6 +53,7 @@ static const char *rcsid = "tftp-hpa $Id$";
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
@@ -154,7 +155,7 @@ struct options {
static void
usage(void)
{
- syslog(LOG_ERR, "Usage: %s [-c] [-u user] [-r option...] [-s] [directory ...]",
+ syslog(LOG_ERR, "Usage: %s [-c] [-u user] [-t timeout] [-r option...] [-s] [directory ...]",
__progname);
exit(1);
}
@@ -170,16 +171,16 @@ main(int argc, char **argv)
int on = 1;
int fd = 0;
int pid;
- int i, j;
int c;
int setrv;
+ int timeout = 900; /* Default timeout */
char *user = "nobody"; /* Default user */
__progname = basename(argv[0]);
openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
- while ((c = getopt(argc, argv, "csu:r:")) != -1)
+ while ((c = getopt(argc, argv, "csu:r:t:")) != -1)
switch (c) {
case 'c':
cancreate = 1;
@@ -187,6 +188,9 @@ main(int argc, char **argv)
case 's':
secure = 1;
break;
+ case 't':
+ timeout = atoi(optarg);
+ break;
case 'u':
user = optarg;
break;
@@ -248,37 +252,69 @@ main(int argc, char **argv)
exit(1);
}
- fromlen = sizeof (from);
- n = myrecvfrom(fd, buf, sizeof (buf), 0,
- (struct sockaddr *)&from, &fromlen,
- &myaddr);
- if (n < 0) {
- syslog(LOG_ERR, "recvfrom: %m");
- exit(1);
- }
+ /* This means we don't want to wait() for children */
+ bsd_signal(SIGCHLD, SIG_IGN);
+
+ do {
+ fd_set readset;
+ struct timeval tv_timeout;
+
+
+ FD_ZERO(&readset);
+ FD_SET(fd, &readset);
+ tv_timeout.tv_sec = timeout;
+ tv_timeout.tv_usec = 0;
+
+ if ( select(fd+1, &readset, NULL, NULL, &tv_timeout) == 0 )
+ exit(0); /* Timeout, return to inetd */
+
+ fromlen = sizeof (from);
+ n = myrecvfrom(fd, buf, sizeof (buf), 0,
+ (struct sockaddr *)&from, &fromlen,
+ &myaddr);
+
+ if (n < 0) {
+ syslog(LOG_ERR, "recvfrom: %m");
+ exit(1);
+ }
#ifdef HAVE_TCPWRAPPERS
/* Verify if this was a legal request for us. This has to be
done before the chroot, while /etc is still accessible. */
- request_init(&wrap_request,
- RQ_DAEMON, __progname,
- RQ_FILE, fd,
- RQ_CLIENT_SIN, &from,
- RQ_SERVER_SIN, &myaddr,
- 0);
- sock_methods(&wrap_request);
- if ( hosts_access(&wrap_request) == 0 ) {
- if ( deny_severity != -1 )
- syslog(deny_severity, "connection refused from %s",
+ request_init(&wrap_request,
+ RQ_DAEMON, __progname,
+ RQ_FILE, fd,
+ RQ_CLIENT_SIN, &from,
+ RQ_SERVER_SIN, &myaddr,
+ 0);
+ sock_methods(&wrap_request);
+ if ( hosts_access(&wrap_request) == 0 ) {
+ if ( deny_severity != -1 )
+ syslog(deny_severity, "connection refused from %s",
+ inet_ntoa(from.sin_addr));
+ exit(1); /* Access denied */
+ } else if ( allow_severity != -1 ) {
+ syslog(allow_severity, "connect from %s",
inet_ntoa(from.sin_addr));
- exit(1); /* Access denied */
- } else if ( allow_severity != -1 ) {
- syslog(allow_severity, "connect from %s",
- inet_ntoa(from.sin_addr));
- }
+ }
#endif
- /* Drop privileges */
+ /*
+ * Now that we have read the message out of the UDP
+ * socket, we fork and go back to listening to the
+ * socket.
+ */
+ pid = fork();
+ if (pid < 0) {
+ syslog(LOG_ERR, "fork: %m");
+ exit(1); /* Return to inetd, just in case */
+ }
+ } while ( pid > 0 ); /* Parent process continues... */
+
+ /* Child process: handle the actual request here */
+
+ /* Chroot and drop privileges */
+
if (secure && chroot(".")) {
syslog(LOG_ERR, "chroot: %m");
exit(1);
@@ -303,55 +339,15 @@ main(int argc, char **argv)
exit(1);
}
- /*
- * Now that we have read the message out of the UDP
- * socket, we fork and exit. Thus, inetd will go back
- * to listening to the tftp port, and the next request
- * to come in will start up a new instance of tftpd.
- *
- * We do this so that inetd can run tftpd in "wait" mode.
- * The problem with tftpd running in "nowait" mode is that
- * inetd may get one or more successful "selects" on the
- * tftp port before we do our receive, so more than one
- * instance of tftpd may be started up. Worse, if tftpd
- * break before doing the above "recvfrom", inetd would
- * spawn endless instances, clogging the system.
- */
- for (i = 1; i < 20; i++) {
- pid = fork();
- if (pid < 0) {
- sleep(i);
- /*
- * flush out to most recently sent request.
- *
- * This may drop some request, but those
- * will be resent by the clients when
- * they timeout. The positive effect of
- * this flush is to (try to) prevent more
- * than one tftpd being started up to service
- * a single request from a single client.
- */
- j = sizeof from;
- i = myrecvfrom(fd, buf, sizeof (buf), 0,
- (struct sockaddr *)&from, &j, &myaddr);
- if (i > 0) {
- n = i;
- fromlen = j;
- }
- } else
- break;
- }
- if (pid < 0) {
- syslog(LOG_ERR, "fork: %m");
- exit(1);
- } else if (pid != 0)
- exit(0);
+ /* Close file descriptors we don't need */
from.sin_family = AF_INET;
alarm(0);
close(fd);
close(1);
+ /* Process the request... */
+
peer = socket(AF_INET, SOCK_DGRAM, 0);
if (peer < 0) {
syslog(LOG_ERR, "socket: %m");