Add a -b flag so that clients that return their acknowledgements to the

broadcast address can inter-operate with the tftpd server.
Discussed in bin/49868
This commit is contained in:
buhrow 2015-05-05 05:50:31 +00:00
parent 58ba35d88f
commit 3a2e9669fe
2 changed files with 52 additions and 18 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: tftpd.8,v 1.28 2010/04/29 21:34:04 wiz Exp $
.\" $NetBSD: tftpd.8,v 1.29 2015/05/05 05:50:31 buhrow Exp $
.\"
.\" Copyright (c) 1983, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@ -39,7 +39,7 @@
Internet Trivial File Transfer Protocol server
.Sh SYNOPSIS
.Nm
.Op Fl cdln
.Op Fl bcdln
.Op Fl g Ar group
.Op Fl p Ar pathsep
.Op Fl s Ar directory
@ -95,6 +95,12 @@ relative filename requests.
.Pp
The options are:
.Bl -tag -width "XsXdirectoryX"
.It Fl b
Allow clients which return acknowledgements to the broadcast address to
communicate with the tftp server.
Some tftp clients, notably ones resident in the ROMs of older Cisco
equipment, return their acknowledgements to the broadcast address rather
than the server's unicast address.
.It Fl c
Allow unrestricted creation of new files.
Without this flag, only existing publicly writable files can be overwritten.

View File

