blob: d611dea9df4dbeb46081cff9475b7ad83946e26a [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 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);
23static ssize_t tftp_err(int err, char *packet, char *mess, 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 Kelley8ef5ada2010-06-03 19:42:45 +010052#ifdef HAVE_DHCP
Simon Kelley832af0b2007-01-21 20:01:28 +000053 struct iname *tmp;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010054#endif
Simon Kelley5aabfc72007-08-29 11:24:47 +010055 struct tftp_transfer *transfer;
Simon Kelley824af852008-02-12 20:43:05 +000056 int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
57#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelley1f15b812009-10-13 17:49:32 +010058 int mtuflag = IP_PMTUDISC_DONT;
Simon Kelley824af852008-02-12 20:43:05 +000059#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +010060 char namebuff[IF_NAMESIZE];
Simon Kelley52d4abf2012-03-21 21:39:48 +000061 char *name = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010062 char *prefix = daemon->tftp_prefix;
63 struct tftp_prefix *pref;
Simon Kelleye25db1f2013-01-29 22:10:26 +000064 struct all_addr addra;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010065
Simon Kelley832af0b2007-01-21 20:01:28 +000066 union {
67 struct cmsghdr align; /* this ensures alignment */
Simon Kelley28866e92011-02-14 20:19:14 +000068#ifdef HAVE_IPV6
69 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
70#endif
Simon Kelley824af852008-02-12 20:43:05 +000071#if defined(HAVE_LINUX_NETWORK)
Simon Kelley832af0b2007-01-21 20:01:28 +000072 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +000073#elif defined(HAVE_SOLARIS_NETWORK)
74 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +010075#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelley832af0b2007-01-21 20:01:28 +000076 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
77#endif
78 } control_u;
79
80 msg.msg_controllen = sizeof(control_u);
81 msg.msg_control = control_u.control;
82 msg.msg_flags = 0;
83 msg.msg_name = &peer;
84 msg.msg_namelen = sizeof(peer);
85 msg.msg_iov = &iov;
86 msg.msg_iovlen = 1;
87
88 iov.iov_base = packet;
89 iov.iov_len = daemon->packet_buff_sz;
90
91 /* we overwrote the buffer... */
92 daemon->srv_save = NULL;
93
94 if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
95 return;
96
Simon Kelley28866e92011-02-14 20:19:14 +000097 if (option_bool(OPT_NOWILD))
Simon Kelley1f15b812009-10-13 17:49:32 +010098 {
Simon Kelley52d4abf2012-03-21 21:39:48 +000099 if (listen->iface)
100 {
101 addr = listen->iface->addr;
102 mtu = listen->iface->mtu;
103 name = listen->iface->name;
104 }
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
171#ifdef HAVE_IPV6
172 if (listen->family == AF_INET6)
173 {
174 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000175 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
Simon Kelley28866e92011-02-14 20:19:14 +0000176 {
177 union {
178 unsigned char *c;
179 struct in6_pktinfo *p;
180 } p;
181 p.c = CMSG_DATA(cmptr);
182
183 addr.in6.sin6_addr = p.p->ipi6_addr;
184 if_index = p.p->ipi6_ifindex;
185 }
186 }
187#endif
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000188
Simon Kelley28866e92011-02-14 20:19:14 +0000189 if (!indextoname(listen->tftpfd, if_index, namebuff))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000190 return;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100191
192 name = namebuff;
Simon Kelleye25db1f2013-01-29 22:10:26 +0000193
194 addra.addr.addr4 = addr.in.sin_addr;
Simon Kelley28866e92011-02-14 20:19:14 +0000195
196#ifdef HAVE_IPV6
197 if (listen->family == AF_INET6)
Simon Kelleye25db1f2013-01-29 22:10:26 +0000198 addra.addr.addr6 = addr.in6.sin6_addr;
199#endif
200
Simon Kelley2937f8a2013-07-29 19:49:07 +0100201 if (daemon->tftp_interfaces)
Simon Kelley3169daa2012-08-13 17:39:57 +0100202 {
Simon Kelley2937f8a2013-07-29 19:49:07 +0100203 /* dedicated tftp interface list */
204 for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next)
205 if (tmp->name && wildcard_match(tmp->name, name))
206 break;
207
208 if (!tmp)
Simon Kelley3169daa2012-08-13 17:39:57 +0100209 return;
210 }
Simon Kelley2937f8a2013-07-29 19:49:07 +0100211 else
212 {
213 /* Do the same as DHCP */
214 if (!iface_check(listen->family, &addra, name, NULL))
215 {
216 if (!option_bool(OPT_CLEVERBIND))
217 enumerate_interfaces(0);
218 if (!loopback_exception(listen->tftpfd, listen->family, &addra, name) &&
219 !label_exception(if_index, listen->family, &addra) )
220 return;
221 }
222
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100223#ifdef HAVE_DHCP
Simon Kelley2937f8a2013-07-29 19:49:07 +0100224 /* allowed interfaces are the same as for DHCP */
225 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
226 if (tmp->name && wildcard_match(tmp->name, name))
227 return;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100228#endif
Simon Kelley2937f8a2013-07-29 19:49:07 +0100229 }
230
Simon Kelley316e2732010-01-22 20:16:09 +0000231 strncpy(ifr.ifr_name, name, IF_NAMESIZE);
Simon Kelley1f15b812009-10-13 17:49:32 +0100232 if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
233 mtu = ifr.ifr_mtu;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000234 }
235
Simon Kelley52d4abf2012-03-21 21:39:48 +0000236 if (name)
237 {
238 /* check for per-interface prefix */
239 for (pref = daemon->if_prefix; pref; pref = pref->next)
240 if (strcmp(pref->interface, name) == 0)
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100241 prefix = pref->prefix;
Simon Kelley52d4abf2012-03-21 21:39:48 +0000242 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100243
Simon Kelley28866e92011-02-14 20:19:14 +0000244 if (listen->family == AF_INET)
Simon Kelley7de060b2011-08-26 17:24:52 +0100245 {
246 addr.in.sin_port = htons(port);
247#ifdef HAVE_SOCKADDR_SA_LEN
248 addr.in.sin_len = sizeof(addr.in);
249#endif
250 }
Simon Kelley28866e92011-02-14 20:19:14 +0000251#ifdef HAVE_IPV6
252 else
253 {
254 addr.in6.sin6_port = htons(port);
255 addr.in6.sin6_flowinfo = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +0100256 addr.in6.sin6_scope_id = 0;
257#ifdef HAVE_SOCKADDR_SA_LEN
258 addr.in6.sin6_len = sizeof(addr.in6);
259#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000260 }
261#endif
262
Simon Kelley5aabfc72007-08-29 11:24:47 +0100263 if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
Simon Kelley832af0b2007-01-21 20:01:28 +0000264 return;
265
Simon Kelley28866e92011-02-14 20:19:14 +0000266 if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000267 {
268 free(transfer);
269 return;
270 }
271
272 transfer->peer = peer;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100273 transfer->timeout = now + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +0000274 transfer->backoff = 1;
275 transfer->block = 1;
276 transfer->blocksize = 512;
Simon Kelley9e038942008-05-30 20:06:34 +0100277 transfer->offset = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000278 transfer->file = NULL;
279 transfer->opt_blocksize = transfer->opt_transize = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100280 transfer->netascii = transfer->carrylf = 0;
Simon Kelley28866e92011-02-14 20:19:14 +0000281
Simon Kelleyc72daea2012-01-05 21:33:27 +0000282 prettyprint_addr(&peer, daemon->addrbuff);
Simon Kelley28866e92011-02-14 20:19:14 +0000283
Simon Kelley824af852008-02-12 20:43:05 +0000284 /* if we have a nailed-down range, iterate until we find a free one. */
285 while (1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000286 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100287 if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000288#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000289 setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +0000290#endif
291 !fix_fd(transfer->sockfd))
292 {
293 if (errno == EADDRINUSE && daemon->start_tftp_port != 0)
294 {
295 if (++port <= daemon->end_tftp_port)
296 {
Simon Kelley28866e92011-02-14 20:19:14 +0000297 if (listen->family == AF_INET)
298 addr.in.sin_port = htons(port);
299#ifdef HAVE_IPV6
300 else
301 addr.in6.sin6_port = htons(port);
302#endif
Simon Kelley824af852008-02-12 20:43:05 +0000303 continue;
304 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100305 my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
Simon Kelley824af852008-02-12 20:43:05 +0000306 }
307 free_transfer(transfer);
308 return;
309 }
310 break;
Simon Kelley832af0b2007-01-21 20:01:28 +0000311 }
Simon Kelley824af852008-02-12 20:43:05 +0000312
Simon Kelley832af0b2007-01-21 20:01:28 +0000313 p = packet + 2;
314 end = packet + len;
315
316 if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
317 !(filename = next(&p, end)) ||
318 !(mode = next(&p, end)) ||
Simon Kelley9e038942008-05-30 20:06:34 +0100319 (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
Simon Kelley6a69ab52012-04-24 14:42:26 +0100320 {
321 len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
322 is_err = 1;
323 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000324 else
325 {
Simon Kelley9e038942008-05-30 20:06:34 +0100326 if (strcasecmp(mode, "netascii") == 0)
327 transfer->netascii = 1;
328
Simon Kelley832af0b2007-01-21 20:01:28 +0000329 while ((opt = next(&p, end)))
330 {
Simon Kelley77e94da2009-08-31 17:32:17 +0100331 if (strcasecmp(opt, "blksize") == 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000332 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100333 if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
Simon Kelley77e94da2009-08-31 17:32:17 +0100334 {
335 transfer->blocksize = atoi(opt);
336 if (transfer->blocksize < 1)
337 transfer->blocksize = 1;
338 if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
339 transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
Simon Kelley1f15b812009-10-13 17:49:32 +0100340 /* 32 bytes for IP, UDP and TFTP headers */
341 if (mtu != 0 && transfer->blocksize > (unsigned)mtu - 32)
342 transfer->blocksize = (unsigned)mtu - 32;
Simon Kelley77e94da2009-08-31 17:32:17 +0100343 transfer->opt_blocksize = 1;
344 transfer->block = 0;
345 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000346 }
Simon Kelley77e94da2009-08-31 17:32:17 +0100347 else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
Simon Kelley832af0b2007-01-21 20:01:28 +0000348 {
349 transfer->opt_transize = 1;
350 transfer->block = 0;
351 }
352 }
353
Simon Kelley1f15b812009-10-13 17:49:32 +0100354 /* cope with backslashes from windows boxen. */
Simon Kelley61ce6002012-04-20 21:28:49 +0100355 for (p = filename; *p; p++)
356 if (*p == '\\')
357 *p = '/';
358 else if (option_bool(OPT_TFTP_LC))
359 *p = tolower(*p);
360
Simon Kelleyf2621c72007-04-29 19:47:21 +0100361 strcpy(daemon->namebuff, "/");
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100362 if (prefix)
Simon Kelley832af0b2007-01-21 20:01:28 +0000363 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100364 if (prefix[0] == '/')
Simon Kelleyf2621c72007-04-29 19:47:21 +0100365 daemon->namebuff[0] = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100366 strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
367 if (prefix[strlen(prefix)-1] != '/')
Simon Kelley77e94da2009-08-31 17:32:17 +0100368 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100369
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100370 if (option_bool(OPT_TFTP_APREF))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100371 {
372 size_t oldlen = strlen(daemon->namebuff);
373 struct stat statbuf;
374
Simon Kelleyc72daea2012-01-05 21:33:27 +0000375 strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley77e94da2009-08-31 17:32:17 +0100376 strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100377
378 /* remove unique-directory if it doesn't exist */
379 if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
380 daemon->namebuff[oldlen] = 0;
381 }
382
Simon Kelleyf2621c72007-04-29 19:47:21 +0100383 /* Absolute pathnames OK if they match prefix */
384 if (filename[0] == '/')
385 {
386 if (strstr(filename, daemon->namebuff) == filename)
387 daemon->namebuff[0] = 0;
388 else
389 filename++;
390 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000391 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100392 else if (filename[0] == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000393 daemon->namebuff[0] = 0;
Simon Kelley77e94da2009-08-31 17:32:17 +0100394 strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
Simon Kelley832af0b2007-01-21 20:01:28 +0000395
Simon Kelley5aabfc72007-08-29 11:24:47 +0100396 /* check permissions and open file */
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100397 if ((transfer->file = check_tftp_fileperm(&len, prefix)))
Simon Kelley832af0b2007-01-21 20:01:28 +0000398 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000399 if ((len = get_block(packet, transfer)) == -1)
400 len = tftp_err_oops(packet, daemon->namebuff);
401 else
402 is_err = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000403 }
404 }
405
406 while (sendto(transfer->sockfd, packet, len, 0,
Simon Kelley04363602012-04-27 10:11:51 +0100407 (struct sockaddr *)&peer, sa_len(&peer)) == -1 && errno == EINTR);
Simon Kelley832af0b2007-01-21 20:01:28 +0000408
409 if (is_err)
410 free_transfer(transfer);
411 else
412 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000413 transfer->next = daemon->tftp_trans;
414 daemon->tftp_trans = transfer;
415 }
416}
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000417
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100418static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000419{
420 char *packet = daemon->packet, *namebuff = daemon->namebuff;
421 struct tftp_file *file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100422 struct tftp_transfer *t;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000423 uid_t uid = geteuid();
424 struct stat statbuf;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100425 int fd = -1;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000426
427 /* trick to ban moving out of the subtree */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100428 if (prefix && strstr(namebuff, "/../"))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100429 goto perm;
430
431 if ((fd = open(namebuff, O_RDONLY)) == -1)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000432 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100433 if (errno == ENOENT)
434 {
435 *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
436 return NULL;
437 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000438 else if (errno == EACCES)
439 goto perm;
440 else
441 goto oops;
442 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100443
444 /* stat the file descriptor to avoid stat->open races */
445 if (fstat(fd, &statbuf) == -1)
446 goto oops;
447
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000448 /* running as root, must be world-readable */
449 if (uid == 0)
450 {
451 if (!(statbuf.st_mode & S_IROTH))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100452 goto perm;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000453 }
454 /* in secure mode, must be owned by user running dnsmasq */
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100455 else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100456 goto perm;
457
458 /* If we're doing many tranfers from the same file, only
459 open it once this saves lots of file descriptors
460 when mass-booting a big cluster, for instance.
461 Be conservative and only share when inode and name match
462 this keeps error messages sane. */
463 for (t = daemon->tftp_trans; t; t = t->next)
464 if (t->file->dev == statbuf.st_dev &&
465 t->file->inode == statbuf.st_ino &&
466 strcmp(t->file->filename, namebuff) == 0)
467 {
468 close(fd);
469 t->file->refcount++;
470 return t->file;
471 }
472
473 if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000474 {
475 errno = ENOMEM;
476 goto oops;
477 }
478
Simon Kelley5aabfc72007-08-29 11:24:47 +0100479 file->fd = fd;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000480 file->size = statbuf.st_size;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100481 file->dev = statbuf.st_dev;
482 file->inode = statbuf.st_ino;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000483 file->refcount = 1;
484 strcpy(file->filename, namebuff);
485 return file;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100486
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000487 perm:
Simon Kelley5aabfc72007-08-29 11:24:47 +0100488 errno = EACCES;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000489 *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100490 if (fd != -1)
491 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000492 return NULL;
493
Simon Kelley5aabfc72007-08-29 11:24:47 +0100494 oops:
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000495 *len = tftp_err_oops(packet, namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100496 if (fd != -1)
497 close(fd);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000498 return NULL;
499}
500
Simon Kelley5aabfc72007-08-29 11:24:47 +0100501void check_tftp_listeners(fd_set *rset, time_t now)
Simon Kelley832af0b2007-01-21 20:01:28 +0000502{
503 struct tftp_transfer *transfer, *tmp, **up;
504 ssize_t len;
505
506 struct ack {
507 unsigned short op, block;
508 } *mess = (struct ack *)daemon->packet;
509
510 /* Check for activity on any existing transfers */
511 for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
512 {
513 tmp = transfer->next;
514
Simon Kelley6a69ab52012-04-24 14:42:26 +0100515 prettyprint_addr(&transfer->peer, daemon->addrbuff);
516
Simon Kelley832af0b2007-01-21 20:01:28 +0000517 if (FD_ISSET(transfer->sockfd, rset))
518 {
519 /* we overwrote the buffer... */
520 daemon->srv_save = NULL;
521
522 if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
523 {
524 if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
525 {
526 /* Got ack, ensure we take the (re)transmit path */
527 transfer->timeout = now;
528 transfer->backoff = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100529 if (transfer->block++ != 0)
530 transfer->offset += transfer->blocksize - transfer->expansion;
Simon Kelley832af0b2007-01-21 20:01:28 +0000531 }
532 else if (ntohs(mess->op) == OP_ERR)
533 {
534 char *p = daemon->packet + sizeof(struct ack);
535 char *end = daemon->packet + len;
536 char *err = next(&p, end);
Simon Kelley28866e92011-02-14 20:19:14 +0000537
Simon Kelley832af0b2007-01-21 20:01:28 +0000538 /* Sanitise error message */
539 if (!err)
540 err = "";
541 else
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100542 sanitise(err);
Simon Kelley6a69ab52012-04-24 14:42:26 +0100543
Simon Kelley316e2732010-01-22 20:16:09 +0000544 my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100545 (int)ntohs(mess->block), err,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000546 daemon->addrbuff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000547
548 /* Got err, ensure we take abort */
549 transfer->timeout = now;
550 transfer->backoff = 100;
551 }
552 }
553 }
554
555 if (difftime(now, transfer->timeout) >= 0.0)
556 {
557 int endcon = 0;
558
559 /* timeout, retransmit */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100560 transfer->timeout += 1 + (1<<transfer->backoff);
Simon Kelley832af0b2007-01-21 20:01:28 +0000561
562 /* we overwrote the buffer... */
563 daemon->srv_save = NULL;
564
565 if ((len = get_block(daemon->packet, transfer)) == -1)
566 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000567 len = tftp_err_oops(daemon->packet, transfer->file->filename);
Simon Kelley832af0b2007-01-21 20:01:28 +0000568 endcon = 1;
569 }
Simon Kelley42fb8152012-04-20 17:15:01 +0100570 /* don't complain about timeout when we're awaiting the last
571 ACK, some clients never send it */
Simon Kelleyb4b93082013-06-19 10:31:23 +0100572 else if (++transfer->backoff > 7 && len != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000573 {
Simon Kelley42fb8152012-04-20 17:15:01 +0100574 endcon = 1;
575 len = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000576 }
Simon Kelley42fb8152012-04-20 17:15:01 +0100577
Simon Kelley832af0b2007-01-21 20:01:28 +0000578 if (len != 0)
579 while(sendto(transfer->sockfd, daemon->packet, len, 0,
Simon Kelley04363602012-04-27 10:11:51 +0100580 (struct sockaddr *)&transfer->peer, sa_len(&transfer->peer)) == -1 && errno == EINTR);
Simon Kelley832af0b2007-01-21 20:01:28 +0000581
582 if (endcon || len == 0)
583 {
Simon Kelley42fb8152012-04-20 17:15:01 +0100584 strcpy(daemon->namebuff, transfer->file->filename);
585 sanitise(daemon->namebuff);
586 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 +0000587 /* unlink */
588 *up = tmp;
Simon Kelley231d0612012-04-27 13:50:45 +0100589 if (endcon)
590 free_transfer(transfer);
591 else
592 {
593 /* put on queue to be sent to script and deleted */
594 transfer->next = daemon->tftp_done_trans;
595 daemon->tftp_done_trans = transfer;
596 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000597 continue;
598 }
599 }
600
601 up = &transfer->next;
Simon Kelley28866e92011-02-14 20:19:14 +0000602 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000603}
604
605static void free_transfer(struct tftp_transfer *transfer)
606{
607 close(transfer->sockfd);
608 if (transfer->file && (--transfer->file->refcount) == 0)
609 {
610 close(transfer->file->fd);
611 free(transfer->file);
612 }
613 free(transfer);
614}
615
616static char *next(char **p, char *end)
617{
618 char *ret = *p;
619 size_t len;
620
621 if (*(end-1) != 0 ||
622 *p == end ||
623 (len = strlen(ret)) == 0)
624 return NULL;
625
626 *p += len + 1;
627 return ret;
628}
629
Simon Kelley42fb8152012-04-20 17:15:01 +0100630static void sanitise(char *buf)
631{
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100632 unsigned char *q, *r;
633 for (q = r = (unsigned char *)buf; *r; r++)
Simon Kelley11263a42012-04-27 14:00:55 +0100634 if (isprint((int)*r))
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100635 *(q++) = *r;
636 *q = 0;
Simon Kelley42fb8152012-04-20 17:15:01 +0100637
Simon Kelley7a14dfe2012-04-20 20:50:42 +0100638}
Simon Kelley42fb8152012-04-20 17:15:01 +0100639
Simon Kelley832af0b2007-01-21 20:01:28 +0000640static ssize_t tftp_err(int err, char *packet, char *message, char *file)
641{
642 struct errmess {
643 unsigned short op, err;
644 char message[];
645 } *mess = (struct errmess *)packet;
646 ssize_t ret = 4;
647 char *errstr = strerror(errno);
Simon Kelley42fb8152012-04-20 17:15:01 +0100648
649 sanitise(file);
650
Simon Kelley832af0b2007-01-21 20:01:28 +0000651 mess->op = htons(OP_ERR);
652 mess->err = htons(err);
653 ret += (snprintf(mess->message, 500, message, file, errstr) + 1);
Simon Kelley316e2732010-01-22 20:16:09 +0000654 my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
Simon Kelley832af0b2007-01-21 20:01:28 +0000655
656 return ret;
657}
658
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000659static ssize_t tftp_err_oops(char *packet, char *file)
660{
Simon Kelley42fb8152012-04-20 17:15:01 +0100661 /* May have >1 refs to file, so potentially mangle a copy of the name */
662 strcpy(daemon->namebuff, file);
663 return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000664}
665
Simon Kelley832af0b2007-01-21 20:01:28 +0000666/* return -1 for error, zero for done. */
667static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
668{
669 if (transfer->block == 0)
670 {
671 /* send OACK */
672 char *p;
673 struct oackmess {
674 unsigned short op;
675 char data[];
676 } *mess = (struct oackmess *)packet;
677
678 p = mess->data;
679 mess->op = htons(OP_OACK);
680 if (transfer->opt_blocksize)
681 {
682 p += (sprintf(p, "blksize") + 1);
683 p += (sprintf(p, "%d", transfer->blocksize) + 1);
684 }
685 if (transfer->opt_transize)
686 {
687 p += (sprintf(p,"tsize") + 1);
688 p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
689 }
690
691 return p - packet;
692 }
693 else
694 {
695 /* send data packet */
696 struct datamess {
697 unsigned short op, block;
698 unsigned char data[];
699 } *mess = (struct datamess *)packet;
700
Simon Kelley9e038942008-05-30 20:06:34 +0100701 size_t size = transfer->file->size - transfer->offset;
Simon Kelley832af0b2007-01-21 20:01:28 +0000702
Simon Kelley9e038942008-05-30 20:06:34 +0100703 if (transfer->offset > transfer->file->size)
Simon Kelley832af0b2007-01-21 20:01:28 +0000704 return 0; /* finished */
705
706 if (size > transfer->blocksize)
707 size = transfer->blocksize;
708
Simon Kelley832af0b2007-01-21 20:01:28 +0000709 mess->op = htons(OP_DATA);
710 mess->block = htons((unsigned short)(transfer->block));
711
Simon Kelley9e038942008-05-30 20:06:34 +0100712 if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
713 !read_write(transfer->file->fd, mess->data, size, 1))
Simon Kelley832af0b2007-01-21 20:01:28 +0000714 return -1;
Simon Kelley9e038942008-05-30 20:06:34 +0100715
716 transfer->expansion = 0;
717
718 /* Map '\n' to CR-LF in netascii mode */
719 if (transfer->netascii)
720 {
721 size_t i;
722 int newcarrylf;
723
724 for (i = 0, newcarrylf = 0; i < size; i++)
725 if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
726 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100727 transfer->expansion++;
728
729 if (size != transfer->blocksize)
Simon Kelley9e038942008-05-30 20:06:34 +0100730 size++; /* room in this block */
Simon Kelley7de060b2011-08-26 17:24:52 +0100731 else if (i == size - 1)
732 newcarrylf = 1; /* don't expand LF again if it moves to the next block */
733
Simon Kelley9e038942008-05-30 20:06:34 +0100734 /* make space and insert CR */
735 memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
736 mess->data[i] = '\r';
737
738 i++;
739 }
740 transfer->carrylf = newcarrylf;
741
742 }
743
744 return size + 4;
Simon Kelley832af0b2007-01-21 20:01:28 +0000745 }
746}
747
Simon Kelleya9530962012-03-20 22:07:35 +0000748
749int do_tftp_script_run(void)
750{
751 struct tftp_transfer *transfer;
752
753 if ((transfer = daemon->tftp_done_trans))
754 {
755 daemon->tftp_done_trans = transfer->next;
756#ifdef HAVE_SCRIPT
757 queue_tftp(transfer->file->size, transfer->file->filename, &transfer->peer);
758#endif
759 free_transfer(transfer);
760 return 1;
761 }
762
763 return 0;
764}
Simon Kelley832af0b2007-01-21 20:01:28 +0000765#endif