From 3a2e9669fe1f59a3c76b542cae73f9930d15e45f Mon Sep 17 00:00:00 2001 From: buhrow Date: Tue, 5 May 2015 05:50:31 +0000 Subject: [PATCH] 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 --- libexec/tftpd/tftpd.8 | 10 ++++++-- libexec/tftpd/tftpd.c | 60 +++++++++++++++++++++++++++++++------------ 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/libexec/tftpd/tftpd.8 b/libexec/tftpd/tftpd.8 index 62e919c532e6..6f609de261d6 100644 --- a/libexec/tftpd/tftpd.8 +++ b/libexec/tftpd/tftpd.8 @@ -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. diff --git a/libexec/tftpd/tftpd.c b/libexec/tftpd/tftpd.c index dda35a363d43..4998707aedc0 100644 --- a/libexec/tftpd/tftpd.c +++ b/libexec/tftpd/tftpd.c @@ -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"); }