@ -1,4 +1,4 @@
/* $NetBSD: tftpd.c,v 1.43 2013/10/04 07:51:48 jnemeth Exp $ */
/* $NetBSD: tftpd.c,v 1.44 2015/05/05 05:50:31 buhrow Exp $ */
/*
* Copyright (c) 1983, 1993
@ -36,7 +36,7 @@ __COPYRIGHT("@(#) Copyright (c) 1983, 1993\
#if 0
static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93";
#else
__RCSID("$NetBSD: tftpd.c,v 1.43 2013/10/04 07:51:48 jnemeth Exp $");
__RCSID("$NetBSD: tftpd.c,v 1.44 2015/05/05 05:50:31 buhrow Exp $");
#endif
#endif /* not lint */
@ -110,6 +110,7 @@ static int secure;
static char pathsep = '\0';
static char *securedir;
static int unrestricted_writes; /* uploaded files don't have to exist */
static int broadcast_client = 0; /* Some clients ack to the broadcast address */
struct formats;
@ -142,7 +143,7 @@ usage(void)
{
syslog(LOG_ERR,
"Usage: %s [-cdln] [-g group] [-p pathsep] [-s directory] [-u user] [directory ...]",
"Usage: %s [-bcdln] [-g group] [-p pathsep] [-s directory] [-u user] [directory ...]",
getprogname());
exit(1);
}
@ -172,8 +173,30 @@ main(int argc, char *argv[])
curuid = getuid();
curgid = getgid();
while ((ch = getopt(argc, argv, "cdg:lnp:s:u:")) != -1)
while ((ch = getopt(argc, argv, "bcdg:lnp:s:u:")) != -1)
switch (ch) {
case 'b':
/*
* Some clients, notably older Cisco boot loaders,
* send their acknowledgements to the broadcast address
* rather than the unicast address of the server.
* Allow those clients to inter-operate with us.
* It's worth noting that this interaction doesn't cause the
* server to change where it sends the responses, meaning
* servers that have this flag enabled are no more
* susceptible to magnifcation DOS attacks than those
* servers that don't use this flag. This flag merely
* permits the reception of acknowledgement traffic to the
* broadcast address/specific port number that's being used for
* this session as well as the unicast address/specific port
* number for this session. For example, if the session is
* expecting acks on 192.168.1.40:50201, then this flag
* would also allow acks to be returned to
* 192.168.1.255:50201, assuming that 192.168.1.255 is the
* broadcast address for the subnet containing 192.168.1.40.
*/
broadcast_client = 1;
break;
case 'c':
unrestricted_writes = 1;
break;
@ -393,14 +416,17 @@ main(int argc, char *argv[])
syslog(LOG_ERR, "socket: %m");
exit(1);
}
if (broadcast_client) {
soopt = 1;
if (setsockopt(peer, SOL_SOCKET, SO_BROADCAST, (void *) &soopt, sizeof(soopt)) < 0) {
syslog(LOG_ERR, "set SO_BROADCAST: %m");
exit(1);
}
}
if (bind(peer, (struct sockaddr *)&me, me.ss_len) < 0) {
syslog(LOG_ERR, "bind: %m");
exit(1);
}
if (connect(peer, (struct sockaddr *)&from, from.ss_len) < 0) {
syslog(LOG_ERR, "connect: %m");
exit(1);
}
soopt = 65536; /* larger than we'll ever need */
if (setsockopt(peer, SOL_SOCKET, SO_SNDBUF, (void *) &soopt, sizeof(soopt)) < 0) {
syslog(LOG_ERR, "set SNDBUF: %m");
@ -955,7 +981,7 @@ sendfile(struct formats *pf, volatile int etftp, int acklength)
send_data:
if (!etftp && debug)
syslog(LOG_DEBUG, "Send DATA %u", block);
if ((n = send(peer, dp, size + 4, 0)) != size + 4) {
if ((n = sendto(peer, dp, size + 4, 0, (struct sockaddr *)&from, fromlen)) != size + 4) {
syslog(LOG_ERR, "tftpd: write: %m");
goto abort;
}
@ -963,7 +989,8 @@ send_data:
read_ahead(file, tftp_blksize, pf->f_convert);
for ( ; ; ) {
alarm(rexmtval); /* read the ack */
n = recv(peer, ackbuf, tftp_blksize, 0);
n = recvfrom(peer, ackbuf, tftp_blksize, 0,(struct sockaddr
*)&from, &fromlen );
alarm(0);
if (n < 0) {
syslog(LOG_ERR, "tftpd: read: %m");
@ -1049,14 +1076,15 @@ recvfile(struct formats *pf, volatile int etftp, volatile int acklength)
(void) setjmp(timeoutbuf);
send_ack:
ap = (struct tftphdr *) (etftp ? oackbuf : ackbuf);
if (send(peer, ap, acklength, 0) != acklength) {
if (sendto(peer, ap, acklength, 0, (struct sockaddr *)&from, fromlen) != acklength) {
syslog(LOG_ERR, "tftpd: write: %m");
goto abort;
}
write_behind(file, pf->f_convert);
for ( ; ; ) {
alarm(rexmtval);
n = recv(peer, dp, tftp_blksize + 4, 0);
n = recvfrom(peer, dp, tftp_blksize + 4, 0, (struct sockaddr
*)&from, &fromlen);
alarm(0);
if (n < 0) { /* really? */
syslog(LOG_ERR, "tftpd: read: %m");
@ -1108,16 +1136,16 @@ done:
ap->th_block = htons((u_short)(block));
if (debug)
syslog(LOG_DEBUG, "Send final ACK %u", block);
(void) send(peer, ackbuf, 4, 0);
(void) sendto(peer, ackbuf, 4, 0, (struct sockaddr *)&from, fromlen);
signal(SIGALRM, justquit); /* just quit on timeout */
alarm(rexmtval);
n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
n = recvfrom(peer, buf, sizeof (buf), 0, (struct sockaddr *)&from, &fromlen); /* normally times out and quits */
alarm(0);
if (n >= 4 && /* if read some data */
dp->th_opcode == DATA && /* and got a data block */
block == dp->th_block) { /* then my last ack was lost */
(void) send(peer, ackbuf, 4, 0); /* resend final ack */
(void) sendto(peer, ackbuf, 4, 0, (struct sockaddr *)&from, fromlen); /* resend final ack */
}
abort:
return;
@ -1185,7 +1213,7 @@ nak(int error)
syslog(LOG_DEBUG, "Send NACK %s", tp->th_msg);
length = strlen(tp->th_msg);
msglen = &tp->th_msg[length + 1] - buf;
if (send(peer, buf, msglen, 0) != (ssize_t)msglen)
if (sendto(peer, buf, msglen, 0, (struct sockaddr *)&from, fromlen) != (ssize_t)msglen)
syslog(LOG_ERR, "nak: %m");
}