blob: 435b21687ffa366a7bebf1bfc7905997be5b8f7d [file] [log] [blame]
Simon Kelleyd1ced3a2018-01-01 22:18:03 +00001/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
Simon Kelley832af0b2007-01-21 20:01:28 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley832af0b2007-01-21 20:01:28 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley832af0b2007-01-21 20:01:28 +000015*/
16
17#include "dnsmasq.h"
18
19#ifdef HAVE_TFTP
20
Simon Kelley8bc4cec2012-07-03 21:04:11 +010021static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
Simon Kelley832af0b2007-01-21 20:01:28 +000022static void free_transfer(struct tftp_transfer *transfer);
Rosen Penev50a28412017-06-27 22:27:02 +010023static ssize_t tftp_err(int err, char *packet, char *message, char *file);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000024static ssize_t tftp_err_oops(char *packet, char *file);
Simon Kelley832af0b2007-01-21 20:01:28 +000025static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
26static char *next(char **p, char *end);
Simon Kelley42fb8152012-04-20 17:15:01 +010027static void sanitise(char *buf);
Simon Kelley832af0b2007-01-21 20:01:28 +000028
29#define OP_RRQ 1
30#define OP_WRQ 2
31#define OP_DATA 3
32#define OP_ACK 4
33#define OP_ERR 5
34#define OP_OACK 6
35
36#define ERR_NOTDEF 0
37#define ERR_FNF 1
38#define ERR_PERM 2
39#define ERR_FULL 3
40#define ERR_ILL 4
41
Simon Kelley5aabfc72007-08-29 11:24:47 +010042void tftp_request(struct listener *listen, time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +000043{
44 ssize_t len;
45 char *packet = daemon->packet;
46 char *filename, *mode, *p, *end, *opt;
Simon Kelley28866e92011-02-14 20:19:14 +000047 union mysockaddr addr, peer;
Simon Kelley832af0b2007-01-21 20:01:28 +000048 struct msghdr msg;
Simon Kelley832af0b2007-01-21 20:01:28 +000049 struct iovec iov;
Simon Kelley1f15b812009-10-13 17:49:32 +010050 struct ifreq ifr;
Simon Kelley8bc4cec2012-07-03 21:04:11 +010051 int is_err = 1, if_index = 0, mtu = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +000052 struct iname *tmp;
Simon Kelley5aabfc72007-08-29 11:24:47 +010053 struct tftp_transfer *transfer;
Simon Kelley824af852008-02-12 20:43:05 +000054 int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
55#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelley1f15b812009-10-13 17:49:32 +010056 int mtuflag = IP_PMTUDISC_DONT;
Simon Kelley824af852008-02-12 20:43:05 +000057#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +010058 char namebuff[IF_NAMESIZE];
Simon Kelley52d4abf2012-03-21 21:39:48 +000059 char *name = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010060 char *prefix = daemon->tftp_prefix;
61 struct tftp_prefix *pref;
Simon Kelleycc921df2019-01-02 22:48:59 +000062 union all_addr addra;
Simon Kelley2329bef2013-12-03 13:41:16 +000063 /* Can always get recvd interface for IPv6 */
64 int check_dest = !option_bool(OPT_NOWILD) || listen->family == AF_INET6;
Simon Kelley832af0b2007-01-21 20:01:28 +000065 union {
66 struct cmsghdr align; /* this ensures alignment */
Simon Kelley28866e92011-02-14 20:19:14 +000067 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +000068#if defined(HAVE_LINUX_NETWORK)
Simon Kelley832af0b2007-01-21 20:01:28 +000069 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +000070#elif defined(HAVE_SOLARIS_NETWORK)
71 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +010072#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley832af0b2007-01-21 20:01:28 +000073 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
74#endif
75 } control_u;
76
77 msg.msg_controllen = sizeof(control_u);
78 msg.msg_control = control_u.control;
79 msg.msg_flags = 0;
80 msg.msg_name = &peer;
81 msg.msg_namelen = sizeof(peer);
82 msg.msg_iov = &iov;
83 msg.msg_iovlen = 1;
84
85 iov.iov_base = packet;
86 iov.iov_len = daemon->packet_buff_sz;
87
88 /* we overwrote the buffer... */
89 daemon->srv_save = NULL;
90
91 if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
92 return;
Simon Kelley2329bef2013-12-03 13:41:16 +000093
94 /* Can always get recvd interface for IPv6 */
95 if (!check_dest)
Simon Kelley1f15b812009-10-13 17:49:32 +010096 {
Simon Kelley52d4abf2012-03-21 21:39:48 +000097 if (listen->iface)
98 {
99 addr = listen->iface->addr;
Simon Kelley52d4abf2012-03-21 21:39:48 +0000100 name = listen->iface->name;
Simon Kelleybec366b2016-02-24 22:03:26 +0000101 mtu = listen->iface->mtu;
102 if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
103 mtu = daemon->tftp_mtu;
Simon Kelley52d4abf2012-03-21 21:39:48 +0000104 }
105 else
106 {
107 /* we're listening on an address that doesn't appear on an interface,
108 ask the kernel what the socket is bound to */
109 socklen_t tcp_len = sizeof(union mysockaddr);
110 if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1)
111 return;
112 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100113 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000114 else
115 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100116 struct cmsghdr *cmptr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100117
Simon Kelley28866e92011-02-14 20:19:14 +0000118 if (msg.msg_controllen < sizeof(struct cmsghdr))
119 return;
120
121 addr.sa.sa_family = listen->family;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000122
Simon Kelley832af0b2007-01-21 20:01:28 +0000123#if defined(HAVE_LINUX_NETWORK)
Simon Kelley28866e92011-02-14 20:19:14 +0000124 if (listen->family == AF_INET)
125 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000126 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley28866e92011-02-14 20:19:14 +0000127 {
128 union {
129 unsigned char *c;
130 struct in_pktinfo *p;
131 } p;
132 p.c = CMSG_DATA(cmptr);
133 addr.in.sin_addr = p.p->ipi_spec_dst;
134 if_index = p.p->ipi_ifindex;
135 }
136
137#elif defined(HAVE_SOLARIS_NETWORK)
138 if (listen->family == AF_INET)
139 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000140 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100141 union {
142 unsigned char *c;
Simon Kelley28866e92011-02-14 20:19:14 +0000143 struct in_addr *a;
144 unsigned int *i;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100145 } p;
146 p.c = CMSG_DATA(cmptr);
Simon Kelley28866e92011-02-14 20:19:14 +0000147 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
148 addr.in.sin_addr = *(p.a);
149 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
150 if_index = *(p.i);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000151 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000152
Simon Kelley832af0b2007-01-21 20:01:28 +0000153#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley28866e92011-02-14 20:19:14 +0000154 if (listen->family == AF_INET)
155 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
156 {
157 union {
158 unsigned char *c;
159 struct in_addr *a;
160 struct sockaddr_dl *s;
161 } p;
162 p.c = CMSG_DATA(cmptr);
163 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
164 addr.in.sin_addr = *(p.a);
165 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
166 if_index = p.s->sdl_index;
167 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100168
Simon Kelley824af852008-02-12 20:43:05 +0000169#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000170
Simon Kelley28866e92011-02-14 20:19:14 +0000171 if (listen->family == AF_INET6)
172 {
173 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000174 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
Simon Kelley28866e92011-02-14 20:19:14 +0000175 {
176 union {
177 unsigned char *c;
178 struct in6_pktinfo *p;
179 } p;
180 p.c = CMSG_DATA(cmptr);
181
182 addr.in6.sin6_addr = p.p->ipi6_addr;
183 if_index = p.p->ipi6_ifindex;
184 }
185 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000186
Simon Kelley28866e92011-02-14 20:19:14 +0000187 if (!indextoname(listen->tftpfd, if_index, namebuff))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000188 return;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100189
190 name = namebuff;
Simon Kelleye25db1f2013-01-29 22:10:26 +0000191
Simon Kelleycc921df2019-01-02 22:48:59 +0000192 addra.addr4 = addr.in.sin_addr;
Simon Kelley28866e92011-02-14 20:19:14 +0000193
Simon Kelley28866e92011-02-14 20:19:14 +0000194 if (listen->family == AF_INET6)
Simon Kelleycc921df2019-01-02 22:48:59 +0000195 addra.addr6 = addr.in6.sin6_addr;
Simon Kelleye25db1f2013-01-29 22:10:26 +0000196
Simon Kelley2937f8a2013-07-29 19:49:07 +0100197 if (daemon->tftp_interfaces)
Simon Kelley3169daa2012-08-13 17:39:57 +0100198 {
Simon Kelley2937f8a2013-07-29 19:49:07 +0100199 /* dedicated tftp interface list */
200 for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next)
201 if (tmp->name && wildcard_match(tmp->name, name))
202 break;
203
204 if (!tmp)
Simon Kelley3169daa2012-08-13 17:39:57 +0100205 return;
206 }
Simon Kelley2937f8a2013-07-29 19:49:07 +0100207 else
208 {
209 /* Do the same as DHCP */
210 if (!iface_check(listen->family, &addra, name, NULL))
211 {
212 if (!option_bool(OPT_CLEVERBIND))
213 enumerate_interfaces(0);
214 if (!loopback_exception(listen->tftpfd, listen->family, &addra, name) &&
Simon Kelleycc921df2019-01-02 22:48:59 +0000215 !label_exception(if_index, listen->family, &addra))
Simon Kelley2937f8a2013-07-29 19:49:07 +0100216 return;
217 }
218
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100219#ifdef HAVE_DHCP
Simon Kelley2937f8a2013-07-29 19:49:07 +0100220 /* allowed interfaces are the same as for DHCP */
221 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
222 if (tmp->name && wildcard_match(tmp->name, name))
223 return;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100224#endif
Simon Kelley2937f8a2013-07-29 19:49:07 +0100225 }
226
Petr Menšík47b45b22018-08-15 18:17:00 +0200227 safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE);
Simon Kelley1f15b812009-10-13 17:49:32 +0100228 if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
Simon Kelleybec366b2016-02-24 22:03:26 +0000229 {
230 mtu = ifr.ifr_mtu;
231 if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
232 mtu = daemon->tftp_mtu;
233 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000234 }
Stefan Tomanek7aa970e2015-04-01 17:55:07 +0100235
Simon Kelleybec366b2016-02-24 22:03:26 +0000236 /* Failed to get interface mtu - can use configured value. */
237 if (mtu == 0)
238 mtu = daemon->tftp_mtu;
239
Simon Kelley52d4abf2012-03-21 21:39:48 +0000240 if (name)
241 {
242 /* check for per-interface prefix */
243 for (pref = daemon->if_prefix; pref; pref = pref->next)
244 if (strcmp(pref->interface, name) == 0)
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100245 prefix = pref->prefix;
Simon Kelley52d4abf2012-03-21 21:39:48 +0000246 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100247
Simon Kelley28866e92011-02-14 20:19:14 +0000248 if (listen->family == AF_INET)
Simon Kelley7de060b2011-08-26 17:24:52 +0100249 {
250 addr.in.sin_port = htons(port);
251#ifdef HAVE_SOCKADDR_SA_LEN
252 addr.in.sin_len = sizeof(addr.in);
253#endif
254 }
Simon Kelley28866e92011-02-14 20:19:14 +0000255 else
256 {
257 addr.in6.sin6_port = htons(port);
258 addr.in6.sin6_flowinfo = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +0100259 addr.in6.sin6_scope_id = 0;
260#ifdef HAVE_SOCKADDR_SA_LEN
261 addr.in6.sin6_len = sizeof(addr.in6);
262#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000263 }
Simon Kelley28866e92011-02-14 20:19:14 +0000264
Simon Kelley5aabfc72007-08-29 11:24:47 +0100265 if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
Simon Kelley832af0b2007-01-21 20:01:28 +0000266 return;
267
Simon Kelley28866e92011-02-14 20:19:14 +0000268 if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000269 {
270 free(transfer);
271 return;
272 }
273
274 transfer->peer = peer;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100275 transfer->timeout = now + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +0000276 transfer->backoff = 1;
277 transfer->block = 1;
278 transfer->blocksize = 512;
Simon Kelley9e038942008-05-30 20:06:34 +0100279 transfer->offset = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000280 transfer->file = NULL;
281 transfer->opt_blocksize = transfer->opt_transize = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100282 transfer->netascii = transfer->carrylf = 0;
Simon Kelley28866e92011-02-14 20:19:14 +0000283
Simon Kelleyc72daea2012-01-05 21:33:27 +0000284 prettyprint_addr(&peer, daemon->addrbuff);
Simon Kelley28866e92011-02-14 20:19:14 +0000285
Simon Kelley824af852008-02-12 20:43:05 +0000286 /* if we have a nailed-down range, iterate until we find a free one. */
287 while (1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000288 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100289 if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000290#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000291 setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000292#endif
293 !fix_fd(transfer->sockfd))
294 {
295 if (errno == EADDRINUSE && daemon->start_tftp_port != 0)
296 {
297 if (++port <= daemon->end_tftp_port)
298 {
Simon Kelley28866e92011-02-14 20:19:14 +0000299 if (listen->family == AF_INET)
300 addr.in.sin_port = htons(port);
Simon Kelley28866e92011-02-14 20:19:14 +0000301 else
Simon Kelleyee875042018-10-23 22:10:17 +0100302 addr.in6.sin6_port = htons(port);
303
Simon Kelley824af852008-02-12 20:43:05 +0000304 continue;
305 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100306 my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
Simon Kelley824af852008-02-12 20:43:05 +0000307 }
308 free_transfer(transfer);
309 return;
310 }
311 break;
Simon Kelley832af0b2007-01-21 20:01:28 +0000312 }
Simon Kelley824af852008-02-12 20:43:05 +0000313
Simon Kelley832af0b2007-01-21 20:01:28 +0000314 p = packet + 2;
315 end = packet + len;
316
317 if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
318 !(filename = next(&p, end)) ||
319 !(mode = next(&p, end)) ||
Simon Kelley9e038942008-05-30 20:06:34 +0100320 (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
Simon Kelley6a69ab52012-04-24 14:42:26 +0100321 {
322 len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
323 is_err = 1;
324 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000325 else
326 {
Simon Kelley9e038942008-05-30 20:06:34 +0100327 if (strcasecmp(mode, "netascii") == 0)
328 transfer->netascii = 1;
329
Simon Kelley832af0b2007-01-21 20:01:28 +0000330 while ((opt = next(&p, end)))
331 {
Simon Kelley77e94da2009-08-31 17:32:17 +0100332 if (strcasecmp(opt, "blksize") == 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000333 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100334 if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
Simon Kelley77e94da2009-08-31 17:32:17 +0100335 {
Simon Kelleyd1377fa2016-03-04 21:32:21 +0000336 /* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */
337 int overhead = (listen->family == AF_INET) ? 32 : 52;
Simon Kelley77e94da2009-08-31 17:32:17 +0100338 transfer->blocksize = atoi(opt);
339 if (transfer->blocksize < 1)
340 transfer->blocksize = 1;
341 if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
342 transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
Simon Kelleyd1377fa2016-03-04 21:32:21 +0000343 if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead)
344 transfer->blocksize = (unsigned)mtu - overhead;
Simon Kelley77e94da2009-08-31 17:32:17 +0100345 transfer->opt_blocksize = 1;
346 transfer->block = 0;
347 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000348 }
Simon Kelley77e94da2009-08-31 17:32:17 +0100349 else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
Simon Kelley832af0b2007-01-21 20:01:28 +0000350 {
351 transfer->opt_transize = 1;
352 transfer->block = 0;
353 }
354 }
355
Simon Kelley1f15b812009-10-13 17:49:32 +0100356 /* cope with backslashes from windows boxen. */
Simon Kelley61ce6002012-04-20 21:28:49 +0100357 for (p = filename; *p; p++)
358 if (*p == '\\')
359 *p = '/';
360 else if (option_bool(OPT_TFTP_LC))
361 *p = tolower(*p);
362
Simon Kelleyf2621c72007-04-29 19:47:21 +0100363 strcpy(daemon->namebuff, "/");
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100364 if (prefix)
Simon Kelley832af0b2007-01-21 20:01:28 +0000365 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100366 if (prefix[0] == '/')
Simon Kelleyf2621c72007-04-29 19:47:21 +0100367 daemon->namebuff[0] = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100368 strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
369 if (prefix[strlen(prefix)-1] != '/')
Simon Kelley77e94da2009-08-31 17:32:17 +0100370 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100371
Floris Bos60704f52017-04-09 22:22:49 +0100372 if (option_bool(OPT_TFTP_APREF_IP))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100373 {
374 size_t oldlen = strlen(daemon->namebuff);
375 struct stat statbuf;
376
Simon Kelleyc72daea2012-01-05 21:33:27 +0000377 strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley77e94da2009-08-31 17:32:17 +0100378 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100379
380 /* remove unique-directory if it doesn't exist */
381 if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
382 daemon->namebuff[oldlen] = 0;
383 }
Floris Bos503c6092017-04-09 23:07:13 +0100384
Floris Bos60704f52017-04-09 22:22:49 +0100385 if (option_bool(OPT_TFTP_APREF_MAC))
386 {
387 unsigned char *macaddr = NULL;
388 unsigned char macbuf[DHCP_CHADDR_MAX];
Floris Bos503c6092017-04-09 23:07:13 +0100389
Floris Bos60704f52017-04-09 22:22:49 +0100390#ifdef HAVE_DHCP
391 if (daemon->dhcp && peer.sa.sa_family == AF_INET)
392 {
393 /* Check if the client IP is in our lease database */
394 struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr);
395 if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN)
396 macaddr = lease->hwaddr;
397 }
398#endif
Floris Bos503c6092017-04-09 23:07:13 +0100399
Floris Bos60704f52017-04-09 22:22:49 +0100400 /* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */
401 if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0)
Floris Bos503c6092017-04-09 23:07:13 +0100402 macaddr = macbuf;
403
Floris Bos60704f52017-04-09 22:22:49 +0100404 if (macaddr)
405 {
406 size_t oldlen = strlen(daemon->namebuff);
407 struct stat statbuf;
408
409 snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
410 macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
Floris Bos503c6092017-04-09 23:07:13 +0100411
Floris Bos60704f52017-04-09 22:22:49 +0100412 /* remove unique-directory if it doesn't exist */
413 if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
414 daemon->namebuff[oldlen] = 0;
415 }
416 }
Floris Bos503c6092017-04-09 23:07:13 +0100417
Simon Kelleyf2621c72007-04-29 19:47:21 +0100418 /* Absolute pathnames OK if they match prefix */
419 if (filename[0] == '/')
420 {
421 if (strstr(filename, daemon->namebuff) == filename)
422 daemon->namebuff[0] = 0;
423 else
424 filename++;
425 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000426 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100427 else if (filename[0] == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000428 daemon->namebuff[0] = 0;
Simon Kelley77e94da2009-08-31 17:32:17 +0100429 strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
Floris Bos503c6092017-04-09 23:07:13 +0100430
Simon Kelley5aabfc72007-08-29 11:24:47 +0100431 /* check permissions and open file */
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100432 if ((transfer->file = check_tftp_fileperm(&len, prefix)))
Simon Kelley832af0b2007-01-21 20:01:28 +0000433 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000434 if ((len = get_block(packet, transfer)) == -1)
435 len = tftp_err_oops(packet, daemon->namebuff);
436 else
437 is_err = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000438 }
439 }
440
441 while (sendto(transfer->sockfd, packet, len, 0,
Simon Kelley04363602012-04-27 10:11:51 +0100442 (struct sockaddr *)&peer, sa_len(&peer)) == -1 && errno == EINTR);
Simon Kelley832af0b2007-01-21 20:01:28 +0000443
444 if (is_err)
445 free_transfer(transfer);
446 else
447 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000448 transfer->next = daemon->tftp_trans;
449 daemon->tftp_trans = transfer;
450 }
451}
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000452
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100453static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000454{
455 char *packet = daemon->packet, *namebuff = daemon->namebuff;
456 struct tftp_file *file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100457 struct tftp_transfer *t;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000458 uid_t uid = geteuid();
459 struct stat statbuf;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100460 int fd = -1;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000461
462 /* trick to ban moving out of the subtree */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100463 if (prefix && strstr(namebuff, "/../"))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100464 goto perm;
465
466 if ((fd = open(namebuff, O_RDONLY)) == -1)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000467 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100468 if (errno == ENOENT)
469 {
470 *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
471 return NULL;
472 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000473 else if (errno == EACCES)
474 goto perm;
475 else
476 goto oops;
477 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100478
479 /* stat the file descriptor to avoid stat->open races */
480 if (fstat(fd, &statbuf) == -1)
481 goto oops;
482
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000483 /* running as root, must be world-readable */
484 if (uid == 0)
485 {
486 if (!(statbuf.st_mode & S_IROTH))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100487 goto perm;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000488 }
489 /* in secure mode, must be owned by user running dnsmasq */
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100490 else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100491 goto perm;
492
Josh Soref730c6742017-02-06 16:14:04 +0000493 /* If we're doing many transfers from the same file, only
Simon Kelley5aabfc72007-08-29 11:24:47 +0100494 open it once this saves lots of file descriptors
495 when mass-booting a big cluster, for instance.
496 Be conservative and only share when inode and name match
497 this keeps error messages sane. */
498 for (t = daemon->tftp_trans; t; t = t->next)
499 if (t->file->dev == statbuf.st_dev &&
500 t->file->inode == statbuf.st_ino &&
501 strcmp(t->file->filename, namebuff) == 0)
502 {
503 close(fd);
504 t->file->refcount++;
505 return t->file;
506 }
507
508 if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000509 {
510 errno = ENOMEM;
511 goto oops;
512 }
513
Simon Kelley5aabfc72007-08-29 11:24:47 +0100514 file->fd = fd;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000515 file->size = statbuf.st_size;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100516 file->dev = statbuf.st_dev;
517 file->inode = statbuf.st_ino;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000518 file->refcount = 1;
519 strcpy(file->filename, namebuff);
520 return file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100521
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000522 perm:
Simon Kelley5aabfc72007-08-29 11:24:47 +0100523 errno = EACCES;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000524 *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100525 if (fd != -1)
526 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000527 return NULL;
528
Simon Kelley5aabfc72007-08-29 11:24:47 +0100529 oops:
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000530 *len = tftp_err_oops(packet, namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100531 if (fd != -1)
532 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000533 return NULL;
534}
535
Simon Kelleyb842bc92015-07-12 21:09:11 +0100536void check_tftp_listeners(time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +0000537{
538 struct tftp_transfer *transfer, *tmp, **up;
539 ssize_t len;
540
541 struct ack {
542 unsigned short op, block;
543 } *mess = (struct ack *)daemon->packet;
544
545 /* Check for activity on any existing transfers */
546 for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
547 {
548 tmp = transfer->next;
549
Simon Kelley6a69ab52012-04-24 14:42:26 +0100550 prettyprint_addr(&transfer->peer, daemon->addrbuff);
551
Simon Kelleyb842bc92015-07-12 21:09:11 +0100552 if (poll_check(transfer->sockfd, POLLIN))
Simon Kelley832af0b2007-01-21 20:01:28 +0000553 {
554 /* we overwrote the buffer... */
555 daemon->srv_save = NULL;
556
557 if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
558 {
559 if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
560 {
561 /* Got ack, ensure we take the (re)transmit path */
562 transfer->timeout = now;
563 transfer->backoff = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100564 if (transfer->block++ != 0)
565 transfer->offset += transfer->blocksize - transfer->expansion;
Simon Kelley832af0b2007-01-21 20:01:28 +0000566 }
567 else if (ntohs(mess->op) == OP_ERR)
568 {
569 char *p = daemon->packet + sizeof(struct ack);
570 char *end = daemon->packet + len;
571 char *err = next(&p, end);
Simon Kelley28866e92011-02-14 20:19:14 +0000572
Simon Kelley832af0b2007-01-21 20:01:28 +0000573 /* Sanitise error message */
574 if (!err)
575 err = "";
576 else
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100577 sanitise(err);
Simon Kelley6a69ab52012-04-24 14:42:26 +0100578
Simon Kelley316e2732010-01-22 20:16:09 +0000579 my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100580 (int)ntohs(mess->block), err,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000581 daemon->addrbuff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000582
583 /* Got err, ensure we take abort */
584 transfer->timeout = now;
585 transfer->backoff = 100;
586 }
587 }
588 }
589
590 if (difftime(now, transfer->timeout) >= 0.0)
591 {
592 int endcon = 0;
593
594 /* timeout, retransmit */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100595 transfer->timeout += 1 + (1<<transfer->backoff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000596
597 /* we overwrote the buffer... */
598 daemon->srv_save = NULL;
599
600 if ((len = get_block(daemon->packet, transfer)) == -1)
601 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000602 len = tftp_err_oops(daemon->packet, transfer->file->filename);
Simon Kelley832af0b2007-01-21 20:01:28 +0000603 endcon = 1;
604 }
Simon Kelley42fb8152012-04-20 17:15:01 +0100605 /* don't complain about timeout when we're awaiting the last
606 ACK, some clients never send it */
Simon Kelleyb4b93082013-06-19 10:31:23 +0100607 else if (++transfer->backoff > 7 && len != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000608 {
Simon Kelley42fb8152012-04-20 17:15:01 +0100609 endcon = 1;
610 len = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000611 }
Simon Kelley42fb8152012-04-20 17:15:01 +0100612
Simon Kelley832af0b2007-01-21 20:01:28 +0000613 if (len != 0)
614 while(sendto(transfer->sockfd, daemon->packet, len, 0,
Simon Kelley04363602012-04-27 10:11:51 +0100615 (struct sockaddr *)&transfer->peer, sa_len(&transfer->peer)) == -1 && errno == EINTR);
Simon Kelley832af0b2007-01-21 20:01:28 +0000616
617 if (endcon || len == 0)
618 {
Simon Kelley42fb8152012-04-20 17:15:01 +0100619 strcpy(daemon->namebuff, transfer->file->filename);
620 sanitise(daemon->namebuff);
621 my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000622 /* unlink */
623 *up = tmp;
Simon Kelley231d0612012-04-27 13:50:45 +0100624 if (endcon)
625 free_transfer(transfer);
626 else
627 {
628 /* put on queue to be sent to script and deleted */
629 transfer->next = daemon->tftp_done_trans;
630 daemon->tftp_done_trans = transfer;
631 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000632 continue;
633 }
634 }
635
636 up = &transfer->next;
Simon Kelley28866e92011-02-14 20:19:14 +0000637 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000638}
639
640static void free_transfer(struct tftp_transfer *transfer)
641{
642 close(transfer->sockfd);
643 if (transfer->file && (--transfer->file->refcount) == 0)
644 {
645 close(transfer->file->fd);
646 free(transfer->file);
647 }
648 free(transfer);
649}
650
651static char *next(char **p, char *end)
652{
653 char *ret = *p;
654 size_t len;
655
656 if (*(end-1) != 0 ||
657 *p == end ||
658 (len = strlen(ret)) == 0)
659 return NULL;
660
661 *p += len + 1;
662 return ret;
663}
664
Simon Kelley42fb8152012-04-20 17:15:01 +0100665static void sanitise(char *buf)
666{
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100667 unsigned char *q, *r;
668 for (q = r = (unsigned char *)buf; *r; r++)
Simon Kelley11263a42012-04-27 14:00:55 +0100669 if (isprint((int)*r))
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100670 *(q++) = *r;
671 *q = 0;
Simon Kelley42fb8152012-04-20 17:15:01 +0100672
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100673}
Simon Kelley42fb8152012-04-20 17:15:01 +0100674
Simon Kelley294d36d2016-07-06 21:30:25 +0100675#define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */
Simon Kelley832af0b2007-01-21 20:01:28 +0000676static ssize_t tftp_err(int err, char *packet, char *message, char *file)
677{
678 struct errmess {
679 unsigned short op, err;
680 char message[];
681 } *mess = (struct errmess *)packet;
Simon Kelley294d36d2016-07-06 21:30:25 +0100682 ssize_t len, ret = 4;
Simon Kelley832af0b2007-01-21 20:01:28 +0000683 char *errstr = strerror(errno);
Simon Kelley42fb8152012-04-20 17:15:01 +0100684
Simon Kelleyfa785732016-07-22 20:56:01 +0100685 memset(packet, 0, daemon->packet_buff_sz);
Simon Kelley42fb8152012-04-20 17:15:01 +0100686 sanitise(file);
Simon Kelleyfa785732016-07-22 20:56:01 +0100687
Simon Kelley832af0b2007-01-21 20:01:28 +0000688 mess->op = htons(OP_ERR);
689 mess->err = htons(err);
Simon Kelley294d36d2016-07-06 21:30:25 +0100690 len = snprintf(mess->message, MAXMESSAGE, message, file, errstr);
691 ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */
692
Simon Kelley316e2732010-01-22 20:16:09 +0000693 my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
Simon Kelley832af0b2007-01-21 20:01:28 +0000694
695 return ret;
696}
697
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000698static ssize_t tftp_err_oops(char *packet, char *file)
699{
Simon Kelley42fb8152012-04-20 17:15:01 +0100700 /* May have >1 refs to file, so potentially mangle a copy of the name */
701 strcpy(daemon->namebuff, file);
702 return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000703}
704
Simon Kelley832af0b2007-01-21 20:01:28 +0000705/* return -1 for error, zero for done. */
706static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
707{
Simon Kelleyfa785732016-07-22 20:56:01 +0100708 memset(packet, 0, daemon->packet_buff_sz);
709
Simon Kelley832af0b2007-01-21 20:01:28 +0000710 if (transfer->block == 0)
711 {
712 /* send OACK */
713 char *p;
714 struct oackmess {
715 unsigned short op;
716 char data[];
717 } *mess = (struct oackmess *)packet;
718
719 p = mess->data;
720 mess->op = htons(OP_OACK);
721 if (transfer->opt_blocksize)
722 {
723 p += (sprintf(p, "blksize") + 1);
Rosen Penevcbd29e52017-06-27 22:29:51 +0100724 p += (sprintf(p, "%u", transfer->blocksize) + 1);
Simon Kelley832af0b2007-01-21 20:01:28 +0000725 }
726 if (transfer->opt_transize)
727 {
728 p += (sprintf(p,"tsize") + 1);
729 p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
730 }
731
732 return p - packet;
733 }
734 else
735 {
736 /* send data packet */
737 struct datamess {
738 unsigned short op, block;
739 unsigned char data[];
740 } *mess = (struct datamess *)packet;
741
Simon Kelley9e038942008-05-30 20:06:34 +0100742 size_t size = transfer->file->size - transfer->offset;
Simon Kelley832af0b2007-01-21 20:01:28 +0000743
Simon Kelley9e038942008-05-30 20:06:34 +0100744 if (transfer->offset > transfer->file->size)
Simon Kelley832af0b2007-01-21 20:01:28 +0000745 return 0; /* finished */
746
747 if (size > transfer->blocksize)
748 size = transfer->blocksize;
749
Simon Kelley832af0b2007-01-21 20:01:28 +0000750 mess->op = htons(OP_DATA);
751 mess->block = htons((unsigned short)(transfer->block));
752
Simon Kelley9e038942008-05-30 20:06:34 +0100753 if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
754 !read_write(transfer->file->fd, mess->data, size, 1))
Simon Kelley832af0b2007-01-21 20:01:28 +0000755 return -1;
Simon Kelley9e038942008-05-30 20:06:34 +0100756
757 transfer->expansion = 0;
758
759 /* Map '\n' to CR-LF in netascii mode */
760 if (transfer->netascii)
761 {
762 size_t i;
763 int newcarrylf;
764
765 for (i = 0, newcarrylf = 0; i < size; i++)
766 if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
767 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100768 transfer->expansion++;
769
770 if (size != transfer->blocksize)
Simon Kelley9e038942008-05-30 20:06:34 +0100771 size++; /* room in this block */
Simon Kelley7de060b2011-08-26 17:24:52 +0100772 else if (i == size - 1)
773 newcarrylf = 1; /* don't expand LF again if it moves to the next block */
774
Simon Kelley9e038942008-05-30 20:06:34 +0100775 /* make space and insert CR */
776 memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
777 mess->data[i] = '\r';
778
779 i++;
780 }
781 transfer->carrylf = newcarrylf;
782
783 }
784
785 return size + 4;
Simon Kelley832af0b2007-01-21 20:01:28 +0000786 }
787}
788
Simon Kelleya9530962012-03-20 22:07:35 +0000789
790int do_tftp_script_run(void)
791{
792 struct tftp_transfer *transfer;
793
794 if ((transfer = daemon->tftp_done_trans))
795 {
796 daemon->tftp_done_trans = transfer->next;
797#ifdef HAVE_SCRIPT
798 queue_tftp(transfer->file->size, transfer->file->filename, &transfer->peer);
799#endif
800 free_transfer(transfer);
801 return 1;
802 }
803
804 return 0;
805}
Simon Kelley832af0b2007-01-21 20:01:28 +0000806#endif